blob: 7d28ea390ed1f1d9a0a08453d92d89aeda7a6444 [file] [log] [blame]
/*
* drivers/amlogic/media/video_processor/pic_dev/picdec.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/time.h>
#include <linux/of_reserved_mem.h>
#include <linux/amlogic/media/vout/vinfo.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/platform_device.h>
#include <linux/amlogic/media/frame_sync/ptsserv.h>
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vfm/vframe_receiver.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/media/utils/amlog.h>
#include <linux/amlogic/media/ge2d/ge2d.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/semaphore.h>
#include <linux/sched/rt.h>
#include <linux/platform_device.h>
#include "picdec.h"
#include <linux/videodev2.h>
#include <media/videobuf-core.h>
#include <media/videobuf2-core.h>
#include <media/videobuf-dma-contig.h>
#include <media/videobuf-vmalloc.h>
#include <media/videobuf-dma-sg.h>
#include <linux/amlogic/media/v4l_util/videobuf-res.h>
#include <linux/amlogic/media/frame_provider/tvin/tvin_v4l2.h>
#include <linux/ctype.h>
#include <linux/of.h>
#include <linux/sizes.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#include <linux/dma-mapping.h>
#include <linux/of_fdt.h>
#include <linux/dma-contiguous.h>
static int debug_flag;
static int dump_file_flag;
static int p2p_mode = 2;
static int output_format_mode = 1;
static int txlx_output_format_mode = 1;
static int cma_layout_flag = 1;
#define NO_TASK_MODE
#define aml_pr_info(level, fmt, arg...) \
do { \
if (debug_flag >= level) \
pr_info(fmt, ## arg); \
} while (0)
#define DEBUG
#ifdef DEBUG
#define AMLOG 1
#define LOG_LEVEL_VAR amlog_level_picdec
#define LOG_MASK_VAR amlog_mask_picdec
#endif
#define LOG_LEVEL_HIGH 0x00f
#define LOG_LEVEL_1 0x001
#define LOG_LEVEL_LOW 0x000
#define LOG_LEVEL_DESC \
"[0x00]LOW[0X01]LEVEL1[0xf]HIGH"
#define LOG_MASK_INIT 0x001
#define LOG_MASK_IOCTL 0x002
#define LOG_MASK_HARDWARE 0x004
#define LOG_MASK_CONFIG 0x008
#define LOG_MASK_WORK 0x010
#define LOG_MASK_DESC \
"[0x01]:INIT,[0x02]:IOCTL,[0x04]:HARDWARE,[0x08]LOG_MASK_CONFIG[0x10]LOG_MASK_WORK"
static int task_running;
#define MAX_VF_POOL_SIZE 2
#define PIC_DEC_CANVAS_START 3
#define PIC_DEC_CANVAS_Y_FRONT (PIC_DEC_CANVAS_START + 1)
#define PIC_DEC_CANVAS_UV_FRONT (PIC_DEC_CANVAS_Y_FRONT + 1)
#define PIC_DEC_CANVAS_Y_BACK (PIC_DEC_CANVAS_UV_FRONT + 1)
#define PIC_DEC_CANVAS_UV_BACK (PIC_DEC_CANVAS_Y_BACK + 1)
#define PIC_DEC_SOURCE_CANVAS (PIC_DEC_CANVAS_UV_BACK + 1)
/*same as tvin pool*/
static int PICDEC_POOL_SIZE = 2;
static int VF_POOL_SIZE = 2;
static int ZOOM_WIDTH = 1280;
static int ZOOM_HEIGHT = 720;
/*same as tvin pool*/
static struct picdec_device_s picdec_device;
static struct source_input_s picdec_input;
static int picdec_buffer_status;
#define INCPTR(p) ptr_atomic_wrap_inc(&p)
#define GE2D_ENDIAN_SHIFT 24
#define GE2D_ENDIAN_MASK (0x1 << GE2D_ENDIAN_SHIFT)
#define GE2D_BIG_ENDIAN (0 << GE2D_ENDIAN_SHIFT)
#define GE2D_LITTLE_ENDIAN (1 << GE2D_ENDIAN_SHIFT)
#define PROVIDER_NAME "decoder.pic"
static DEFINE_SPINLOCK(lock);
static struct vframe_receiver_op_s *picdec_stop(void);
static inline void ptr_atomic_wrap_inc(u32 *ptr)
{
u32 i = *ptr;
i++;
if (i >= PICDEC_POOL_SIZE)
i = 0;
*ptr = i;
}
static struct vframe_s vfpool[MAX_VF_POOL_SIZE];
static s32 vfbuf_use[MAX_VF_POOL_SIZE];
static s32 fill_ptr, get_ptr, putting_ptr, put_ptr;
struct semaphore pic_vb_start_sema;
struct semaphore pic_vb_done_sema;
static wait_queue_head_t frame_ready;
static int render_frame(struct ge2d_context_s *context,
struct config_para_ex_s *ge2d_config);
static void post_frame(void);
/************************************************
*
* buffer op for video sink.
*
************************************************
*/
static int picdec_canvas_table[2];
static inline u32 index2canvas(u32 index)
{
return picdec_canvas_table[index];
}
static struct vframe_s *picdec_vf_peek(void *);
static int picdec_vf_states(struct vframe_states *states, void *op_arg);
static struct vframe_s *picdec_vf_get(void *);
static void picdec_vf_put(struct vframe_s *vf, void *);
static int picdec_vf_states(struct vframe_states *states, void *op_arg)
{
int i;
unsigned long flags;
spin_lock_irqsave(&lock, flags);
states->vf_pool_size = VF_POOL_SIZE;
i = fill_ptr - get_ptr;
if (i < 0)
i += VF_POOL_SIZE;
states->buf_avail_num = i;
spin_unlock_irqrestore(&lock, flags);
return 0;
}
static struct vframe_s *picdec_vf_peek(void *op_arg)
{
if (get_ptr == fill_ptr)
return NULL;
return &vfpool[get_ptr];
}
static struct vframe_s *picdec_vf_get(void *op_arg)
{
struct vframe_s *vf;
if (get_ptr == fill_ptr)
return NULL;
vf = &vfpool[get_ptr];
INCPTR(get_ptr);
return vf;
}
static void picdec_vf_put(struct vframe_s *vf, void *op_arg)
{
int i;
int canvas_addr;
if (!vf)
return;
INCPTR(putting_ptr);
for (i = 0; i < VF_POOL_SIZE; i++) {
canvas_addr = index2canvas(i);
if (vf->canvas0Addr == canvas_addr) {
vfbuf_use[i] = 0;
pr_info
("******recycle buffer index : %d ******\n", i);
}
}
}
static int picdec_event_cb(int type, void *data, void *private_data)
{
if (type & VFRAME_EVENT_RECEIVER_FORCE_UNREG) {
picdec_stop();
aml_pr_info(0, "picdec device force to quit\n");
}
return 0;
}
static const struct vframe_operations_s picdec_vf_provider = {
.peek = picdec_vf_peek,
.get = picdec_vf_get,
.put = picdec_vf_put,
.event_cb = picdec_event_cb,
.vf_states = picdec_vf_states,
};
static struct vframe_provider_s picdec_vf_prov;
int get_unused_picdec_index(void)
{
int i;
for (i = 0; i < VF_POOL_SIZE; i++) {
if (vfbuf_use[i] == 0)
return i;
}
return -1;
}
static int render_frame(struct ge2d_context_s *context,
struct config_para_ex_s *ge2d_config)
{
struct vframe_s *new_vf;
int index;
struct timeval start;
struct timeval end;
unsigned long time_use = 0;
int temp;
unsigned int phase = 0;
unsigned int h_phase, v_phase;
struct picdec_device_s *dev = &picdec_device;
struct source_input_s *input = &picdec_input;
do_gettimeofday(&start);
index = get_unused_picdec_index();
if (index < 0) {
pr_info("no buffer available, need post ASAP\n");
/* return -1; */
index = fill_ptr;
}
dev->origin_width = input->frame_width;
dev->origin_height = input->frame_height;
if ((picdec_input.rotate == 90) || (picdec_input.rotate == 270)) {
temp = input->frame_width;
input->frame_width = input->frame_height;
input->frame_height = temp;
}
h_phase = (input->frame_width << 18) / dev->disp_width;
v_phase = (input->frame_height << 18) / dev->disp_height;
phase = max(h_phase, v_phase);
dev->p2p_mode = p2p_mode;
switch (dev->p2p_mode) {
case 0:
if ((input->frame_width < dev->disp_width) &&
(input->frame_height < dev->disp_height)) {
dev->target_width = input->frame_width;
dev->target_height = input->frame_height;
} else {
dev->target_width = dev->disp_width;
dev->target_height = dev->disp_height;
}
break;
case 1:
if ((input->frame_width >= ZOOM_WIDTH) &&
(input->frame_height >= ZOOM_HEIGHT)) {
if ((input->frame_width < dev->disp_width) &&
(input->frame_height < dev->disp_height)) {
dev->target_width = input->frame_width;
dev->target_height = input->frame_height;
} else {
dev->target_width = dev->disp_width;
dev->target_height = dev->disp_height;
}
} else {
dev->target_width = dev->disp_width;
dev->target_height = dev->disp_height;
}
break;
case 2:
if ((phase <= (1 << 18)) && (phase >= (1 << 16))) {
input->frame_width =
(input->frame_width << 18) / phase;
input->frame_height =
(input->frame_height << 18) / phase;
} else if (phase < (1 << 16)) {
input->frame_width <<= 2;
input->frame_height <<= 2;
}
dev->target_width = dev->disp_width;
dev->target_height = dev->disp_height;
break;
default:
dev->target_width = dev->disp_width;
dev->target_height = dev->disp_height;
break;
}
aml_pr_info(1, "p2p_mode :%d ----render buffer index is %d\n",
dev->p2p_mode, index);
aml_pr_info(1, "render target width: %d ; target height: %d\n",
dev->target_width, dev->target_height);
new_vf = &vfpool[fill_ptr];
new_vf->canvas0Addr = new_vf->canvas1Addr = index2canvas(index);
new_vf->width = dev->target_width;
new_vf->height = dev->target_height;
if (dev->output_format_mode)
new_vf->type =
VIDTYPE_VIU_444 | VIDTYPE_VIU_SINGLE_PLANE
| VIDTYPE_VIU_FIELD | VIDTYPE_PIC;
else
new_vf->type =
VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD |
VIDTYPE_VIU_NV21 | VIDTYPE_PIC;
new_vf->duration_pulldown = 0;
new_vf->index = index;
new_vf->pts = 0;
new_vf->pts_us64 = 0;
/*
new_vf->ratio_control = DISP_RATIO_FORCECONFIG |
DISP_RATIO_FORCE_NORMALWIDE;
*/
new_vf->ratio_control = 0;
vfbuf_use[index]++;
picdec_fill_buffer(new_vf, context, ge2d_config);
do_gettimeofday(&end);
time_use = (end.tv_sec - start.tv_sec) * 1000 +
(end.tv_usec - start.tv_usec) / 1000;
pr_info("render frame time use: %ldms\n", time_use);
return 0;
}
static int render_frame_block(void)
{
struct vframe_s *new_vf;
int index;
struct timeval start;
struct timeval end;
int temp;
unsigned int phase = 0;
unsigned int h_phase, v_phase;
unsigned long time_use = 0;
struct config_para_ex_s ge2d_config;
struct ge2d_context_s *context = picdec_device.context;
struct picdec_device_s *dev = &picdec_device;
struct source_input_s *input = &picdec_input;
do_gettimeofday(&start);
memset(&ge2d_config, 0, sizeof(struct config_para_ex_s));
index = get_unused_picdec_index();
if (index < 0) {
pr_info("no buffer available, need post ASAP\n");
/* return -1; */
index = fill_ptr;
}
dev->origin_width = input->frame_width;
dev->origin_height = input->frame_height;
if ((picdec_input.rotate == 90) || (picdec_input.rotate == 270)) {
temp = input->frame_width;
input->frame_width = input->frame_height;
input->frame_height = temp;
}
h_phase = (input->frame_width << 18) / dev->disp_width;
v_phase = (input->frame_height << 18) / dev->disp_height;
phase = max(h_phase, v_phase);
dev->p2p_mode = p2p_mode;
switch (dev->p2p_mode) {
case 0:
if ((input->frame_width < dev->disp_width) &&
(input->frame_height < dev->disp_height)) {
dev->target_width = input->frame_width;
dev->target_height = input->frame_height;
} else {
dev->target_width = dev->disp_width;
dev->target_height = dev->disp_height;
}
break;
case 1:
if ((input->frame_width >= ZOOM_WIDTH) &&
(input->frame_height >= ZOOM_HEIGHT)) {
if ((input->frame_width < dev->disp_width) &&
(input->frame_height < dev->disp_height)) {
dev->target_width = input->frame_width;
dev->target_height = input->frame_height;
} else {
dev->target_width = dev->disp_width;
dev->target_height = dev->disp_height;
}
} else {
dev->target_width = dev->disp_width;
dev->target_height = dev->disp_height;
}
break;
case 2:
if ((phase <= (1 << 18)) && (phase >= (1 << 16))) {
input->frame_width =
(input->frame_width << 18) / phase;
input->frame_height =
(input->frame_height << 18) / phase;
} else if (phase < (1 << 16)) {
input->frame_width <<= 2;
input->frame_height <<= 2;
}
dev->target_width = dev->disp_width;
dev->target_height = dev->disp_height;
break;
default:
dev->target_width = dev->disp_width;
dev->target_height = dev->disp_height;
break;
}
aml_pr_info(1, "p2p_mode :%d ----render buffer index is %d\n",
dev->p2p_mode, index);
aml_pr_info(1, "render target width: %d ; target height: %d\n",
dev->target_width, dev->target_height);
new_vf = &vfpool[fill_ptr];
new_vf->canvas0Addr = new_vf->canvas1Addr = index2canvas(index);
new_vf->width = picdec_device.target_width;
new_vf->height = picdec_device.target_height;
if (dev->output_format_mode)
new_vf->type =
VIDTYPE_VIU_444 | VIDTYPE_VIU_SINGLE_PLANE
| VIDTYPE_VIU_FIELD | VIDTYPE_PIC;
else
new_vf->type =
VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD |
VIDTYPE_VIU_NV21 | VIDTYPE_PIC;
/* indicate the vframe is a full range frame */
new_vf->signal_type =
/* HD default 709 limit */
(1 << 29) /* video available */
| (5 << 26) /* unspecified */
| (1 << 25) /* full */
| (1 << 24) /* color available */
| (1 << 16) /* bt709 */
| (1 << 8) /* bt709 */
| (1 << 0); /* bt709 */
new_vf->duration_pulldown = 0;
new_vf->index = index;
new_vf->pts = 0;
new_vf->pts_us64 = 0;
new_vf->ratio_control = DISP_RATIO_FORCECONFIG |
DISP_RATIO_FORCE_NORMALWIDE;
picdec_device.cur_index = index;
aml_pr_info(1, "picdec_fill_buffer start\n");
picdec_fill_buffer(new_vf, context, &ge2d_config);
aml_pr_info(1, "picdec_fill_buffer finish\n");
do_gettimeofday(&end);
time_use = (end.tv_sec - start.tv_sec) * 1000 +
(end.tv_usec - start.tv_usec) / 1000;
aml_pr_info(1, "Total render frame time use: %ldms\n", time_use);
return 0;
}
static void post_frame(void)
{
vfbuf_use[picdec_device.cur_index]++;
INCPTR(fill_ptr);
vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_VFRAME_READY,
NULL);
}
/*************************************************
*
* buffer op for decoder, camera, etc.
*
*************************************************
*/
void picdec_local_init(void)
{
int i;
for (i = 0; i < MAX_VF_POOL_SIZE; i++)
vfbuf_use[i] = 0;
fill_ptr = get_ptr = putting_ptr = put_ptr = 0;
memset((char *)&picdec_input, 0, sizeof(struct source_input_s));
picdec_device.context = NULL;
picdec_device.cur_index = 0;
}
static struct vframe_receiver_op_s *picdec_stop(void)
{
/* ulong flags; */
vf_unreg_provider(&picdec_vf_prov);
pr_info("vf_unreg_provider : picdec\n");
#ifndef NO_TASK_MODE
stop_picdec_task();
#else
destroy_ge2d_work_queue(picdec_device.context);
#endif
picdec_cma_buf_uninit();
set_freerun_mode(0);
picdec_local_init();
if (picdec_device.use_reserved) {
if (picdec_device.mapping) {
io_mapping_free(picdec_device.mapping);
picdec_device.mapping = 0;
}
}
pr_info("stop picdec task\n");
return (struct vframe_receiver_op_s *)NULL;
}
static int picdec_start(void)
{
resource_size_t buf_start;
unsigned int buf_size;
unsigned int map_start, map_size = 0;
if (picdec_buffer_init() < 0) {
aml_pr_info(0, "no memory, open fail\n");
return -1;
}
get_picdec_buf_info(&buf_start, &buf_size, NULL);
map_start = picdec_device.assit_buf_start;
map_size = buf_size - (picdec_device.assit_buf_start - buf_start);
if (map_size > 0x1800000) /* max size is 3840*2160*3 */
map_size = 0x1800000;
if (picdec_device.use_reserved) {
picdec_device.mapping = io_mapping_create_wc(map_start,
map_size);
if (picdec_device.mapping == NULL) {
aml_pr_info(1, "mapping failed!!!!!!!!!!!!\n");
return -1;
}
aml_pr_info(1, "mapping addr is %x : %p , mapping size is %d\n ",
map_start, picdec_device.mapping, map_size);
} else{
picdec_device.mapping = NULL;
if (!cma_layout_flag)
picdec_device.vir_addr = phys_to_virt(map_start);
}
vf_provider_init(&picdec_vf_prov, PROVIDER_NAME, &picdec_vf_provider,
NULL);
vf_reg_provider(&picdec_vf_prov);
vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL);
set_freerun_mode(1);
picdec_local_init();
#ifndef NO_TASK_MODE
start_picdec_task();
#else
picdec_device.context = create_ge2d_work_queue();
#endif
return 0;
}
static int test_r = 0xff;
static int test_g = 0xff;
static int test_b = 0xff;
/*************************************************
* main task functions.
*************************************************
*/
static unsigned int print_ifmt;
module_param(print_ifmt, uint, 0644);
MODULE_PARM_DESC(print_ifmt, "print input format\n");
/* fill the RGB user buffer to physical buffer */
static void dma_flush(u32 buf_start, u32 buf_size)
{
dma_sync_single_for_device(
&picdec_device.pdev->dev, buf_start,
buf_size, DMA_TO_DEVICE);
}
int picdec_pre_process(void)
{
struct io_mapping *mapping_wc;
void __iomem *buffer_start;
int i, j;
unsigned int dst_addr_off = 0;
char *p;
char *q;
char *ref;
int ret = 0;
int frame_width = picdec_device.origin_width;
int frame_height = picdec_device.origin_height;
int bp = ((frame_width + 0x1f) & ~0x1f) * 3;
struct timeval start;
struct timeval end;
unsigned long time_use = 0;
do_gettimeofday(&start);
get_picdec_buf_info(NULL, NULL, &mapping_wc);
if (picdec_device.use_reserved) {
if (!mapping_wc)
return -1;
buffer_start = io_mapping_map_atomic_wc(mapping_wc, 0);
} else{
buffer_start = phys_to_virt(picdec_device.assit_buf_start);
}
if (buffer_start == NULL) {
pr_info(" assit buffer mapping error\n");
return -1;
}
aml_pr_info(1, "picdec_input width is %d , height is %d\n",
frame_width, frame_height);
if (picdec_input.input == NULL) {
for (j = 0; j < frame_height; j++) {
for (i = 0; i < frame_width * 3; i += 3) {
dst_addr_off = i + bp * j;
*(char *)(buffer_start + dst_addr_off) = test_r;
*(char *)(buffer_start + dst_addr_off + 1) =
test_g;
*(char *)(buffer_start + dst_addr_off + 2) =
test_b;
}
}
aml_pr_info(1, "test color copy finish ###\n");
} else {
switch (picdec_input.format) {
case 0: /* RGB */
p = (char *)buffer_start;
q = (char *)picdec_input.input;
for (j = 0; j < frame_height; j++) {
ret = copy_from_user(p, (void __user *)q,
frame_width * 3);
q += frame_width * 3;
p += bp;
}
break;
case 1: /* RGBA */
p = ref = (char *)buffer_start;
q = (char *)picdec_input.input;
for (j = 0; j < frame_height; j++) {
p = ref;
for (i = 0; i < frame_width; i++) {
ret = copy_from_user(p,
(void __user *)q, 3);
q += 4;
p += 3;
}
ref += bp;
}
aml_pr_info(1, "RGBA copy finish #########################\n");
break;
case 2: /* ARGB */
p = ref = (char *)buffer_start;
q = (char *)picdec_input.input;
for (j = 0; j < frame_height; j++) {
p = ref;
for (i = 0; i < frame_width; i++) {
ret = copy_from_user(p,
(void __user *)(q + 1),
3);
q += 4;
p += 3;
}
ref += bp;
}
aml_pr_info(1, "ARGB copy finish#########################\n");
break;
default:
p = (char *)buffer_start;
q = (char *)picdec_input.input;
for (j = 0; j < frame_height; j++) {
ret = copy_from_user(p, (void __user *)q,
frame_width * 3);
q += frame_width * 3;
p += bp;
}
break;
}
}
if (picdec_device.use_reserved) {
io_mapping_unmap_atomic(buffer_start);
} else {
dma_flush(picdec_device.assit_buf_start,
bp * frame_height);
}
do_gettimeofday(&end);
time_use = (end.tv_sec - start.tv_sec) * 1000 +
(end.tv_usec - start.tv_usec) / 1000;
aml_pr_info(2, "picdec_pre_process time use: %ldms\n", time_use);
return 0;
}
int fill_black_color_by_ge2d(struct vframe_s *vf,
struct ge2d_context_s *context,
struct config_para_ex_s *ge2d_config) {
struct canvas_s cd;
memset(ge2d_config, 0, sizeof(struct config_para_ex_s));
ge2d_config->alu_const_color = 0;
ge2d_config->bitmask_en = 0;
ge2d_config->src1_gb_alpha = 0;
ge2d_config->dst_xy_swap = 0;
canvas_read(vf->canvas0Addr & 0xff, &cd);
ge2d_config->src_planes[0].addr = cd.addr;
ge2d_config->src_planes[0].w = cd.width;
ge2d_config->src_planes[0].h = cd.height;
ge2d_config->dst_planes[0].addr = cd.addr;
ge2d_config->dst_planes[0].w = cd.width;
ge2d_config->dst_planes[0].h = cd.height;
ge2d_config->src_key.key_enable = 0;
ge2d_config->src_key.key_mask = 0;
ge2d_config->src_key.key_mode = 0;
ge2d_config->src_para.canvas_index = vf->canvas0Addr;
ge2d_config->src_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config->src_para.format = GE2D_FORMAT_S24_YUV444;
ge2d_config->src_para.fill_color_en = 0;
ge2d_config->src_para.fill_mode = 0;
ge2d_config->src_para.x_rev = 0;
ge2d_config->src_para.y_rev = 0;
ge2d_config->src_para.color = 0;
ge2d_config->src_para.top = 0;
ge2d_config->src_para.left = 0;
ge2d_config->src_para.width = vf->width;
ge2d_config->src_para.height = vf->height;
ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config->dst_para.canvas_index = vf->canvas0Addr;
ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config->dst_para.format = GE2D_FORMAT_S24_YUV444;
ge2d_config->dst_para.fill_color_en = 0;
ge2d_config->dst_para.fill_mode = 0;
ge2d_config->dst_para.x_rev = 0;
ge2d_config->dst_para.y_rev = 0;
ge2d_config->dst_para.color = 0;
ge2d_config->dst_para.top = 0;
ge2d_config->dst_para.left = 0;
ge2d_config->dst_para.width = vf->width;
ge2d_config->dst_para.height = vf->height;
if (ge2d_context_config_ex(context, ge2d_config) < 0) {
pr_err("++ge2d configing error.\n");
return -2;
}
fillrect(
context,
0,
0,
vf->width,
vf->height,
0x008080ff);
memset(ge2d_config, 0, sizeof(struct config_para_ex_s));
return 0;
}
static int picdec_memset_phyaddr(ulong phys, u32 size, u32 val)
{
u32 i, span = SZ_1M;
u32 count = size / PAGE_ALIGN(span);
ulong addr = phys;
u8 *p;
for (i = 0; i < count; i++) {
addr = phys + i * span;
p = codec_mm_vmap(addr, span);
if (!p)
return -1;
memset(p, val, span);
codec_mm_unmap_phyaddr(p);
}
return 0;
}
int fill_color(struct vframe_s *vf, struct ge2d_context_s *context,
struct config_para_ex_s *ge2d_config)
{
struct io_mapping *mapping_wc;
struct canvas_s cs0, cs1, cs2;
struct timeval start;
struct timeval end;
unsigned long time_use = 0;
void __iomem *p;
int ret = 0;
do_gettimeofday(&start);
get_picdec_buf_info(NULL, NULL, &mapping_wc);
if (picdec_device.use_reserved) {
if (!mapping_wc)
return -1;
}
canvas_read(vf->canvas0Addr & 0xff, &cs0);
canvas_read((vf->canvas0Addr >> 8) & 0xff, &cs1);
canvas_read((vf->canvas0Addr >> 16) & 0xff, &cs2);
if (picdec_device.use_reserved) {
p = ioremap_nocache(cs0.addr, cs0.width * cs0.height);
memset(p, 0, cs0.width * cs0.height);
iounmap(p);
p = ioremap_nocache(cs1.addr, cs1.width * cs1.height);
memset(p, 0x80, cs1.width * cs1.height);
iounmap(p);
} else {
if (picdec_device.output_format_mode) {
ret = fill_black_color_by_ge2d(vf, context,
ge2d_config);
if (ret < 0)
pr_err("fill black color by ge2d failed\n");
} else {
if (!cma_layout_flag) {
p = phys_to_virt(cs0.addr);
memset(p, 0, cs0.width * cs0.height);
p = phys_to_virt(cs1.addr);
memset(p, 0x80, cs1.width * cs1.height);
} else {
picdec_memset_phyaddr(cs0.addr,
(cs0.width * cs0.height), 0);
picdec_memset_phyaddr(cs1.addr,
(cs1.width * cs1.height), 0x80);
}
}
}
do_gettimeofday(&end);
time_use = (end.tv_sec - start.tv_sec) * 1000 +
(end.tv_usec - start.tv_usec) / 1000;
if (debug_flag)
pr_info("clear background time use: %ldms\n", time_use);
return 0;
}
static void rotate_adjust(int w_in, int h_in, int *w_out, int *h_out, int angle)
{
int w = 0, h = 0, disp_w = 0, disp_h = 0;
disp_w = *w_out;
disp_h = *h_out;
if ((angle == 90) || (angle == 270)) {
if ((w_in < disp_h) && (h_in < disp_w)) {
w = h_in;
h = w_in;
} else {
h = min_t(int, w_in, disp_h);
w = h_in * h / w_in;
if (w > disp_w) {
h = (h * disp_w) / w;
w = disp_w;
}
}
} else {
if ((w_in < disp_w) && (h_in < disp_h)) {
w = w_in;
h = h_in;
} else {
if ((w_in * disp_h) > (disp_w * h_in)) {
w = disp_w;
h = disp_w * h_in / w_in;
} else {
h = disp_h;
w = disp_h * w_in / h_in;
}
}
}
*w_out = w;
*h_out = h;
}
static int copy_phybuf_to_file(ulong phys, u32 size,
struct file *fp, loff_t pos)
{
u32 i, span = SZ_1M;
u32 count = size / PAGE_ALIGN(span);
ulong addr = phys;
u8 *p;
for (i = 0; i < count; i++) {
addr = phys + i * span;
p = codec_mm_vmap(addr, span);
if (!p)
return -1;
vfs_write(fp, (char *)p,
span, &pos);
pos += span;
codec_mm_unmap_phyaddr(p);
}
return 0;
}
int picdec_fill_buffer(struct vframe_s *vf, struct ge2d_context_s *context,
struct config_para_ex_s *ge2d_config)
{
struct canvas_s cs0, cs1, cs2;
int canvas_id = PIC_DEC_SOURCE_CANVAS;
int canvas_width = (picdec_device.origin_width + 0x1f) & ~0x1f;
int canvas_height = (picdec_device.origin_height + 0xf) & ~0xf;
int frame_width = picdec_input.frame_width;
int frame_height = picdec_input.frame_height;
int dst_top, dst_left, dst_width, dst_height;
struct file *filp = NULL;
loff_t pos = 0;
int ret = 0;
void __iomem *p;
mm_segment_t old_fs = get_fs();
fill_color(vf, context, ge2d_config);
canvas_config(PIC_DEC_SOURCE_CANVAS,
(ulong)(picdec_device.assit_buf_start),
canvas_width * 3, canvas_height,
CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
if (!cma_layout_flag) {
aml_pr_info(1, "picdec_pre_process start\n");
picdec_pre_process();
aml_pr_info(1, "picdec_pre_process finish\n");
} else {
dma_flush(picdec_device.assit_buf_start,
(canvas_width * picdec_device.origin_height * 3));
}
ge2d_config->alu_const_color = 0; /* 0x000000ff; */
ge2d_config->bitmask_en = 0;
ge2d_config->src1_gb_alpha = 0; /* 0xff; */
ge2d_config->dst_xy_swap = 0;
canvas_read((canvas_id & 0xff), &cs0);
canvas_read(((canvas_id >> 8) & 0xff), &cs1);
canvas_read(((canvas_id >> 16) & 0xff), &cs2);
ge2d_config->src_planes[0].addr = cs0.addr;
ge2d_config->src_planes[0].w = cs0.width;
ge2d_config->src_planes[0].h = cs0.height;
ge2d_config->src_planes[1].addr = cs1.addr;
ge2d_config->src_planes[1].w = cs1.width;
ge2d_config->src_planes[1].h = cs1.height;
ge2d_config->src_planes[2].addr = cs2.addr;
ge2d_config->src_planes[2].w = cs2.width;
ge2d_config->src_planes[2].h = cs2.height;
aml_pr_info(1, "src addr is %lx , plane 0 width is %d , height is %d\n",
cs0.addr, cs0.width, cs0.height);
ge2d_config->src_key.key_enable = 0;
ge2d_config->src_key.key_mask = 0;
ge2d_config->src_key.key_mode = 0;
ge2d_config->src_para.canvas_index = PIC_DEC_SOURCE_CANVAS;
ge2d_config->src_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config->src_para.format = GE2D_FORMAT_S24_BGR;
ge2d_config->src_para.fill_color_en = 0;
ge2d_config->src_para.fill_mode = 0;
ge2d_config->src_para.x_rev = 0;
ge2d_config->src_para.y_rev = 0;
ge2d_config->src_para.color = 0xffffffff;
ge2d_config->src_para.top = 0;
ge2d_config->src_para.left = 0;
ge2d_config->src_para.width = picdec_device.origin_width;
ge2d_config->src_para.height = picdec_device.origin_height;
ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID;
ge2d_config->dst_para.canvas_index = vf->canvas0Addr;
canvas_read(vf->canvas0Addr & 0xff, &cs0);
canvas_read((vf->canvas0Addr >> 8) & 0xff, &cs1);
canvas_read((vf->canvas0Addr >> 16) & 0xff, &cs2);
ge2d_config->dst_planes[0].addr = cs0.addr;
ge2d_config->dst_planes[0].w = cs0.width;
ge2d_config->dst_planes[0].h = cs0.height;
aml_pr_info(1, "dst 0 addr is %x\n", (unsigned int)cs0.addr);
ge2d_config->dst_planes[1].addr = cs1.addr;
ge2d_config->dst_planes[1].w = cs1.width;
ge2d_config->dst_planes[1].h = cs1.height;
aml_pr_info(1, "dst 1 addr is %x\n", (unsigned int)cs1.addr);
ge2d_config->dst_planes[2].addr = cs2.addr;
ge2d_config->dst_planes[2].w = cs2.width;
ge2d_config->dst_planes[2].h = cs2.height;
aml_pr_info(1, "dst 2 addr is %x\n", (unsigned int)cs2.addr);
ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID;
if (picdec_device.output_format_mode)
ge2d_config->dst_para.format = GE2D_FORMAT_S24_YUV444;
else
ge2d_config->dst_para.format = GE2D_FORMAT_M24_NV21;
ge2d_config->dst_para.fill_color_en = 0;
ge2d_config->dst_para.fill_mode = 0;
ge2d_config->dst_para.x_rev = 0;
ge2d_config->dst_para.y_rev = 0;
ge2d_config->dst_para.color = 0;
ge2d_config->dst_para.top = 0;
ge2d_config->dst_para.left = 0;
ge2d_config->dst_para.width = picdec_device.disp_width;
ge2d_config->dst_para.height = picdec_device.disp_height;
if (picdec_input.rotate == 90) {
ge2d_config->dst_xy_swap = 1;
ge2d_config->dst_para.x_rev ^= 1;
} else if (picdec_input.rotate == 180) {
ge2d_config->dst_para.x_rev ^= 1;
ge2d_config->dst_para.y_rev ^= 1;
} else if (picdec_input.rotate == 270) {
ge2d_config->dst_xy_swap = 1;
ge2d_config->dst_para.y_rev ^= 1;
} else {
ge2d_config->dst_para.x_rev = 0;
ge2d_config->dst_para.y_rev = 0;
}
if (ge2d_context_config_ex(context, ge2d_config) < 0) {
pr_info("++ge2d configing error.\n");
return -1;
}
dst_top = 0;
dst_left = 0;
dst_width = picdec_device.target_width;
dst_height = picdec_device.target_height;
rotate_adjust(frame_width, frame_height, &dst_width, &dst_height,
0);
dst_width = (dst_width + 0x1) & ~0x1;
dst_height = (dst_height + 0x1) & ~0x1;
switch (picdec_device.p2p_mode) {
case 0:
dst_left = 0;
dst_top = 0;
vf->width = dst_width;
vf->height = dst_height;
break;
case 1:
if ((picdec_input.frame_width >= ZOOM_WIDTH) &&
(picdec_input.frame_height >= ZOOM_HEIGHT)) {
dst_left = 0;
dst_top = 0;
vf->width = dst_width;
vf->height = dst_height;
} else {
dst_left = (picdec_device.disp_width - dst_width) >> 1;
dst_top = (picdec_device.disp_height - dst_height) >> 1;
}
break;
case 2:
dst_left = (picdec_device.disp_width - dst_width) >> 1;
dst_top = (picdec_device.disp_height - dst_height) >> 1;
break;
default:
dst_left = (picdec_device.disp_width - dst_width) >> 1;
dst_top = (picdec_device.disp_height - dst_height) >> 1;
break;
}
stretchblt_noalpha(context, 0, 0, picdec_device.origin_width,
picdec_device.origin_height,
dst_left, dst_top,
dst_width, dst_height);
/*dump ge2d output buffer data. use this function should close selinux*/
if ((dump_file_flag) && (picdec_device.origin_width > 100)) {
if (picdec_device.output_format_mode) {
set_fs(KERNEL_DS);
filp = filp_open("/data/temp/yuv444.yuv",
O_RDWR|O_CREAT, 0666);
if (IS_ERR(filp)) {
aml_pr_info(0, "open file failed\n");
} else {
if (!cma_layout_flag) {
p = phys_to_virt(cs0.addr);
vfs_write(filp, (char *)p,
cs0.width * cs0.height, &pos);
} else {
ret = copy_phybuf_to_file(cs0.addr,
cs0.width * cs0.height,
filp, 0);
if (ret < 0)
pr_err("write yuv444 file failed.\n");
}
vfs_fsync(filp, 0);
filp_close(filp, NULL);
set_fs(old_fs);
}
} else {
set_fs(KERNEL_DS);
filp = filp_open("/data/temp/NV21.yuv",
O_RDWR|O_CREAT, 0666);
if (IS_ERR(filp)) {
aml_pr_info(0, "open file failed\n");
} else {
if (!cma_layout_flag) {
p = phys_to_virt(cs0.addr);
vfs_write(filp, (char *)p,
cs0.width * cs0.height, &pos);
} else {
ret = copy_phybuf_to_file(cs0.addr,
cs0.width * cs0.height,
filp, 0);
if (ret < 0)
pr_err("write NV21 Y to file failed\n");
}
pos = (loff_t)cs0.width * cs0.height;
if (!cma_layout_flag) {
p = phys_to_virt(cs1.addr);
vfs_write(filp, (char *)p,
cs1.width * cs1.height, &pos);
} else {
ret = copy_phybuf_to_file(cs1.addr,
cs1.width * cs1.height,
filp, pos);
if (ret < 0)
pr_err("write NV21 UV to file failed\n");
}
vfs_fsync(filp, 0);
filp_close(filp, NULL);
set_fs(old_fs);
}
}
}
return 0;
}
static struct task_struct *task;
static int picdec_task(void *data)
{
int ret = 0;
struct sched_param param = {.sched_priority = MAX_USER_RT_PRIO + 1 };
struct ge2d_context_s *context = create_ge2d_work_queue();
struct config_para_ex_s ge2d_config;
#ifdef CONFIG_AMLCAP_LOG_TIME_USEFORFRAMES
struct timeval start;
struct timeval end;
unsigned long time_use = 0;
#endif
memset(&ge2d_config, 0, sizeof(struct config_para_ex_s));
amlog_level(LOG_LEVEL_HIGH, "picdec task is running\n ");
sched_setscheduler(current, SCHED_FIFO, &param);
allow_signal(SIGTERM);
while (1) {
ret = down_interruptible(&pic_vb_start_sema);
if (kthread_should_stop()) {
up(&pic_vb_done_sema);
break;
}
if (!task_running) {
up(&pic_vb_done_sema);
goto picdec_exit;
}
render_frame(context, &ge2d_config);
if (kthread_should_stop()) {
up(&pic_vb_done_sema);
break;
}
up(&pic_vb_done_sema);
}
picdec_exit:
destroy_ge2d_work_queue(context);
picdec_cma_buf_uninit();
while (!kthread_should_stop()) {
/* may not call stop, wait..
* it is killed by SIGTERM,eixt on down_interruptible
* if not call stop,this thread may on do_exit and
* kthread_stop may not work good;
*/
msleep(20);
}
return ret;
}
/************************************************
*
* init functions.
*
************************************************
*/
int picdec_cma_buf_init(void)
{
int flags;
if (!picdec_device.use_reserved) {
switch (picdec_buffer_status) {
case 0:/* not config */
break;
case 1:/* config before , return ok */
return 0;
case 2:/* config fail, won't retry , return failure */
return -1;
default:
return -1;
}
aml_pr_info(0, "reserved memory config fail , use CMA.\n");
if (picdec_device.output_format_mode) {
if (picdec_device.cma_mode == 0) {
picdec_device.cma_pages =
dma_alloc_from_contiguous(
&(picdec_device.pdev->dev),
(72*SZ_1M) >> PAGE_SHIFT, 0);
picdec_device.buffer_start = page_to_phys(
picdec_device.cma_pages);
picdec_device.buffer_size = (72*SZ_1M);
} else{
flags = CODEC_MM_FLAGS_DMA |
CODEC_MM_FLAGS_CMA_CLEAR;
picdec_device.buffer_start =
codec_mm_alloc_for_dma("picdec",
(72*SZ_1M)/PAGE_SIZE, 0, flags);
picdec_device.buffer_size = (72*SZ_1M);
}
} else {
if (picdec_device.cma_mode == 0) {
picdec_device.cma_pages =
dma_alloc_from_contiguous(
&(picdec_device.pdev->dev),
(48*SZ_1M) >> PAGE_SHIFT, 0);
picdec_device.buffer_start = page_to_phys(
picdec_device.cma_pages);
picdec_device.buffer_size = (48*SZ_1M);
} else{
flags = CODEC_MM_FLAGS_DMA |
CODEC_MM_FLAGS_CMA_CLEAR;
picdec_device.buffer_start =
codec_mm_alloc_for_dma("picdec",
(48*SZ_1M)/PAGE_SIZE, 0, flags);
picdec_device.buffer_size = (48*SZ_1M);
}
}
aml_pr_info(0, "cma memory is %x , size is %x\n",
(unsigned int)picdec_device.buffer_start,
(unsigned int)picdec_device.buffer_size);
if (picdec_device.buffer_start == 0) {
picdec_buffer_status = 2;
aml_pr_info(0, "cma memory allocate fail\n");
return -1;
}
picdec_buffer_status = 1;
aml_pr_info(0, "cma memory allocate succeed\n");
return 0;
}
return 0;
}
int picdec_cma_buf_uninit(void)
{
if (!picdec_device.use_reserved) {
if (picdec_device.output_format_mode) {
if (picdec_device.cma_mode == 0) {
if (picdec_device.cma_pages) {
dma_release_from_contiguous(
&picdec_device.pdev->dev,
picdec_device.cma_pages,
(72*SZ_1M) >> PAGE_SHIFT);
picdec_device.cma_pages = NULL;
}
} else {
if (picdec_device.buffer_start != 0) {
codec_mm_free_for_dma(
"picdec",
picdec_device.buffer_start);
picdec_device.buffer_start = 0;
picdec_device.buffer_size = 0;
aml_pr_info(0,
"cma memory release succeed\n");
}
}
} else {
if (picdec_device.cma_mode == 0) {
if (picdec_device.cma_pages) {
dma_release_from_contiguous(
&picdec_device.pdev->dev,
picdec_device.cma_pages,
(48*SZ_1M) >> PAGE_SHIFT);
picdec_device.cma_pages = NULL;
}
} else {
if (picdec_device.buffer_start != 0) {
codec_mm_free_for_dma(
"picdec",
picdec_device.buffer_start);
picdec_device.buffer_start = 0;
picdec_device.buffer_size = 0;
aml_pr_info(0,
"cma memory release succeed\n");
}
}
}
}
picdec_buffer_status = 0;
return 0;
}
int picdec_buffer_init(void)
{
int i;
int ret = 0;
u32 canvas_width, canvas_height;
u32 decbuf_size;
resource_size_t buf_start;
unsigned int buf_size;
unsigned int offset = 0;
picdec_buffer_status = 0;
picdec_device.vinfo = get_current_vinfo();
picdec_device.disp_width = picdec_device.vinfo->width;
picdec_device.disp_height = picdec_device.vinfo->height;
if ((get_cpu_type() == MESON_CPU_MAJOR_ID_TXLX) &&
((picdec_device.disp_width * picdec_device.disp_height) >=
1920 * 1080))
picdec_device.output_format_mode = txlx_output_format_mode;
else
picdec_device.output_format_mode = output_format_mode;
picdec_cma_buf_init();
get_picdec_buf_info(&buf_start, &buf_size, NULL);
aml_pr_info(1, "picdec buffer size is %x\n", buf_size);
sema_init(&pic_vb_start_sema, 1);/*init 1*/
sema_init(&pic_vb_done_sema, 1);/*init 1*/
if (!buf_start || !buf_size) {
ret = -1;
goto exit;
}
canvas_width = (picdec_device.disp_width + 0x1f) & ~0x1f;
canvas_height = (picdec_device.disp_height + 0xf) & ~0xf;
decbuf_size = canvas_width * canvas_height;
if (picdec_device.output_format_mode) {
for (i = 0; i < MAX_VF_POOL_SIZE; i++) {
canvas_config(PIC_DEC_CANVAS_START + i,
(unsigned int)(buf_start + offset),
canvas_width * 3,
canvas_height,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
offset = canvas_width * canvas_height * 3;
picdec_canvas_table[i] =
PIC_DEC_CANVAS_START + i;
vfbuf_use[i] = 0;
}
} else {
for (i = 0; i < MAX_VF_POOL_SIZE; i++) {
pr_info("%d addr is %x################\n",
i, (unsigned int)(buf_start + offset));
canvas_config(PIC_DEC_CANVAS_START + 2 * i,
(unsigned int)(buf_start + offset),
canvas_width,
canvas_height,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
offset += canvas_width * canvas_height;
canvas_config(PIC_DEC_CANVAS_START + 2 * i + 1,
(unsigned int)(buf_start + offset),
canvas_width,
canvas_height / 2,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
offset += canvas_width * canvas_height / 2;
picdec_canvas_table[i] =
(PIC_DEC_CANVAS_START + 2 * i) |
((PIC_DEC_CANVAS_START + 2 * i + 1) << 8);
vfbuf_use[i] = 0;
}
}
if (picdec_device.output_format_mode)
picdec_device.assit_buf_start =
buf_start + canvas_width * canvas_height * 6;
else
picdec_device.assit_buf_start =
buf_start + canvas_width * canvas_height * 3;
exit:
return ret;
}
int start_picdec_task(void)
{
/* init the device. */
if (!task) {
task = kthread_create(picdec_task, &picdec_device, "picdec");
if (IS_ERR(task)) {
amlog_level(LOG_LEVEL_HIGH, "thread creating error.\n");
return -1;
}
init_waitqueue_head(&frame_ready);
wake_up_process(task);
}
task_running = 1;
picdec_device.task_running = task_running;
return 0;
}
void stop_picdec_task(void)
{
if (task) {
task_running = 0;
picdec_device.task_running = task_running;
send_sig(SIGTERM, task, 1);
up(&pic_vb_start_sema);
wake_up_interruptible(&frame_ready);
kthread_stop(task);
task = NULL;
}
}
/***********************************************************************
*
* global status.
*
***********************************************************************
*/
static int picdec_enable_flag;
int get_picdec_status(void)
{
return picdec_enable_flag;
}
void set_picdec_status(int flag)
{
if (flag >= 0)
picdec_enable_flag = flag;
else
picdec_enable_flag = 0;
}
/***********************************************************************
*
* file op section.
*
***********************************************************************
*/
void set_picdec_buf_info(resource_size_t start, unsigned int size)
{
picdec_device.buffer_start = start;
picdec_device.buffer_size = size;
}
void unset_picdec_buf_info(void)
{
if (picdec_device.use_reserved) {
if (picdec_device.mapping) {
io_mapping_free(picdec_device.mapping);
picdec_device.mapping = 0;
}
}
picdec_device.buffer_start = 0;
picdec_device.buffer_size = 0;
}
void get_picdec_buf_info(resource_size_t *start, unsigned int *size,
struct io_mapping **mapping)
{
if (start)
*start = picdec_device.buffer_start;
if (size)
*size = picdec_device.buffer_size;
if (mapping)
*mapping = picdec_device.mapping;
}
static int picdec_open(struct inode *inode, struct file *file)
{
int ret = 0;
if (!cma_layout_flag) {
struct ge2d_context_s *context = NULL;
aml_pr_info(1, "open one picdec device\n");
file->private_data = context;
picdec_device.open_count++;
ret = picdec_start();
} else {
struct picdec_private_s *priv;
aml_pr_info(1, "open one picdec device\n");
priv = kmalloc(sizeof(struct picdec_private_s), GFP_KERNEL);
if (!priv) {
pr_err("alloc memory failed for amvideo cap\n");
return -ENOMEM;
}
memset(priv, 0, sizeof(struct picdec_private_s));
picdec_device.open_count++;
ret = picdec_start();
priv->context = NULL;
priv->phyaddr = (unsigned long)picdec_device.assit_buf_start;
priv->buf_len = (picdec_device.buffer_size/3);
file->private_data = priv;
}
return ret;
}
static int picdec_mmap(struct file *file,
struct vm_area_struct *vma)
{
struct picdec_private_s *priv = file->private_data;
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
unsigned int vm_size = vma->vm_end - vma->vm_start;
if (!priv->phyaddr)
return -EIO;
aml_pr_info(1, "priv->phyaddr = %lx , vm_size = %d\n",
priv->phyaddr, vm_size);
if (vm_size == 0)
return -EAGAIN;
off += priv->phyaddr;
if ((off + vm_size) > (priv->phyaddr + priv->buf_len))
return -ENOMEM;
/*vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);*/
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
if (remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot)) {
pr_err("set_cached: failed remap_pfn_range\n");
return -EAGAIN;
}
aml_pr_info(1, "picdec_mmap ok\n");
return 0;
}
static long picdec_ioctl(struct file *filp, unsigned int cmd,
unsigned long args)
{
int ret = 0;
void __user *argp;
struct ge2d_context_s *context;
struct picdec_private_s *priv;
if (!cma_layout_flag) {
context = (struct ge2d_context_s *) filp->private_data;
} else {
priv = filp->private_data;
context = (struct ge2d_context_s *) priv->context;
}
argp = (void __user *)args;
switch (cmd) {
#ifdef CONFIG_COMPAT
case PICDEC_IOC_FRAME_RENDER32:
aml_pr_info(1, "PICDEC_IOC_FRAME_RENDER32\n");
{
ulong paddr, r;
struct compat_source_input_s __user *uf =
(struct compat_source_input_s *)argp;
memset(&picdec_input, 0, sizeof(struct source_input_s));
r = get_user(paddr, &uf->input);
picdec_input.input = (char *)paddr;
r |= get_user(picdec_input.frame_width,
&uf->frame_width);
r |= get_user(picdec_input.frame_height,
&uf->frame_height);
r |= get_user(picdec_input.format, &uf->format);
r |= get_user(picdec_input.rotate, &uf->rotate);
if (r)
return -EFAULT;
render_frame_block();
}
break;
#endif
case PICDEC_IOC_FRAME_RENDER:
aml_pr_info(1, "PICDEC_IOC_FRAME_RENDER\n");
if (copy_from_user
(&picdec_input, (void *)argp,
sizeof(struct source_input_s)))
return -EFAULT;
aml_pr_info(1, "w:%d,h:%d,format:%d,rotate:%d\n",
picdec_input.frame_width,
picdec_input.frame_height,
picdec_input.format,
picdec_input.rotate);
render_frame_block();
break;
case PICDEC_IOC_FRAME_POST:
pr_info("PICDEC_IOC_FRAME_POST\n");
post_frame();
break;
case PICDEC_IOC_CONFIG_FRAME:
break;
default:
return -ENOIOCTLCMD;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long picdec_compat_ioctl(struct file *filp,
unsigned int cmd, unsigned long args)
{
unsigned long ret;
args = (unsigned long)compat_ptr(args);
ret = picdec_ioctl(filp, cmd, args);
return ret;
}
#endif
static int picdec_release(struct inode *inode, struct file *file)
{
struct ge2d_context_s *context;
struct picdec_private_s *priv = file->private_data;
if (!cma_layout_flag)
context = (struct ge2d_context_s *) file->private_data;
else
context = (struct ge2d_context_s *) priv->context;
aml_pr_info(1, "picdec stop start");
picdec_stop();
if (context && (destroy_ge2d_work_queue(context) == 0)) {
picdec_device.open_count--;
return 0;
}
if (cma_layout_flag) {
kfree(priv);
priv = NULL;
}
aml_pr_info(0, "release one picdec device\n");
return -1;
}
#ifdef CONFIG_COMPAT
static long picdec_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long args);
#endif
/***********************************************************************
*
* file op init section.
*
***********************************************************************
*/
static const struct file_operations picdec_fops = {
.owner = THIS_MODULE,
.open = picdec_open,
.mmap = picdec_mmap,
.unlocked_ioctl = picdec_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = picdec_compat_ioctl,
#endif
.release = picdec_release,
};
/*sys attribute*/
static int parse_para(const char *para, int para_num, int *result)
{
char *token = NULL;
char *params;
int *out = result;
int len = 0, count = 0;
int res = 0;
int ret = 0;
if (!para)
return 0;
params = kstrdup(para, GFP_KERNEL);
token = params;
len = strlen(token);
do {
token = strsep(&params, " ");
while (token && (isspace(*token)
|| !isgraph(*token)) && len) {
token++;
len--;
}
if ((len == 0) || (!token))
break;
ret = kstrtoint(token, 0, &res);
if (ret < 0)
break;
len = strlen(token);
*out++ = res;
count++;
} while ((token) && (count < para_num) && (len > 0));
kfree(params);
return count;
}
static ssize_t frame_render_read(struct class *cla,
struct class_attribute *attr, char *buf)
{
return snprintf(buf, 80,
"render width:%d\nheight:%d\nformat:%d\ndirection:%d\n",
picdec_input.frame_width, picdec_input.frame_height,
picdec_input.format, picdec_input.rotate);
}
static int first_running = 1;
static ssize_t frame_render_write(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
/*
ssize_t size;
char *endp;
*/
int parsed[4];
if (likely(parse_para(buf, 4, parsed) == 4)) {
picdec_input.frame_width = parsed[0];
picdec_input.frame_height = parsed[1];
picdec_input.format = parsed[2];
picdec_input.rotate = parsed[3];
} else
return count;
picdec_input.input = NULL;
if (first_running) {
first_running = 0;
picdec_start();
}
#ifdef NO_TASK_MODE
render_frame_block();
#else
up(&pic_vb_start_sema);
#endif
/* size = endp - buf; */
return count;
}
static ssize_t frame_post_read(struct class *cla,
struct class_attribute *attr, char *buf)
{
return snprintf(buf, 80, "post command is %d\n",
picdec_device.frame_post);
}
static ssize_t frame_post_write(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
unsigned long r;
int ret = 0;
ret = kstrtoul(buf, 0, (unsigned long *)&r);
if (ret < 0)
return -EINVAL;
post_frame();
return count;
}
static ssize_t dec_status_read(struct class *cla,
struct class_attribute *attr, char *buf)
{
return snprintf(buf, 80, "PIC decoder status is %d\n",
(picdec_device.open_count > 0)?1:0);
}
static ssize_t test_color_read(struct class *cla,
struct class_attribute *attr, char *buf)
{
return snprintf(buf, 80, "post command is %d\n",
picdec_device.frame_post);
}
static ssize_t test_color_write(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
/*
ssize_t size;
char *endp;
*/
int parsed[3];
if (likely(parse_para(buf, 3, parsed) == 3)) {
test_r = parsed[0];
test_g = parsed[1];
test_b = parsed[2];
} else
return count;
/*size = endp - buf;*/
return count;
}
static struct class_attribute picdec_class_attrs[] = {
__ATTR(frame_render,
0644,
frame_render_read,
frame_render_write),
__ATTR(frame_post,
0644,
frame_post_read,
frame_post_write),
__ATTR(test_color,
0644,
test_color_read,
test_color_write),
__ATTR(dec_status,
0644,
dec_status_read,
NULL),
__ATTR_NULL
};
#define PICDEC_CLASS_NAME "picdec"
static struct class picdec_class = {
.name = PICDEC_CLASS_NAME,
.class_attrs = picdec_class_attrs,
};
struct class *init_picdec_cls(void)
{
int ret = 0;
ret = class_register(&picdec_class);
if (ret < 0) {
amlog_level(LOG_LEVEL_HIGH,
"error create picdec class\n");
return NULL;
}
return &picdec_class;
}
int init_picdec_device(void)
{
int ret = 0;
strcpy(picdec_device.name, "picdec");
ret = register_chrdev(0, picdec_device.name, &picdec_fops);
if (ret <= 0) {
amlog_level(LOG_LEVEL_HIGH, "register picdec device error\n");
return ret;
}
picdec_device.major = ret;
picdec_device.dbg_enable = 0;
amlog_level(LOG_LEVEL_LOW, "picdec_dev major:%d\n", ret);
picdec_device.cla = init_picdec_cls();
if (picdec_device.cla == NULL)
return -1;
picdec_device.dev =
device_create(picdec_device.cla, NULL,
MKDEV(picdec_device.major, 0),
NULL, picdec_device.name);
if (IS_ERR(picdec_device.dev)) {
amlog_level(LOG_LEVEL_HIGH, "create picdec device error\n");
goto unregister_dev;
}
/* device_create_file( picdec_device.dev, &dev_attr_dump); */
picdec_device.dump = 0;
dev_set_drvdata(picdec_device.dev, &picdec_device);
platform_set_drvdata(picdec_device.pdev, &picdec_device);
vf_provider_init(&picdec_vf_prov, PROVIDER_NAME, &picdec_vf_provider,
NULL);
return 0;
unregister_dev:
class_unregister(picdec_device.cla);
return -1;
}
int uninit_picdec_device(void)
{
if (picdec_device.cla) {
if (picdec_device.dev)
device_destroy(picdec_device.cla,
MKDEV(picdec_device.major, 0));
picdec_device.dev = NULL;
class_unregister(picdec_device.cla);
}
unregister_chrdev(picdec_device.major, picdec_device.name);
return 0;
}
/*******************************************************************
*
* interface for Linux driver
*
* ******************************************************************/
MODULE_AMLOG(AMLOG_DEFAULT_LEVEL, 0xff, LOG_LEVEL_DESC, LOG_MASK_DESC);
/* for driver. */
static int picdec_driver_probe(struct platform_device *pdev)
{
int r;
picdec_device.use_reserved = 0;
aml_pr_info(0, "picdec_driver_probe called.\n");
r = of_reserved_mem_device_init(&pdev->dev);
if (r == 0)
aml_pr_info(0, "picdec_driver_probe done.\n");
picdec_device.cma_mode = 1;
picdec_device.pdev = pdev;
init_picdec_device();
picdec_device.p2p_mode = 0;
picdec_device.output_format_mode = 0;
txlx_output_format_mode = 1;
return r;
}
static int picdec_mem_device_init(struct reserved_mem *rmem, struct device *dev)
{
unsigned long start, end;
start = rmem->base;
end = rmem->base + rmem->size - 1;
aml_pr_info(0, "init picdec memsource %lx->%lx\n", start, end);
picdec_device.use_reserved = 1;
set_picdec_buf_info((resource_size_t) rmem->base, rmem->size);
return 0;
}
static const struct reserved_mem_ops rmem_picdec_ops = {
.device_init = picdec_mem_device_init,
};
static int __init picdec_mem_setup(struct reserved_mem *rmem)
{
rmem->ops = &rmem_picdec_ops;
aml_pr_info(0, "picdec share mem setup\n");
return 0;
}
static int picdec_drv_remove(struct platform_device *plat_dev)
{
uninit_picdec_device();
if (picdec_device.use_reserved) {
if (picdec_device.mapping)
io_mapping_free(picdec_device.mapping);
}
return 0;
}
static const struct of_device_id amlogic_picdec_dt_match[] = {
{
.compatible = "amlogic, picdec",
},
{},
};
/* general interface for a linux driver .*/
static struct platform_driver picdec_drv = {
.probe = picdec_driver_probe,
.remove = picdec_drv_remove,
.driver = {
.name = "picdec",
.owner = THIS_MODULE,
.of_match_table = amlogic_picdec_dt_match,
}
};
static struct mconfig jpeg_configs[] = {
MC_PU32("debug_flag", &debug_flag),
MC_PU32("dump_file_flag", &dump_file_flag),
MC_PU32("p2p_mode", &p2p_mode),
MC_PU32("output_format_mode", &output_format_mode),
};
static struct mconfig_node jpeg_node;
static int __init picdec_init_module(void)
{
int err;
amlog_level(LOG_LEVEL_HIGH, "picdec_init\n");
err = platform_driver_register(&picdec_drv);
if (err) {
pr_info("Failed to register picdec driver error=%d\n",
err);
return err;
}
INIT_REG_NODE_CONFIGS("media.decoder", &jpeg_node,
"jpeg", jpeg_configs, CONFIG_FOR_RW);
return err;
}
static void __exit picdec_remove_module(void)
{
platform_driver_unregister(&picdec_drv);
amlog_level(LOG_LEVEL_HIGH, "picdec module removed.\n");
}
module_init(picdec_init_module);
module_exit(picdec_remove_module);
RESERVEDMEM_OF_DECLARE(picdec, "amlogic, picdec_memory", picdec_mem_setup);
MODULE_PARM_DESC(debug_flag, "\n debug_flag\n");
module_param(debug_flag, uint, 0664);
module_param(dump_file_flag, uint, 0664);
MODULE_PARM_DESC(dump_file_flag, "\n picdec dump file flag\n");
module_param(p2p_mode, uint, 0664);
MODULE_PARM_DESC(p2p_mode, "\n picdec zoom mode\n");
module_param(output_format_mode, uint, 0664);
MODULE_PARM_DESC(output_format_mode, "\n picdec output fomat mode\n");
module_param(txlx_output_format_mode, uint, 0664);
MODULE_PARM_DESC(txlx_output_format_mode, "\n txlx picdec output fomat mode\n");
module_param(cma_layout_flag, uint, 0664);
MODULE_PARM_DESC(cma_layout_flag, "\n cma layout change flag\n");
MODULE_DESCRIPTION("Amlogic picture decoder driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Simon Zheng <simon.zheng@amlogic.com>");