237 lines
5.7 KiB
C
237 lines
5.7 KiB
C
|
#include "FreeRTOS.h"
|
||
|
#include "chip.h"
|
||
|
#include "os_adapt.h"
|
||
|
|
||
|
#include "hx170dec.h"
|
||
|
#include "vdec.h"
|
||
|
|
||
|
#define REGS_VDEC_BASE 0x71200000
|
||
|
|
||
|
#define VDEC_MAX_CORES 1 /* number of cores of the hardware IP */
|
||
|
#define VDEC_NUM_REGS_DEC 60 /* number of registers of the Decoder part */
|
||
|
#define VDEC_NUM_REGS_PP 41 /* number of registers of the Post Processor part */
|
||
|
#define VDEC_DEC_FIRST_REG 0 /* first register (0-based) index */
|
||
|
#define VDEC_DEC_LAST_REG 59 /* last register (0-based) index */
|
||
|
#define VDEC_PP_FIRST_REG 60
|
||
|
#define VDEC_PP_LAST_REG 100
|
||
|
|
||
|
struct vdec_device {
|
||
|
unsigned int mmio_base;
|
||
|
struct device *dev;
|
||
|
int irq;
|
||
|
int num_cores;
|
||
|
unsigned long iobaseaddr;
|
||
|
unsigned long iosize;
|
||
|
QueueHandle_t dec_irq_done;
|
||
|
SemaphoreHandle_t dec_sem;
|
||
|
u32 regs[VDEC_NUM_REGS_DEC + VDEC_NUM_REGS_PP];
|
||
|
};
|
||
|
static struct vdec_device *vdec6731_global;
|
||
|
|
||
|
static inline void vdec_writel(const struct vdec_device *p, unsigned offset, u32 val)
|
||
|
{
|
||
|
writel(val, p->mmio_base + offset);
|
||
|
}
|
||
|
|
||
|
static inline u32 vdec_readl(const struct vdec_device *p, unsigned offset)
|
||
|
{
|
||
|
return readl(p->mmio_base + offset);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Write a range of registers. First register is assumed to be
|
||
|
* "Interrupt Register" and will be written last.
|
||
|
*/
|
||
|
static int vdec_regs_write(struct vdec_device *p, int begin, int end,
|
||
|
const struct core_desc *core)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
memcpy(&p->regs[begin], core->regs, (end - begin + 1) * 4);
|
||
|
|
||
|
for (i = end; i >= begin; i--)
|
||
|
vdec_writel(p, 4 * i, p->regs[i]);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read a range of registers [begin..end]
|
||
|
*/
|
||
|
static int vdec_regs_read(struct vdec_device *p, int begin, int end,
|
||
|
const struct core_desc *core)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = end; i >= begin; i--)
|
||
|
p->regs[i] = vdec_readl(p, 4 * i);
|
||
|
|
||
|
memcpy(core->regs, &p->regs[begin], (end - begin + 1) * 4);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
long vdec_ioctl(unsigned int cmd, void *arg)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
void *argp = arg;
|
||
|
struct vdec_device *p = vdec6731_global;
|
||
|
struct core_desc core;
|
||
|
u32 reg;
|
||
|
|
||
|
switch (cmd) {
|
||
|
case HX170DEC_IOX_ASIC_ID:
|
||
|
reg = vdec_readl(p, VDEC_IDR);
|
||
|
memcpy(argp, ®, sizeof(u32));
|
||
|
break;
|
||
|
|
||
|
case HX170DEC_IOC_MC_OFFSETS:
|
||
|
case HX170DEC_IOCGHWOFFSET:
|
||
|
memcpy(argp, &p->iobaseaddr, sizeof(p->iobaseaddr));
|
||
|
break;
|
||
|
case HX170DEC_IOCGHWIOSIZE: /* in bytes */
|
||
|
memcpy(argp, &p->iosize, sizeof(p->iosize));
|
||
|
break;
|
||
|
case HX170DEC_IOC_MC_CORES:
|
||
|
memcpy(argp, &p->num_cores, sizeof(p->num_cores));
|
||
|
break;
|
||
|
|
||
|
case HX170DEC_IOCS_DEC_PUSH_REG:
|
||
|
memcpy(&core, (void *)arg, sizeof(struct core_desc));
|
||
|
/* Skip VDEC_IDR (ID Register, ro) */
|
||
|
core.regs++; // core.size -= 4;
|
||
|
ret = vdec_regs_write(p, VDEC_DEC_FIRST_REG + 1, VDEC_DEC_LAST_REG, &core);
|
||
|
break;
|
||
|
case HX170DEC_IOCS_PP_PUSH_REG:
|
||
|
break;
|
||
|
|
||
|
case HX170DEC_IOCS_DEC_PULL_REG:
|
||
|
memcpy(&core, (void *)arg, sizeof(struct core_desc));
|
||
|
ret = vdec_regs_read(p, VDEC_DEC_FIRST_REG, VDEC_DEC_LAST_REG, &core);
|
||
|
break;
|
||
|
|
||
|
case HX170DEC_IOCS_PP_PULL_REG:
|
||
|
break;
|
||
|
|
||
|
case HX170DEC_IOCX_DEC_WAIT:
|
||
|
memcpy(&core, (void *)arg, sizeof(struct core_desc));
|
||
|
if (xQueueReceive(p->dec_irq_done, NULL, pdMS_TO_TICKS(1000)) != pdTRUE) {
|
||
|
dev_err(p->dev, "wait_event_interruptible dec error %d\n", ret);
|
||
|
ret = -ETIMEDOUT;
|
||
|
} else {
|
||
|
/* Update dec registers */
|
||
|
ret = vdec_regs_read(p, VDEC_DEC_FIRST_REG, VDEC_DEC_LAST_REG, &core);
|
||
|
}
|
||
|
xQueueReset(p->dec_irq_done);
|
||
|
break;
|
||
|
|
||
|
case HX170DEC_IOCX_PP_WAIT:
|
||
|
break;
|
||
|
|
||
|
case HX170DEC_IOCH_DEC_RESERVE:
|
||
|
if (xSemaphoreTake(p->dec_sem, pdMS_TO_TICKS(100)) == pdTRUE) {
|
||
|
ret = 0; /* core id */
|
||
|
dev_dbg(p->dev, "down dec_sem (core id %d)\n", ret);
|
||
|
} else {
|
||
|
dev_err(p->dev, "down_interruptible dec error\n");
|
||
|
ret = -ETIMEDOUT;
|
||
|
}
|
||
|
break;
|
||
|
case HX170DEC_IOCT_DEC_RELEASE:
|
||
|
dev_dbg(p->dev, "up dec_sem\n");
|
||
|
xSemaphoreGive(p->dec_sem);
|
||
|
break;
|
||
|
|
||
|
case HX170DEC_IOCQ_PP_RESERVE:
|
||
|
|
||
|
break;
|
||
|
case HX170DEC_IOCT_PP_RELEASE:
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
dev_warn(p->dev, "unknown ioctl %x\n", cmd);
|
||
|
ret = -EINVAL;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Platform driver related
|
||
|
*/
|
||
|
|
||
|
/* Should we use spin_lock_irqsave here? */
|
||
|
static void vdec_isr(void *dev_id)
|
||
|
{
|
||
|
struct vdec_device *p = dev_id;
|
||
|
u32 irq_status_dec;
|
||
|
int handled = 0;
|
||
|
|
||
|
/* interrupt status register read */
|
||
|
irq_status_dec = vdec_readl(p, VDEC_DIR);
|
||
|
//printf("irq_status=0x%x.\n", irq_status_dec);
|
||
|
if (irq_status_dec & VDEC_DIR_ISET) {
|
||
|
/* Clear IRQ */
|
||
|
vdec_writel(p, VDEC_DIR, irq_status_dec & ~VDEC_DIR_ISET);
|
||
|
|
||
|
xQueueSendFromISR(p->dec_irq_done, NULL, 0);
|
||
|
handled++;
|
||
|
}
|
||
|
|
||
|
if (handled == 0) {
|
||
|
dev_warn(p->dev, "Spurious IRQ (DIR=%08x)\n", irq_status_dec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
static void vdec_iram_mode_select(void)
|
||
|
{
|
||
|
//0 MFC ram use as iram; 1 MFCram use MFC REF buffer
|
||
|
u32 val = readl(REGS_SYSCTL_BASE + 0x78);
|
||
|
val |= (1<<0);
|
||
|
writel(val, REGS_SYSCTL_BASE + 0x78);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int vdec_init(void)
|
||
|
{
|
||
|
struct vdec_device *p;
|
||
|
int hwid;
|
||
|
|
||
|
/* Allocate private data */
|
||
|
p = pvPortMalloc(sizeof(struct vdec_device));
|
||
|
if (!p) {
|
||
|
dev_dbg(&pdev->dev, "out of memory\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
memset(p, 0, sizeof(struct vdec_device));
|
||
|
|
||
|
p->mmio_base = REGS_VDEC_BASE;
|
||
|
p->irq = JPG_IRQn;
|
||
|
p->num_cores = VDEC_MAX_CORES;
|
||
|
p->iosize = 0x200;
|
||
|
p->iobaseaddr = REGS_VDEC_BASE;
|
||
|
vdec6731_global = p;
|
||
|
|
||
|
request_irq(p->irq, 0, vdec_isr, p);
|
||
|
|
||
|
p->dec_irq_done = xQueueCreate(1, 0);
|
||
|
p->dec_sem = xSemaphoreCreateCounting(VDEC_MAX_CORES, 1);
|
||
|
|
||
|
dev_info(&pdev->dev, "VDEC controller at 0x%u, irq = %d\n",
|
||
|
p->mmio_base, p->irq);
|
||
|
|
||
|
/* Reset Asic (just in case..) */
|
||
|
vdec_writel(p, VDEC_DIR, VDEC_DIR_ID | VDEC_DIR_ABORT);
|
||
|
vdec_writel(p, VDEC_PPIR, VDEC_PPIR_ID);
|
||
|
|
||
|
hwid = vdec_readl(p, VDEC_IDR);
|
||
|
|
||
|
printf("Product ID: %#x (revision %d.%d.%d)\n", \
|
||
|
(hwid & VDEC_IDR_PROD_ID) >> 16,
|
||
|
(hwid & VDEC_IDR_MAJOR_VER) >> 12,
|
||
|
(hwid & VDEC_IDR_MINOR_VER) >> 4,
|
||
|
(hwid & VDEC_IDR_BUILD_VER));
|
||
|
|
||
|
return 0;
|
||
|
}
|