*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/clk.h>
+#include <linux/compat.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
+#include <linux/reset.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/wakelock.h>
#include <linux/of_irq.h>
#include <linux/rockchip/cpu.h>
#include <linux/rockchip/cru.h>
+#include <linux/rockchip/pmu.h>
#ifdef CONFIG_MFD_SYSCON
#include <linux/regmap.h>
#endif
#include "vcodec_service.h"
+/*
+ * debug flag usage:
+ * +------+-------------------+
+ * | 8bit | 24bit |
+ * +------+-------------------+
+ * 0~23 bit is for different information type
+ * 24~31 bit is for information print format
+ */
+
+#define DEBUG_POWER 0x00000001
+#define DEBUG_CLOCK 0x00000002
+#define DEBUG_IRQ_STATUS 0x00000004
+#define DEBUG_IOMMU 0x00000008
+#define DEBUG_IOCTL 0x00000010
+#define DEBUG_FUNCTION 0x00000020
+#define DEBUG_REGISTER 0x00000040
+#define DEBUG_EXTRA_INFO 0x00000080
+#define DEBUG_TIMING 0x00000100
+
+#define PRINT_FUNCTION 0x80000000
+#define PRINT_LINE 0x40000000
+
static int debug;
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug,
struct extra_info_elem elem[20];
};
-#define VPU_SERVICE_SHOW_TIME 0
-
-#if VPU_SERVICE_SHOW_TIME
-static struct timeval enc_start, enc_end;
-static struct timeval dec_start, dec_end;
-static struct timeval pp_start, pp_end;
-#endif
-
#define MHZ (1000*1000)
#define REG_NUM_9190_DEC (60)
},
};
+#ifndef BIT
+#define BIT(x) (1<<(x))
+#endif
+// interrupt and error status register
#define DEC_INTERRUPT_REGISTER 1
-#define PP_INTERRUPT_REGISTER 60
-#define ENC_INTERRUPT_REGISTER 1
-
-#define DEC_INTERRUPT_BIT 0x100
-#define DEC_BUFFER_EMPTY_BIT 0x4000
-#define PP_INTERRUPT_BIT 0x100
-#define ENC_INTERRUPT_BIT 0x1
+#define DEC_INTERRUPT_BIT BIT(8)
+#define DEC_READY_BIT BIT(12)
+#define DEC_BUS_ERROR_BIT BIT(13)
+#define DEC_BUFFER_EMPTY_BIT BIT(14)
+#define DEC_ASO_ERROR_BIT BIT(15)
+#define DEC_STREAM_ERROR_BIT BIT(16)
+#define DEC_SLICE_DONE_BIT BIT(17)
+#define DEC_TIMEOUT_BIT BIT(18)
+#define DEC_ERR_MASK DEC_BUS_ERROR_BIT \
+ |DEC_BUFFER_EMPTY_BIT \
+ |DEC_STREAM_ERROR_BIT \
+ |DEC_TIMEOUT_BIT
-#define HEVC_DEC_INT_RAW_BIT 0x200
-#define HEVC_DEC_STR_ERROR_BIT 0x4000
-#define HEVC_DEC_BUS_ERROR_BIT 0x2000
-#define HEVC_DEC_BUFFER_EMPTY_BIT 0x10000
+#define PP_INTERRUPT_REGISTER 60
+#define PP_INTERRUPT_BIT BIT(8)
+#define PP_READY_BIT BIT(12)
+#define PP_BUS_ERROR_BIT BIT(13)
+#define PP_ERR_MASK PP_BUS_ERROR_BIT
+#define ENC_INTERRUPT_REGISTER 1
+#define ENC_INTERRUPT_BIT BIT(0)
+#define ENC_READY_BIT BIT(2)
+#define ENC_BUS_ERROR_BIT BIT(3)
+#define ENC_BUFFER_FULL_BIT BIT(5)
+#define ENC_TIMEOUT_BIT BIT(6)
+#define ENC_ERR_MASK ENC_BUS_ERROR_BIT \
+ |ENC_BUFFER_FULL_BIT \
+ |ENC_TIMEOUT_BIT
+
+#define HEVC_INTERRUPT_REGISTER 1
+#define HEVC_DEC_INT_RAW_BIT BIT(9)
+#define HEVC_DEC_BUS_ERROR_BIT BIT(13)
+#define HEVC_DEC_STR_ERROR_BIT BIT(14)
+#define HEVC_DEC_TIMEOUT_BIT BIT(15)
+#define HEVC_DEC_BUFFER_EMPTY_BIT BIT(16)
+#define HEVC_DEC_COLMV_ERROR_BIT BIT(17)
+#define HEVC_DEC_ERR_MASK HEVC_DEC_BUS_ERROR_BIT \
+ |HEVC_DEC_STR_ERROR_BIT \
+ |HEVC_DEC_TIMEOUT_BIT \
+ |HEVC_DEC_BUFFER_EMPTY_BIT \
+ |HEVC_DEC_COLMV_ERROR_BIT
+
+
+// gating configuration set
#define VPU_REG_EN_ENC 14
#define VPU_REG_ENC_GATE 2
#define VPU_REG_ENC_GATE_BIT (1<<4)
#define DEBUG
#ifdef DEBUG
-#define vpu_debug(level, fmt, args...) \
+#define vpu_debug_func(type, fmt, args...) \
do { \
- if (debug >= level) \
+ if (unlikely(debug & type)) { \
pr_info("%s:%d: " fmt, \
__func__, __LINE__, ##args); \
+ } \
+ } while (0)
+#define vpu_debug(type, fmt, args...) \
+ do { \
+ if (unlikely(debug & type)) { \
+ pr_info(fmt, ##args); \
+ } \
} while (0)
#else
+#define vpu_debug_func(level, fmt, args...)
#define vpu_debug(level, fmt, args...)
#endif
-#define vpu_debug_enter() vpu_debug(4, "enter\n")
-#define vpu_debug_leave() vpu_debug(4, "leave\n")
+#define vpu_debug_enter() vpu_debug_func(DEBUG_FUNCTION, "enter\n")
+#define vpu_debug_leave() vpu_debug_func(DEBUG_FUNCTION, "leave\n")
#define vpu_err(fmt, args...) \
pr_err("%s:%d: " fmt, __func__, __LINE__, ##args)
enum VPU_CLIENT_TYPE type;
VPU_FREQ freq;
vpu_session *session;
+ struct vpu_subdev_data *data;
struct list_head session_link; /* link to vpu service session */
struct list_head status_link; /* link to register set list */
unsigned long size;
#if defined(CONFIG_VCODEC_MMU)
struct list_head mem_region_list;
+ u32 dec_base;
#endif
- unsigned long *reg;
+ u32 *reg;
} vpu_reg;
typedef struct vpu_device {
struct list_head done; /* link to link_reg in struct vpu_reg */
struct list_head session; /* link to list_session in struct vpu_session */
atomic_t total_running;
- bool enabled;
+ atomic_t enabled;
+ atomic_t power_on_cnt;
+ atomic_t power_off_cnt;
vpu_reg *reg_codec;
vpu_reg *reg_pproc;
vpu_reg *reg_resev;
struct clk *clk_cabac;
struct clk *pd_video;
+#ifdef CONFIG_RESET_CONTROLLER
+ struct reset_control *rst_a;
+ struct reset_control *rst_h;
+ struct reset_control *rst_v;
+#endif
struct device *dev;
u32 irq_status;
+ atomic_t reset_request;
#if defined(CONFIG_VCODEC_MMU)
struct ion_client *ion_client;
struct list_head mem_region_list;
enum vcodec_device_id current_hw_mode;
};
-typedef struct vpu_request {
- unsigned long *req;
+struct vpu_request {
+ u32 *req;
u32 size;
-} vpu_request;
+};
+
+#ifdef CONFIG_COMPAT
+struct compat_vpu_request {
+ compat_uptr_t req;
+ u32 size;
+};
+#endif
/* debugfs root directory for all device (vpu, hevc).*/
static struct dentry *parent;
#define VPU_POWER_OFF_DELAY 4*HZ /* 4s */
#define VPU_TIMEOUT_DELAY 2*HZ /* 2s */
+typedef struct {
+ char *name;
+ struct timeval start;
+ struct timeval end;
+ u32 error_mask;
+} task_info;
+
+typedef enum {
+ TASK_VPU_ENC,
+ TASK_VPU_DEC,
+ TASK_VPU_PP,
+ TASK_RKDEC_HEVC,
+ TASK_TYPE_BUTT,
+} TASK_TYPE;
+
+task_info tasks[TASK_TYPE_BUTT] = {
+ {
+ .name = "enc",
+ .error_mask = ENC_ERR_MASK
+ },
+ {
+ .name = "dec",
+ .error_mask = DEC_ERR_MASK
+ },
+ {
+ .name = "pp",
+ .error_mask = PP_ERR_MASK
+ },
+ {
+ .name = "hevc",
+ .error_mask = HEVC_DEC_ERR_MASK
+ },
+};
+
+static void time_record(task_info *task, int is_end)
+{
+ if (unlikely(debug & DEBUG_TIMING)) {
+ do_gettimeofday((is_end)?(&task->end):(&task->start));
+ }
+}
+
+static void time_diff(task_info *task)
+{
+ vpu_debug(DEBUG_TIMING, "%s task: %ld ms\n", task->name,
+ (task->end.tv_sec - task->start.tv_sec) * 1000 +
+ (task->end.tv_usec - task->start.tv_usec) / 1000);
+}
+
static void vcodec_enter_mode(struct vpu_subdev_data *data)
{
int bits;
u32 raw = 0;
struct vpu_service_info *pservice = data->pservice;
struct vpu_subdev_data *subdata, *n;
- if (pservice->subcnt < 2 || pservice->curr_mode == data->mode) {
- pservice->prev_mode = pservice->curr_mode;
+ if (pservice->subcnt < 2) {
+#if defined(CONFIG_VCODEC_MMU)
+ if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) {
+ set_bit(MMU_ACTIVATED, &data->state);
+ if (atomic_read(&pservice->enabled))
+ rockchip_iovmm_activate(data->dev);
+ else
+ BUG_ON(!atomic_read(&pservice->enabled));
+ }
+#endif
return;
}
- vpu_debug(3, "vcodec enter mode %d\n", data->mode);
+
+ if (pservice->curr_mode == data->mode)
+ return;
+
+ vpu_debug(DEBUG_IOMMU, "vcodec enter mode %d\n", data->mode);
#if defined(CONFIG_VCODEC_MMU)
list_for_each_entry_safe(subdata, n, &pservice->subdev_list, lnk_service) {
if (data != subdata && subdata->mmu_dev &&
#if defined(CONFIG_VCODEC_MMU)
if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) {
set_bit(MMU_ACTIVATED, &data->state);
- BUG_ON(!pservice->enabled);
- if (pservice->enabled)
+ if (atomic_read(&pservice->enabled))
rockchip_iovmm_activate(data->dev);
+ else
+ BUG_ON(!atomic_read(&pservice->enabled));
}
#endif
pservice->prev_mode = pservice->curr_mode;
pservice->curr_mode = data->mode;
}
-static void vcodec_exit_mode(struct vpu_service_info *pservice)
+static void vcodec_exit_mode(struct vpu_subdev_data *data)
{
-
+ if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) {
+ clear_bit(MMU_ACTIVATED, &data->state);
+ rockchip_iovmm_deactivate(data->dev);
+ data->pservice->curr_mode = VCODEC_RUNNING_MODE_NONE;
+ }
}
static int vpu_get_clk(struct vpu_service_info *pservice)
#if VCODEC_CLOCK_ENABLE
switch (pservice->dev_id) {
case VCODEC_DEVICE_ID_HEVC:
- pservice->clk_cabac = devm_clk_get(pservice->dev, "clk_cabac");
- if (IS_ERR(pservice->clk_cabac)) {
- dev_err(pservice->dev, "failed on clk_get clk_cabac\n");
- return -1;
- }
-
pservice->pd_video = devm_clk_get(pservice->dev, "pd_hevc");
if (IS_ERR(pservice->pd_video)) {
dev_err(pservice->dev, "failed on clk_get pd_hevc\n");
return -1;
}
case VCODEC_DEVICE_ID_COMBO:
+ pservice->clk_cabac = devm_clk_get(pservice->dev, "clk_cabac");
+ if (IS_ERR(pservice->clk_cabac)) {
+ dev_err(pservice->dev, "failed on clk_get clk_cabac\n");
+ pservice->clk_cabac = NULL;
+ }
pservice->clk_core = devm_clk_get(pservice->dev, "clk_core");
if (IS_ERR(pservice->clk_core)) {
dev_err(pservice->dev, "failed on clk_get clk_core\n");
}
if (pservice->pd_video == NULL) {
pservice->pd_video = devm_clk_get(pservice->dev, "pd_video");
- if (IS_ERR(pservice->pd_video))
+ if (IS_ERR(pservice->pd_video)) {
pservice->pd_video = NULL;
+ dev_info(pservice->dev, "do not have pd_video\n");
+ }
}
break;
default:
static void vpu_reset(struct vpu_subdev_data *data)
{
struct vpu_service_info *pservice = data->pservice;
+ enum pmu_idle_req type = IDLE_REQ_VIDEO;
+
+ if (pservice->dev_id == VCODEC_DEVICE_ID_HEVC)
+ type = IDLE_REQ_HEVC;
+
+ pr_info("%s: resetting...", dev_name(pservice->dev));
+
#if defined(CONFIG_ARCH_RK29)
clk_disable(aclk_ddr_vepu);
cru_set_soft_reset(SOFT_RST_CPU_VODEC_A2A_AHB, true);
cru_set_soft_reset(SOFT_RST_VCODEC_NIU_AXI, false);
cru_set_soft_reset(SOFT_RST_CPU_VCODEC, false);
pmu_set_idle_request(IDLE_REQ_VIDEO, false);
+#else
#endif
+ WARN_ON(pservice->reg_codec != NULL);
+ WARN_ON(pservice->reg_pproc != NULL);
+ WARN_ON(pservice->reg_resev != NULL);
pservice->reg_codec = NULL;
pservice->reg_pproc = NULL;
pservice->reg_resev = NULL;
+ pr_info("for 3288/3368...");
+#ifdef CONFIG_RESET_CONTROLLER
+ if (pservice->rst_a && pservice->rst_h) {
+ if (rockchip_pmu_ops.set_idle_request)
+ rockchip_pmu_ops.set_idle_request(type, true);
+ pr_info("reset in\n");
+ if (pservice->rst_v)
+ reset_control_assert(pservice->rst_v);
+ reset_control_assert(pservice->rst_a);
+ reset_control_assert(pservice->rst_h);
+ usleep_range(10, 20);
+ reset_control_deassert(pservice->rst_h);
+ reset_control_deassert(pservice->rst_a);
+ if (pservice->rst_v)
+ reset_control_deassert(pservice->rst_v);
+ if (rockchip_pmu_ops.set_idle_request)
+ rockchip_pmu_ops.set_idle_request(type, false);
+ }
+#endif
+
#if defined(CONFIG_VCODEC_MMU)
- if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) {
- set_bit(MMU_ACTIVATED, &data->state);
- BUG_ON(!pservice->enabled);
- if (pservice->enabled)
- rockchip_iovmm_activate(data->dev);
+ if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) {
+ clear_bit(MMU_ACTIVATED, &data->state);
+ if (atomic_read(&pservice->enabled))
+ rockchip_iovmm_deactivate(data->dev);
+ else
+ BUG_ON(!atomic_read(&pservice->enabled));
}
#endif
+ atomic_set(&pservice->reset_request, 0);
+ pr_info("done\n");
}
static void reg_deinit(struct vpu_subdev_data *data, vpu_reg *reg);
{
int total_running;
struct vpu_subdev_data *data = NULL, *n;
- if (!pservice->enabled)
+ int ret = atomic_add_unless(&pservice->enabled, -1, 0);
+ if (!ret)
return;
- pservice->enabled = false;
total_running = atomic_read(&pservice->total_running);
if (total_running) {
pr_alert("alert: power off when %d task running!!\n", total_running);
clk_disable_unprepare(pservice->clk_cabac);
#endif
+ atomic_add(1, &pservice->power_off_cnt);
wake_unlock(&pservice->wake_lock);
pr_info("done\n");
}
static void vpu_service_power_on(struct vpu_service_info *pservice)
{
+ int ret;
static ktime_t last;
ktime_t now = ktime_get();
if (ktime_to_ns(ktime_sub(now, last)) > NSEC_PER_SEC) {
vpu_queue_power_off_work(pservice);
last = now;
}
- if (pservice->enabled)
+ ret = atomic_add_unless(&pservice->enabled, 1, 1);
+ if (!ret)
return ;
- pservice->enabled = true;
pr_info("%s: power on\n", dev_name(pservice->dev));
#define BIT_VCODEC_CLK_SEL (1<<10)
#endif
udelay(10);
+ atomic_add(1, &pservice->power_on_cnt);
wake_lock(&pservice->wake_lock);
}
static inline bool reg_check_rmvb_wmv(vpu_reg *reg)
{
- unsigned long type = (reg->reg[3] & 0xF0000000) >> 28;
+ u32 type = (reg->reg[3] & 0xF0000000) >> 28;
return ((type == 8) || (type == 4));
}
static inline bool reg_check_interlace(vpu_reg *reg)
{
- unsigned long type = (reg->reg[3] & (1 << 23));
+ u32 type = (reg->reg[3] & (1 << 23));
return (type > 0);
}
}
mem_region->hdl = hdl;
- vcodec_enter_mode(data);
ret = ion_map_iommu(data->dev, pservice->ion_client,
mem_region->hdl, &mem_region->iova, &mem_region->len);
- vcodec_exit_mode(pservice);
if (ret < 0) {
vpu_err("ion map iommu failed\n");
return -1;
}
- vpu_service_power_on(pservice);
-
for (i = 0; i < size; i++) {
usr_fd = reg->reg[tbl[i]] & 0x3FF;
mem_region->hdl = hdl;
mem_region->reg_idx = tbl[i];
- vcodec_enter_mode(data);
ret = ion_map_iommu(data->dev,
pservice->ion_client,
mem_region->hdl,
&mem_region->iova,
&mem_region->len);
- vcodec_exit_mode(pservice);
if (ret < 0) {
dev_err(pservice->dev, "ion map iommu failed\n");
ion_free(pservice->ion_client, hdl);
return ret;
}
+
+ /* special for vpu dec num 12: record decoded length
+ hacking for decoded length
+ NOTE: not a perfect fix, the fd is not recorded */
+ if (tbl[i] == 12 && data->hw_info->hw_id != HEVC_ID &&
+ (reg->type == VPU_DEC || reg->type == VPU_DEC_PP)) {
+ reg->dec_base = mem_region->iova + offset;
+ vpu_debug(DEBUG_REGISTER, "dec_set %08x\n", reg->dec_base);
+ }
+
reg->reg[tbl[i]] = mem_region->iova + offset;
INIT_LIST_HEAD(&mem_region->reg_lnk);
list_add_tail(&mem_region->reg_lnk, ®->mem_region_list);
if (ext_inf != NULL && ext_inf->magic == EXTRA_INFO_MAGIC) {
for (i=0; i<ext_inf->cnt; i++) {
- vpu_debug(3, "reg[%d] + offset %d\n",
+ vpu_debug(DEBUG_IOMMU, "reg[%d] + offset %d\n",
ext_inf->elem[i].index,
ext_inf->elem[i].offset);
reg->reg[ext_inf->elem[i].index] +=
}
if (size > data->reg_size) {
- /*printk("warning: vpu reg size %lu is larger than hw reg size %lu\n",
- size, pservice->reg_size);
- size = pservice->reg_size;*/
+ /*printk("warning: vpu reg size %u is larger than hw reg size %u\n",
+ size, data->reg_size);*/
extra_size = size - data->reg_size;
size = data->reg_size;
}
reg->session = session;
+ reg->data = data;
reg->type = session->type;
reg->size = size;
reg->freq = VPU_FREQ_DEFAULT;
- reg->reg = (unsigned long *)®[1];
+ reg->reg = (u32 *)®[1];
INIT_LIST_HEAD(®->session_link);
INIT_LIST_HEAD(®->status_link);
reg->freq = VPU_FREQ_200M;
} else if (reg_check_fmt(reg) == VPU_DEC_FMT_H264) {
if (reg_probe_width(reg) > 3200) {
- // raise frequency for 4k avc.
- reg->freq = VPU_FREQ_500M;
+ /*raise frequency for 4k avc.*/
+ reg->freq = VPU_FREQ_600M;
}
} else {
if (reg_check_interlace(reg)) {
list_del_init(®->session_link);
list_add_tail(®->session_link, ®->session->done);
- vcodec_enter_mode(data);
+ /*vcodec_enter_mode(data);*/
switch (reg->type) {
case VPU_ENC : {
pservice->reg_codec = NULL;
int reg_len = REG_NUM_9190_DEC;
pservice->reg_codec = NULL;
reg_copy_from_hw(reg, data->dec_dev.hwregs, reg_len);
+#if defined(CONFIG_VCODEC_MMU)
+ /* revert hack for decoded length */
+ if (data->hw_info->hw_id != HEVC_ID) {
+ u32 dec_get = reg->reg[12];
+ s32 dec_length = dec_get - reg->dec_base;
+ vpu_debug(DEBUG_REGISTER, "dec_get %08x dec_length %d\n", dec_get, dec_length);
+ reg->reg[12] = dec_length << 10;
+ }
+#endif
irq_reg = DEC_INTERRUPT_REGISTER;
break;
}
pservice->reg_pproc = NULL;
reg_copy_from_hw(reg, data->dec_dev.hwregs, REG_NUM_9190_DEC_PP);
data->dec_dev.hwregs[PP_INTERRUPT_REGISTER] = 0;
+#if defined(CONFIG_VCODEC_MMU)
+ /* revert hack for decoded length */
+ if (data->hw_info->hw_id != HEVC_ID) {
+ u32 dec_get = reg->reg[12];
+ s32 dec_length = dec_get - reg->dec_base;
+ vpu_debug(DEBUG_REGISTER, "dec_get %08x dec_length %d\n", dec_get, dec_length);
+ reg->reg[12] = dec_length << 10;
+ }
+#endif
break;
}
default : {
break;
}
}
- vcodec_exit_mode(pservice);
+ vcodec_exit_mode(data);
if (irq_reg != -1)
reg->reg[irq_reg] = pservice->irq_status;
VEPU_CLEAN_CACHE(dst);
- dsb();
+ dsb(sy);
dst[VPU_REG_ENC_GATE] = src[VPU_REG_ENC_GATE] | VPU_REG_ENC_GATE_BIT;
dst[VPU_REG_EN_ENC] = src[VPU_REG_EN_ENC];
-#if VPU_SERVICE_SHOW_TIME
- do_gettimeofday(&enc_start);
-#endif
-
+ time_record(&tasks[TASK_VPU_ENC], 0);
} break;
case VPU_DEC : {
u32 *dst = (u32 *)data->dec_dev.hwregs;
HEVC_CLEAN_CACHE(dst);
}
- dsb();
+ dsb(sy);
if (data->hw_info->hw_id != HEVC_ID) {
dst[VPU_REG_DEC_GATE] = src[VPU_REG_DEC_GATE] | VPU_REG_DEC_GATE_BIT;
} else {
dst[VPU_REG_EN_DEC] = src[VPU_REG_EN_DEC];
}
- dsb();
- dmb();
-#if VPU_SERVICE_SHOW_TIME
- do_gettimeofday(&dec_start);
-#endif
+ dsb(sy);
+ dmb(sy);
+
+ time_record(&tasks[TASK_VPU_DEC], 0);
} break;
case VPU_PP : {
u32 *dst = (u32 *)data->dec_dev.hwregs + PP_INTERRUPT_REGISTER;
for (i = VPU_REG_PP_GATE + 1; i < REG_NUM_9190_PP; i++)
dst[i] = src[i];
- dsb();
+ dsb(sy);
dst[VPU_REG_EN_PP] = src[VPU_REG_EN_PP];
-#if VPU_SERVICE_SHOW_TIME
- do_gettimeofday(&pp_start);
-#endif
+ time_record(&tasks[TASK_VPU_PP], 0);
} break;
case VPU_DEC_PP : {
u32 *dst = (u32 *)data->dec_dev.hwregs;
dst[i] = src[i];
dst[VPU_REG_EN_DEC_PP] = src[VPU_REG_EN_DEC_PP] | 0x2;
- dsb();
+ dsb(sy);
dst[VPU_REG_DEC_PP_GATE] = src[VPU_REG_DEC_PP_GATE] | VPU_REG_PP_GATE_BIT;
dst[VPU_REG_DEC_GATE] = src[VPU_REG_DEC_GATE] | VPU_REG_DEC_GATE_BIT;
dst[VPU_REG_EN_DEC] = src[VPU_REG_EN_DEC];
-#if VPU_SERVICE_SHOW_TIME
- do_gettimeofday(&dec_start);
-#endif
+
+ time_record(&tasks[TASK_VPU_DEC], 0);
} break;
default : {
vpu_err("error: unsupport session type %d", reg->type);
atomic_sub(1, &pservice->total_running);
atomic_sub(1, ®->session->task_running);
- break;
- }
+ } break;
}
- vcodec_exit_mode(pservice);
+ /*vcodec_exit_mode(data);*/
vpu_debug_leave();
}
vpu_debug_enter();
if (!list_empty(&pservice->waiting)) {
int can_set = 0;
+ bool change_able = (NULL == pservice->reg_codec) && (NULL == pservice->reg_pproc);
+ int reset_request = atomic_read(&pservice->reset_request);
vpu_reg *reg = list_entry(pservice->waiting.next, vpu_reg, status_link);
vpu_service_power_on(pservice);
- switch (reg->type) {
- case VPU_ENC : {
- if ((NULL == pservice->reg_codec) && (NULL == pservice->reg_pproc))
- can_set = 1;
- } break;
- case VPU_DEC : {
- if (NULL == pservice->reg_codec)
- can_set = 1;
- if (pservice->auto_freq && (NULL != pservice->reg_pproc))
- can_set = 0;
- } break;
- case VPU_PP : {
- if (NULL == pservice->reg_codec) {
- if (NULL == pservice->reg_pproc)
+ // first check can_set flag
+ if (change_able || !reset_request) {
+ switch (reg->type) {
+ case VPU_ENC : {
+ if (change_able)
can_set = 1;
- } else {
- if ((VPU_DEC == pservice->reg_codec->type) && (NULL == pservice->reg_pproc))
+ } break;
+ case VPU_DEC : {
+ if (NULL == pservice->reg_codec)
can_set = 1;
- /* can not charge frequency when vpu is working */
- if (pservice->auto_freq)
+ if (pservice->auto_freq && (NULL != pservice->reg_pproc))
can_set = 0;
- }
- } break;
- case VPU_DEC_PP : {
- if ((NULL == pservice->reg_codec) && (NULL == pservice->reg_pproc))
- can_set = 1;
} break;
- default : {
- printk("undefined reg type %d\n", reg->type);
- } break;
+ case VPU_PP : {
+ if (NULL == pservice->reg_codec) {
+ if (NULL == pservice->reg_pproc)
+ can_set = 1;
+ } else {
+ if ((VPU_DEC == pservice->reg_codec->type) && (NULL == pservice->reg_pproc))
+ can_set = 1;
+ /* can not charge frequency when vpu is working */
+ if (pservice->auto_freq)
+ can_set = 0;
+ }
+ } break;
+ case VPU_DEC_PP : {
+ if (change_able)
+ can_set = 1;
+ } break;
+ default : {
+ printk("undefined reg type %d\n", reg->type);
+ } break;
+ }
}
+
+ // then check reset request
+ if (reset_request && !change_able)
+ reset_request = 0;
+
+ // do reset before setting registers
+ if (reset_request)
+ vpu_reset(data);
+
if (can_set) {
reg_from_wait_to_run(pservice, reg);
- reg_copy_to_hw(data, reg);
+ reg_copy_to_hw(reg->data, reg);
}
}
vpu_debug_leave();
struct vpu_service_info *pservice = data->pservice;
vpu_session *session = (vpu_session *)filp->private_data;
vpu_debug_enter();
- vpu_debug(3, "cmd %x, VPU_IOC_SET_CLIENT_TYPE %x\n", cmd, (u32)VPU_IOC_SET_CLIENT_TYPE);
if (NULL == session)
return -EINVAL;
switch (cmd) {
case VPU_IOC_SET_CLIENT_TYPE : {
session->type = (enum VPU_CLIENT_TYPE)arg;
+ vpu_debug(DEBUG_IOCTL, "VPU_IOC_SET_CLIENT_TYPE %d\n", session->type);
break;
}
case VPU_IOC_GET_HW_FUSE_STATUS : {
- vpu_request req;
- if (copy_from_user(&req, (void __user *)arg, sizeof(vpu_request))) {
+ struct vpu_request req;
+ vpu_debug(DEBUG_IOCTL, "VPU_IOC_GET_HW_FUSE_STATUS type %d\n", session->type);
+ if (copy_from_user(&req, (void __user *)arg, sizeof(struct vpu_request))) {
vpu_err("error: VPU_IOC_GET_HW_FUSE_STATUS copy_from_user failed\n");
return -EFAULT;
} else {
break;
}
case VPU_IOC_SET_REG : {
- vpu_request req;
+ struct vpu_request req;
vpu_reg *reg;
+ vpu_debug(DEBUG_IOCTL, "VPU_IOC_SET_REG type %d\n", session->type);
if (copy_from_user(&req, (void __user *)arg,
- sizeof(vpu_request))) {
+ sizeof(struct vpu_request))) {
vpu_err("error: VPU_IOC_SET_REG copy_from_user failed\n");
return -EFAULT;
}
break;
}
case VPU_IOC_GET_REG : {
- vpu_request req;
+ struct vpu_request req;
vpu_reg *reg;
+ vpu_debug(DEBUG_IOCTL, "VPU_IOC_GET_REG type %d\n", session->type);
if (copy_from_user(&req, (void __user *)arg,
- sizeof(vpu_request))) {
+ sizeof(struct vpu_request))) {
vpu_err("error: VPU_IOC_GET_REG copy_from_user failed\n");
return -EFAULT;
} else {
case VPU_IOC_PROBE_IOMMU_STATUS: {
int iommu_enable = 0;
+ vpu_debug(DEBUG_IOCTL, "VPU_IOC_PROBE_IOMMU_STATUS\n");
+
#if defined(CONFIG_VCODEC_MMU)
iommu_enable = data->mmu_dev ? 1 : 0;
#endif
switch (cmd) {
case COMPAT_VPU_IOC_SET_CLIENT_TYPE : {
session->type = (enum VPU_CLIENT_TYPE)arg;
+ vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_SET_CLIENT_TYPE type %d\n", session->type);
break;
}
case COMPAT_VPU_IOC_GET_HW_FUSE_STATUS : {
- vpu_request req;
+ struct compat_vpu_request req;
+ vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_GET_HW_FUSE_STATUS type %d\n", session->type);
if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg),
- sizeof(vpu_request))) {
+ sizeof(struct compat_vpu_request))) {
vpu_err("error: VPU_IOC_GET_HW_FUSE_STATUS"
" copy_from_user failed\n");
return -EFAULT;
break;
}
case COMPAT_VPU_IOC_SET_REG : {
- vpu_request req;
+ struct compat_vpu_request req;
vpu_reg *reg;
+ vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_SET_REG type %d\n", session->type);
if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg),
- sizeof(vpu_request))) {
+ sizeof(struct compat_vpu_request))) {
vpu_err("VPU_IOC_SET_REG copy_from_user failed\n");
return -EFAULT;
}
break;
}
case COMPAT_VPU_IOC_GET_REG : {
- vpu_request req;
+ struct compat_vpu_request req;
vpu_reg *reg;
+ vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_GET_REG type %d\n", session->type);
if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg),
- sizeof(vpu_request))) {
+ sizeof(struct compat_vpu_request))) {
vpu_err("VPU_IOC_GET_REG copy_from_user failed\n");
return -EFAULT;
} else {
case COMPAT_VPU_IOC_PROBE_IOMMU_STATUS : {
int iommu_enable = 0;
+ vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_PROBE_IOMMU_STATUS\n");
#if defined(CONFIG_VCODEC_MMU)
iommu_enable = data->mmu_dev ? 1 : 0;
#endif
#ifdef CONFIG_COMPAT
.compat_ioctl = compat_vpu_service_ioctl,
#endif
- //.fasync = vpu_service_fasync,
};
static irqreturn_t vdpu_irq(int irq, void *dev_id);
if (pservice->reg_codec) {
struct vcodec_mem_region *mem, *n;
int i = 0;
- vpu_debug(3, "vcodec, fault addr 0x%08x\n", (u32)fault_addr);
+ vpu_debug(DEBUG_IOMMU, "vcodec, fault addr 0x%08x\n", (u32)fault_addr);
list_for_each_entry_safe(mem, n,
&pservice->reg_codec->mem_region_list,
reg_lnk) {
- vpu_debug(3, "vcodec, reg[%02u] mem region [%02d] 0x%08x %ld\n",
+ vpu_debug(DEBUG_IOMMU, "vcodec, reg[%02u] mem region [%02d] 0x%08x %ld\n",
mem->reg_idx, i, (u32)mem->iova, mem->len);
i++;
}
of_property_read_string(np, "name", (const char**)&name);
of_property_read_u32(np, "dev_mode", (u32*)&data->mode);
- dev_set_name(dev, name);
+ /*dev_set_name(dev, name);*/
if (pservice->reg_base == 0) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
atomic_set(&data->enc_dev.irq_count_pp, 0);
#if defined(CONFIG_VCODEC_MMU)
if (iommu_en) {
- vcodec_enter_mode(data);
if (data->mode == VCODEC_RUNNING_MODE_HEVC)
sprintf(mmu_dev_dts_name,
HEVC_IOMMU_COMPATIBLE_NAME);
rockchip_iovmm_set_fault_handler(dev, vcodec_sysmmu_fault_hdl);
}
#endif
+ get_hw_info(data);
+ pservice->auto_freq = true;
+
+ vcodec_exit_mode(data);
/* create device node */
- ret = alloc_chrdev_region(&data->dev_t, 0, 1, dev_name(dev));
+ ret = alloc_chrdev_region(&data->dev_t, 0, 1, name);
if (ret) {
dev_err(dev, "alloc dev_t failed\n");
goto err;
goto err;
}
- data->cls = class_create(THIS_MODULE, dev_name(dev));
+ data->cls = class_create(THIS_MODULE, name);
if (IS_ERR(data->cls)) {
ret = PTR_ERR(data->cls);
}
data->child_dev = device_create(data->cls, dev,
- data->dev_t, NULL, dev_name(dev));
-
- get_hw_info(data);
+ data->dev_t, NULL, name);
platform_set_drvdata(pdev, data);
INIT_LIST_HEAD(&data->lnk_service);
list_add_tail(&data->lnk_service, &pservice->subdev_list);
-
+
#ifdef CONFIG_DEBUG_FS
data->debugfs_dir =
- vcodec_debugfs_create_device_dir((char*)dev_name(dev), parent);
+ vcodec_debugfs_create_device_dir((char*)name, parent);
if (data->debugfs_dir == NULL)
- vpu_err("create debugfs dir %s failed\n", dev_name(dev));
+ vpu_err("create debugfs dir %s failed\n", name);
data->debugfs_file_regs =
debugfs_create_file("regs", 0664,
data->debugfs_dir, data,
&debug_vcodec_fops);
-#endif
+#endif
return 0;
err:
if (data->irq_enc > 0)
free_irq(data->irq_dec, (void *)&data);
#ifdef CONFIG_DEBUG_FS
- debugfs_remove(data->debugfs_file_regs);
- debugfs_remove(data->debugfs_dir);
+ debugfs_remove_recursive(data->debugfs_dir);
#endif
}
pservice->grf_base = (u32*)RK_GRF_VIRT;
#endif
if (IS_ERR(pservice->grf_base)) {
+#ifdef CONFIG_ARM
+ pservice->grf_base = RK_GRF_VIRT;
+#else
vpu_err("can't find vpu grf property\n");
return;
+#endif
+ }
+
+#ifdef CONFIG_RESET_CONTROLLER
+ pservice->rst_a = devm_reset_control_get(pservice->dev, "video_a");
+ pservice->rst_h = devm_reset_control_get(pservice->dev, "video_h");
+ pservice->rst_v = devm_reset_control_get(pservice->dev, "video");
+
+ if (IS_ERR_OR_NULL(pservice->rst_a)) {
+ pr_warn("No reset resource define\n");
+ pservice->rst_a = NULL;
+ }
+
+ if (IS_ERR_OR_NULL(pservice->rst_h)) {
+ pr_warn("No reset resource define\n");
+ pservice->rst_h = NULL;
+ }
+
+ if (IS_ERR_OR_NULL(pservice->rst_v)) {
+ pr_warn("No reset resource define\n");
+ pservice->rst_v = NULL;
}
+#endif
+
of_property_read_string(np, "name", (const char**)&pservice->name);
}
pservice->reg_pproc = NULL;
atomic_set(&pservice->total_running, 0);
- pservice->enabled = false;
+ atomic_set(&pservice->enabled, 0);
+ atomic_set(&pservice->power_on_cnt, 0);
+ atomic_set(&pservice->power_off_cnt, 0);
+ atomic_set(&pservice->reset_request, 0);
INIT_DELAYED_WORK(&pservice->power_off_work, vpu_power_off_work);
vpu_err("failed to create ion client for vcodec ret %ld\n",
PTR_ERR(pservice->ion_client));
} else {
- vpu_debug(3, "vcodec ion client create success!\n");
+ vpu_debug(DEBUG_IOMMU, "vcodec ion client create success!\n");
}
}
pr_info("probe device %s\n", dev_name(dev));
+ pservice->dev = dev;
+
vcodec_read_property(np, pservice);
vcodec_init_drvdata(pservice);
else
pservice->dev_id = VCODEC_DEVICE_ID_COMBO;
- pservice->dev = dev;
-
if (0 > vpu_get_clk(pservice))
goto err;
enc->reg_size = data->reg_size;
enc->reserv[0] = enc->reserv[1] = 0;
}
- pservice->auto_freq = soc_is_rk2928g() || soc_is_rk2928l() || soc_is_rk2926() || soc_is_rk3288();
- if (pservice->auto_freq) {
- vpu_debug(3, "vpu_service set to auto frequency mode\n");
- atomic_set(&pservice->freq_status, VPU_FREQ_BUT);
- }
+ pservice->auto_freq = true;
+ vpu_debug(DEBUG_EXTRA_INFO, "vpu_service set to auto frequency mode\n");
+ atomic_set(&pservice->freq_status, VPU_FREQ_BUT);
pservice->bug_dec_addr = cpu_is_rk30xx();
} else {
}
}
+static bool check_irq_err(task_info *task, u32 irq_status)
+{
+ return (task->error_mask & irq_status) ? true : false;
+}
+
static irqreturn_t vdpu_irq(int irq, void *dev_id)
{
struct vpu_subdev_data *data = (struct vpu_subdev_data*)dev_id;
struct vpu_service_info *pservice = data->pservice;
vpu_device *dev = &data->dec_dev;
u32 raw_status;
- u32 irq_status;
+ u32 dec_status;
- vcodec_enter_mode(data);
+ /*vcodec_enter_mode(data);*/
- irq_status = raw_status = readl(dev->hwregs + DEC_INTERRUPT_REGISTER);
+ dec_status = raw_status = readl(dev->hwregs + DEC_INTERRUPT_REGISTER);
- vpu_debug(3, "%s status %08x\n", __func__, raw_status);
-
- if (irq_status & DEC_INTERRUPT_BIT) {
- pr_debug("dec_isr dec %x\n", irq_status);
- if ((irq_status & 0x40001) == 0x40001) {
+ if (dec_status & DEC_INTERRUPT_BIT) {
+ time_record(&tasks[TASK_VPU_DEC], 1);
+ vpu_debug(DEBUG_IRQ_STATUS, "vdpu_irq dec status %08x\n", dec_status);
+ if ((dec_status & 0x40001) == 0x40001) {
do {
- irq_status =
+ dec_status =
readl(dev->hwregs +
DEC_INTERRUPT_REGISTER);
- } while ((irq_status & 0x40001) == 0x40001);
+ } while ((dec_status & 0x40001) == 0x40001);
+ }
+
+ if (check_irq_err((data->hw_info->hw_id == HEVC_ID)?
+ (&tasks[TASK_RKDEC_HEVC]) : (&tasks[TASK_VPU_DEC]),
+ dec_status)) {
+ atomic_add(1, &pservice->reset_request);
}
writel(0, dev->hwregs + DEC_INTERRUPT_REGISTER);
atomic_add(1, &dev->irq_count_codec);
+ time_diff(&tasks[TASK_VPU_DEC]);
}
if (data->hw_info->hw_id != HEVC_ID) {
- irq_status = readl(dev->hwregs + PP_INTERRUPT_REGISTER);
- if (irq_status & PP_INTERRUPT_BIT) {
- pr_debug("vdpu_isr pp %x\n", irq_status);
+ u32 pp_status = readl(dev->hwregs + PP_INTERRUPT_REGISTER);
+ if (pp_status & PP_INTERRUPT_BIT) {
+ time_record(&tasks[TASK_VPU_PP], 1);
+ vpu_debug(DEBUG_IRQ_STATUS, "vdpu_irq pp status %08x\n", pp_status);
+
+ if (check_irq_err(&tasks[TASK_VPU_PP], dec_status))
+ atomic_add(1, &pservice->reset_request);
+
/* clear pp IRQ */
- writel(irq_status & (~DEC_INTERRUPT_BIT), dev->hwregs + PP_INTERRUPT_REGISTER);
+ writel(pp_status & (~DEC_INTERRUPT_BIT), dev->hwregs + PP_INTERRUPT_REGISTER);
atomic_add(1, &dev->irq_count_pp);
+ time_diff(&tasks[TASK_VPU_PP]);
}
}
pservice->irq_status = raw_status;
- vcodec_exit_mode(pservice);
+ /*vcodec_exit_mode(pservice);*/
- return IRQ_WAKE_THREAD;
+ if (atomic_read(&dev->irq_count_pp) ||
+ atomic_read(&dev->irq_count_codec))
+ return IRQ_WAKE_THREAD;
+ else
+ return IRQ_NONE;
}
static irqreturn_t vdpu_isr(int irq, void *dev_id)
mutex_lock(&pservice->lock);
if (atomic_read(&dev->irq_count_codec)) {
-#if VPU_SERVICE_SHOW_TIME
- do_gettimeofday(&dec_end);
- vpu_debug(3, "dec task: %ld ms\n",
- (dec_end.tv_sec - dec_start.tv_sec) * 1000 +
- (dec_end.tv_usec - dec_start.tv_usec) / 1000);
-#endif
atomic_sub(1, &dev->irq_count_codec);
if (NULL == pservice->reg_codec) {
vpu_err("error: dec isr with no task waiting\n");
} else {
reg_from_run_to_done(data, pservice->reg_codec);
+ /* avoid vpu timeout and can't recover problem */
+ VDPU_SOFT_RESET(data->regs);
}
}
if (atomic_read(&dev->irq_count_pp)) {
-#if VPU_SERVICE_SHOW_TIME
- do_gettimeofday(&pp_end);
- printk("pp task: %ld ms\n",
- (pp_end.tv_sec - pp_start.tv_sec) * 1000 +
- (pp_end.tv_usec - pp_start.tv_usec) / 1000);
-#endif
atomic_sub(1, &dev->irq_count_pp);
if (NULL == pservice->reg_pproc) {
vpu_err("error: pp isr with no task waiting\n");
vpu_device *dev = &data->enc_dev;
u32 irq_status;
- vcodec_enter_mode(data);
+ /*vcodec_enter_mode(data);*/
irq_status= readl(dev->hwregs + ENC_INTERRUPT_REGISTER);
- pr_debug("vepu_irq irq status %x\n", irq_status);
+ vpu_debug(DEBUG_IRQ_STATUS, "vepu_irq irq status %x\n", irq_status);
-#if VPU_SERVICE_SHOW_TIME
- do_gettimeofday(&enc_end);
- vpu_debug(3, "enc task: %ld ms\n",
- (enc_end.tv_sec - enc_start.tv_sec) * 1000 +
- (enc_end.tv_usec - enc_start.tv_usec) / 1000);
-#endif
if (likely(irq_status & ENC_INTERRUPT_BIT)) {
+ time_record(&tasks[TASK_VPU_ENC], 1);
+
+ if (check_irq_err(&tasks[TASK_VPU_ENC], irq_status))
+ atomic_add(1, &pservice->reset_request);
+
/* clear enc IRQ */
writel(irq_status & (~ENC_INTERRUPT_BIT), dev->hwregs + ENC_INTERRUPT_REGISTER);
atomic_add(1, &dev->irq_count_codec);
+ time_diff(&tasks[TASK_VPU_ENC]);
}
pservice->irq_status = irq_status;
- vcodec_exit_mode(pservice);
+ /*vcodec_exit_mode(pservice);*/
- return IRQ_WAKE_THREAD;
+ if (atomic_read(&dev->irq_count_codec))
+ return IRQ_WAKE_THREAD;
+ else
+ return IRQ_NONE;
}
static irqreturn_t vepu_isr(int irq, void *dev_id)
seq_printf(s, "done register set\n");
}
}
+
+ seq_printf(s, "\npower counter: on %d off %d\n",
+ atomic_read(&pservice->power_on_cnt),
+ atomic_read(&pservice->power_off_cnt));
mutex_unlock(&pservice->lock);
+ vpu_service_power_off(pservice);
return 0;
}
{
int size = (len+15) & (~15);
struct ion_handle *handle;
- u8 *ptr;// = (u8*)kzalloc(size, GFP_KERNEL);
+ u8 *ptr;
if (ion_client == NULL)
ion_client = rockchip_ion_client_create("vcodec");
{
vpu_session session;
vpu_reg *reg;
- unsigned long size = 272;//sizeof(register_00); // registers array length
+ unsigned long size = 272;
int testidx = 0;
int ret = 0;
u8 *pps_tbl[TEST_CNT];
vpu_err("test index %d failed\n", testidx);
break;
} else {
- vpu_debug(3, "test index %d success\n", testidx);
+ vpu_debug(DEBUG_EXTRA_INFO, "test index %d success\n", testidx);
vpu_reg *reg = list_entry(session.done.next, vpu_reg, session_link);