| /* |
| * drivers/amlogic/media/camera/common/vm.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/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/media/vfm/vfm_ext.h> |
| #include <linux/amlogic/media/ge2d/ge2d_cmd.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 "vm_log.h" |
| #include "vm.h" |
| #include <linux/ctype.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/utils/amlog.h> |
| #include <linux/amlogic/media/camera/vmapi.h> |
| #include <linux/amlogic/media/frame_provider/tvin/tvin_v4l2.h> |
| #include <linux/ctype.h> |
| #include <linux/of.h> |
| #include <linux/cdev.h> |
| |
| #include <linux/sizes.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/of_fdt.h> |
| #include <linux/dma-contiguous.h> |
| #include <linux/module.h> |
| #include <linux/of_reserved_mem.h> |
| #include <linux/list.h> |
| #include <linux/amlogic/media/canvas/canvas_mgr.h> |
| |
| /*class property info.*/ |
| #include "vmcls.h" |
| |
| /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ |
| #if 1 |
| #define GE2D_NV |
| #endif |
| |
| #if 0 |
| static unsigned int amlvm_time_log_enable; |
| module_param(amlvm_time_log_enable, uint, 0644); |
| MODULE_PARM_DESC(amlvm_time_log_enable, "enable vm time log when get frames"); |
| #endif |
| |
| #define MAX_VF_POOL_SIZE 8 |
| |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| /*same as tvin pool*/ |
| static int VM_POOL_SIZE = 6; |
| static int VF_POOL_SIZE = 6; |
| static int VM_CANVAS_ID = 24; |
| /*same as tvin pool*/ |
| #endif |
| |
| |
| /*the counter of VM*/ |
| #define VM_MAX_DEVS 2 |
| |
| static struct vm_device_s *vm_device[VM_MAX_DEVS]; |
| |
| /* static bool isvmused; */ |
| |
| static void vm_cache_this_flush(unsigned int buf_start, |
| unsigned int buf_size, |
| struct vm_init_s *info); |
| |
| static inline void vm_vf_put_from_provider(struct vframe_s *vf, |
| unsigned int vdin_id); |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| #define INCPTR(p) ptr_atomic_wrap_inc(&p) |
| #endif |
| |
| #ifdef CONFIG_AMLOGIC_CAPTURE_FRAME_ROTATE |
| static int vmdecbuf_size[] = { |
| 0x13B3000,/* 5M */ |
| 0xc00000,/* 3M */ |
| 0x753000,/* 2M */ |
| 0x4b0000,/* 1M3 */ |
| 0x300000,/* 1M */ |
| 0x12c000,/* VGA */ |
| 0x4b000,/* QVGA */ |
| }; |
| static struct v4l2_frmsize_discrete canvas_config_wh[] = { |
| {2624, 2624}, |
| {2048, 2048}, |
| {1600, 1600}, |
| {1280, 1280}, |
| {1024, 1024}, |
| {640, 640}, |
| {320, 320}, |
| }; |
| #else |
| static int vmdecbuf_size[] = { |
| 0xEE5000,/* 5M */ |
| 0x900000,/* 3M */ |
| 0x591000,/* 2M */ |
| 0x384000,/* 1M3 */ |
| 0x240000,/* 1M */ |
| 0xF0000,/* VGA */ |
| 0x3C000,/* QVGA */ |
| }; |
| static struct v4l2_frmsize_discrete canvas_config_wh[] = { |
| {2624, 1984}, |
| {2048, 1536}, |
| {1600, 1216}, |
| {1280, 960}, |
| {1024, 768}, |
| {640, 512}, |
| {320, 256}, |
| }; |
| #endif |
| #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 "vm" |
| |
| static dev_t vm_devno; |
| static struct class *vm_clsp; |
| |
| #define VM_DEV_NAME "vm" |
| #define RECEIVER_NAME "vm" |
| #define VM_CLS_NAME "vm" |
| |
| static DEFINE_SPINLOCK(lock); |
| |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| static inline void ptr_atomic_wrap_inc(u32 *ptr) |
| { |
| u32 i = *ptr; |
| |
| i++; |
| if (i >= VM_POOL_SIZE) |
| i = 0; |
| *ptr = i; |
| } |
| #endif |
| |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| 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; |
| #endif |
| |
| atomic_t waiting_flag = ATOMIC_INIT(0); |
| |
| static inline struct vframe_s *vm_vf_get_from_provider(unsigned int vdin_id); |
| static inline struct vframe_s *vm_vf_peek_from_provider(unsigned int vdin_id); |
| static inline void vm_vf_put_from_provider(struct vframe_s *vf, |
| unsigned int vdin_id); |
| static struct vframe_receiver_op_s *vf_vm_unreg_provider( |
| struct vm_device_s *vdevp); |
| static struct vframe_receiver_op_s *vf_vm_reg_provider(struct vm_device_s |
| *vdevp); |
| static void stop_vm_task(struct vm_device_s *vdevp); |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| static int prepare_vframe(struct vframe_s *vf); |
| #endif |
| |
| #ifndef ARRAY_SIZE |
| #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) |
| #endif |
| |
| /* |
| *********************************************** |
| * |
| * buffer op for video sink. |
| * |
| *********************************************** |
| */ |
| |
| #ifdef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| static struct vframe_s *local_vf_peek(unsigned int vdin_id) |
| { |
| struct vframe_s *vf = NULL; |
| |
| vf = vm_vf_peek_from_provider(vdin_id); |
| if (vf) { |
| if (vm_device[vdin_id]->vm_skip_count > 0) { |
| vm_device[vdin_id]->vm_skip_count--; |
| vm_vf_get_from_provider(vdin_id); |
| vm_vf_put_from_provider(vf, vdin_id); |
| vf = NULL; |
| } |
| } |
| return vf; |
| } |
| |
| static struct vframe_s *local_vf_get(unsigned int vdin_id) |
| { |
| return vm_vf_get_from_provider(vdin_id); |
| } |
| |
| static void local_vf_put(struct vframe_s *vf, unsigned int vdin_id) |
| { |
| if (vf) |
| vm_vf_put_from_provider(vf, vdin_id); |
| } |
| #else |
| |
| static inline u32 index2canvas(u32 index) |
| { |
| int i; |
| int start_canvas, count; |
| u32 canvas_tab[6]; |
| struct vdin_v4l2_ops_s *vops = get_vdin_v4l2_ops(); |
| |
| vops->get_tvin_canvas_info(&start_canvas, &count); |
| VM_POOL_SIZE = count; |
| VF_POOL_SIZE = count; |
| VM_CANVAS_ID = start_canvas; |
| for (i = 0; i < count; i++) |
| canvas_tab[i] = VM_CANVAS_INDEX + i; |
| return canvas_tab[index]; |
| } |
| |
| static struct vframe_s *vm_vf_peek(void *op_arg, unsigned int vdin_id) |
| { |
| struct vframe_s *vf = NULL; |
| |
| vf = vm_vf_peek_from_provider(vdin_id); |
| if (vf) { |
| if (vm_device[vdin_id]->vm_skip_count > 0) { |
| vm_device[vdin_id]->vm_skip_count--; |
| vm_vf_get_from_provider(vdin_id); |
| vm_vf_put_from_provider(vf, vdin_id); |
| vf = NULL; |
| } |
| } |
| return vf; |
| } |
| |
| static struct vframe_s *vm_vf_get(void *op_arg, unsigned int vdin_id) |
| { |
| return vm_vf_get_from_provider(vdin_id); |
| } |
| |
| static void vm_vf_put(struct vframe_s *vf, void *op_arg) |
| { |
| prepare_vframe(vf); |
| } |
| |
| static int vm_vf_states(struct vframe_states *states, void *op_arg) |
| { |
| return 0; |
| } |
| |
| static struct vframe_s *local_vf_peek(void) |
| { |
| if (get_ptr == fill_ptr) |
| return NULL; |
| return &vfpool[get_ptr]; |
| } |
| |
| static struct vframe_s *local_vf_get(unsigned int vdin_id) |
| { |
| struct vframe_s *vf; |
| |
| if (get_ptr == fill_ptr) |
| return NULL; |
| vf = &vfpool[get_ptr]; |
| INCPTR(get_ptr); |
| return vf; |
| } |
| |
| static void local_vf_put(struct vframe_s *vf, unsigned int vdin_id) |
| { |
| 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; |
| vm_vf_put_from_provider(vf, vdin_id); |
| } |
| } |
| } |
| #endif |
| |
| #if 0 |
| static int local_vf_states(struct vframe_states *states) |
| { |
| unsigned long flags; |
| int i; |
| |
| spin_lock_irqsave(&lock, flags); |
| states->vf_pool_size = VF_POOL_SIZE; |
| |
| i = put_ptr - fill_ptr; |
| if (i < 0) |
| i += VF_POOL_SIZE; |
| states->buf_free_num = i; |
| |
| i = putting_ptr - put_ptr; |
| if (i < 0) |
| i += VF_POOL_SIZE; |
| states->buf_recycle_num = i; |
| |
| i = fill_ptr - get_ptr; |
| if (i < 0) |
| i += VF_POOL_SIZE; |
| states->buf_avail_num = i; |
| |
| spin_unlock_irqrestore(&lock, flags); |
| return 0; |
| } |
| #endif |
| |
| static int vm_receiver_event_fun(int type, void *data, void *private_data) |
| { |
| struct vm_device_s *vdevp = (struct vm_device_s *)private_data; |
| |
| switch (type) { |
| case VFRAME_EVENT_PROVIDER_VFRAME_READY: |
| /* if (atomic_read(&waiting_flag)) { */ |
| wake_up_interruptible(&vdevp->frame_ready); |
| /* atomic_set(&waiting_flag, 0); */ |
| /* } */ |
| /* up(&vb_start_sema); */ |
| /* printk("vdin %d frame ready !!!!!\n", vdevp->index); */ |
| break; |
| case VFRAME_EVENT_PROVIDER_START: |
| /* printk("vm register!!!!!\n"); */ |
| vf_vm_reg_provider(vdevp); |
| vdevp->vm_skip_count = 0; |
| vdevp->test_zoom = 0; |
| break; |
| case VFRAME_EVENT_PROVIDER_UNREG: |
| /* printk("vm unregister!!!!!\n"); */ |
| vm_local_init(); |
| vf_vm_unreg_provider(vdevp); |
| /* printk("vm unregister succeed!!!!!\n"); */ |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| static struct vframe_receiver_op_s vm_vf_receiver = { |
| .event_cb = vm_receiver_event_fun |
| }; |
| |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| static const struct vframe_operations_s vm_vf_provider = { |
| .peek = vm_vf_peek, |
| .get = vm_vf_get, |
| .put = vm_vf_put, |
| .vf_states = vm_vf_states, |
| }; |
| |
| static struct vframe_provider_s vm_vf_prov; |
| #endif |
| |
| |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| int get_unused_vm_index(void) |
| { |
| int i; |
| |
| for (i = 0; i < VF_POOL_SIZE; i++) { |
| if (vfbuf_use[i] == 0) |
| return i; |
| } |
| return -1; |
| } |
| static int prepare_vframe(struct vframe_s *vf) |
| { |
| struct vframe_s *new_vf; |
| int index; |
| |
| index = get_unused_vm_index(); |
| if (index < 0) |
| return -1; |
| new_vf = &vfpool[fill_ptr]; |
| memcpy(new_vf, vf, sizeof(struct vframe_s)); |
| vfbuf_use[index]++; |
| INCPTR(fill_ptr); |
| return 0; |
| } |
| #endif |
| |
| /* |
| ************************************************ |
| * |
| * buffer op for decoder, camera, etc. |
| * |
| ************************************************** |
| */ |
| |
| /* static const vframe_provider_t *vfp = NULL; */ |
| |
| void vm_local_init(void) |
| { |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| int i; |
| |
| for (i = 0; i < MAX_VF_POOL_SIZE; i++) |
| vfbuf_use[i] = 0; |
| fill_ptr = get_ptr = putting_ptr = put_ptr = 0; |
| #endif |
| } |
| |
| static struct vframe_receiver_op_s *vf_vm_unreg_provider( |
| struct vm_device_s *vdevp) |
| { |
| /* ulong flags; */ |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| vf_unreg_provider(&vm_vf_prov); |
| #endif |
| stop_vm_task(vdevp); |
| /* spin_lock_irqsave(&lock, flags); */ |
| /* vfp = NULL; */ |
| /* spin_unlock_irqrestore(&lock, flags); */ |
| return (struct vframe_receiver_op_s *)NULL; |
| } |
| EXPORT_SYMBOL(vf_vm_unreg_provider); |
| |
| static struct vframe_receiver_op_s *vf_vm_reg_provider(struct vm_device_s |
| *vdevp) |
| { |
| ulong flags; |
| /* int ret; */ |
| spin_lock_irqsave(&lock, flags); |
| spin_unlock_irqrestore(&lock, flags); |
| vm_buffer_init(vdevp); |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| vf_reg_provider(&vm_vf_prov); |
| #endif |
| start_vm_task(vdevp); |
| #if 0 |
| start_simulate_task(); |
| #endif |
| return &vm_vf_receiver; |
| } |
| EXPORT_SYMBOL(vf_vm_reg_provider); |
| |
| static inline struct vframe_s *vm_vf_peek_from_provider(unsigned int vdin_id) |
| { |
| struct vframe_provider_s *vfp; |
| struct vframe_s *vf; |
| char name[20]; |
| |
| sprintf(name, "%s%d", RECEIVER_NAME, vdin_id); |
| vfp = vf_get_provider(name); |
| if (!(vfp && vfp->ops && vfp->ops->peek)) |
| return NULL; |
| vf = vfp->ops->peek(vfp->op_arg); |
| return vf; |
| } |
| |
| static inline struct vframe_s *vm_vf_get_from_provider(unsigned int vdin_id) |
| { |
| struct vframe_provider_s *vfp; |
| char name[20]; |
| |
| sprintf(name, "%s%d", RECEIVER_NAME, vdin_id); |
| vfp = vf_get_provider(name); |
| if (!(vfp && vfp->ops && vfp->ops->peek)) |
| return NULL; |
| return vfp->ops->get(vfp->op_arg); |
| } |
| |
| static inline void vm_vf_put_from_provider(struct vframe_s *vf, |
| unsigned int vdin_id) |
| { |
| struct vframe_provider_s *vfp; |
| char name[20]; |
| |
| sprintf(name, "%s%d", RECEIVER_NAME, vdin_id); |
| vfp = vf_get_provider(name); |
| if (!(vfp && vfp->ops && vfp->ops->peek)) |
| return; |
| vfp->ops->put(vf, vfp->op_arg); |
| } |
| |
| /* |
| *********************************************** |
| * |
| * main task functions. |
| * |
| *********************************************** |
| */ |
| |
| static unsigned int print_ifmt; |
| /* module_param(print_ifmt, unsigned int, 0644); */ |
| /* MODULE_PARM_DESC(print_ifmt, "print input format\n"); */ |
| |
| static int get_input_format(struct vframe_s *vf) |
| { |
| int format = GE2D_FORMAT_M24_NV21; |
| |
| if (vf->type & VIDTYPE_VIU_422) { |
| if (vf->type & VIDTYPE_INTERLACE_BOTTOM) |
| format = GE2D_FORMAT_S16_YUV422 | |
| (GE2D_FORMAT_S16_YUV422B & (3 << 3)); |
| else if (vf->type & VIDTYPE_INTERLACE_TOP) |
| format = GE2D_FORMAT_S16_YUV422 | |
| (GE2D_FORMAT_S16_YUV422T & (3 << 3)); |
| else |
| format = GE2D_FORMAT_S16_YUV422; |
| } else if (vf->type & VIDTYPE_VIU_NV21) { |
| if (vf->type & VIDTYPE_INTERLACE_BOTTOM) |
| format = GE2D_FORMAT_M24_NV21 | |
| (GE2D_FORMAT_M24_NV21B & (3 << 3)); |
| else if (vf->type & VIDTYPE_INTERLACE_TOP) |
| format = GE2D_FORMAT_M24_NV21 | |
| (GE2D_FORMAT_M24_NV21T & (3 << 3)); |
| else |
| format = GE2D_FORMAT_M24_NV21; |
| } else { |
| if (vf->type & VIDTYPE_INTERLACE_BOTTOM) |
| format = GE2D_FORMAT_M24_YUV420 | |
| (GE2D_FMT_M24_YUV420B & (3 << 3)); |
| else if (vf->type & VIDTYPE_INTERLACE_TOP) |
| format = GE2D_FORMAT_M24_YUV420 | |
| (GE2D_FORMAT_M24_YUV420T & (3 << 3)); |
| else |
| format = GE2D_FORMAT_M24_YUV420; |
| } |
| if (print_ifmt == 1) { |
| pr_debug("VIDTYPE_VIU_NV21=%x, vf->type=%x\n", |
| VIDTYPE_VIU_NV21, vf->type); |
| pr_debug("format=%x, w=%d, h=%d\n", |
| format, vf->width, vf->height); |
| print_ifmt = 0; |
| } |
| return format; |
| } |
| |
| #ifdef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| static int calc_zoom(int *top, int *left, int *bottom, int *right, int zoom) |
| { |
| u32 screen_width, screen_height; |
| s32 start, end; |
| s32 video_top, video_left, temp; |
| u32 video_width, video_height; |
| u32 ratio_x = 0; |
| u32 ratio_y = 0; |
| |
| if (zoom < 100) |
| zoom = 100; |
| |
| video_top = *top; |
| video_left = *left; |
| video_width = *right - *left + 1; |
| video_height = *bottom - *top + 1; |
| |
| screen_width = video_width * zoom / 100; |
| screen_height = video_height * zoom / 100; |
| |
| ratio_x = (video_width << 18) / screen_width; |
| if (ratio_x * screen_width < (video_width << 18)) |
| ratio_x++; |
| ratio_y = (video_height << 18) / screen_height; |
| |
| /* vertical */ |
| start = video_top + video_height / 2 - (video_height << 17) / ratio_y; |
| end = (video_height << 18) / ratio_y + start - 1; |
| |
| if (start < video_top) { |
| temp = ((video_top - start) * ratio_y) >> 18; |
| *top = temp; |
| } else |
| *top = 0; |
| |
| temp = *top + (video_height * ratio_y >> 18); |
| *bottom = (temp <= (video_height - 1)) ? temp : (video_height - 1); |
| |
| /* horizontal */ |
| start = video_left + video_width / 2 - (video_width << 17) / ratio_x; |
| end = (video_width << 18) / ratio_x + start - 1; |
| if (start < video_left) { |
| temp = ((video_left - start) * ratio_x) >> 18; |
| *left = temp; |
| } else |
| *left = 0; |
| |
| temp = *left + (video_width * ratio_x >> 18); |
| *right = (temp <= (video_width - 1)) ? temp : (video_width - 1); |
| return 0; |
| } |
| #endif |
| |
| static int get_input_frame(struct display_frame_s *frame, struct vframe_s *vf, |
| int zoom) |
| { |
| int ret = 0; |
| int top, left, bottom, right; |
| |
| if (!vf) |
| return -1; |
| |
| frame->frame_top = 0; |
| frame->frame_left = 0; |
| frame->frame_width = vf->width; |
| frame->frame_height = vf->height; |
| top = 0; |
| left = 0; |
| bottom = vf->height - 1; |
| right = vf->width - 1; |
| #ifdef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| ret = calc_zoom(&top, &left, &bottom, &right, zoom); |
| #else |
| ret = get_curren_frame_para(&top, &left, &bottom, &right); |
| #endif |
| if (ret >= 0) { |
| frame->content_top = top & (~1); |
| frame->content_left = left & (~1); |
| frame->content_width = vf->width - 2 * frame->content_left; |
| frame->content_height = vf->height - 2 * frame->content_top; |
| } else { |
| frame->content_top = 0; |
| frame->content_left = 0; |
| frame->content_width = vf->width; |
| frame->content_height = vf->height; |
| } |
| return 0; |
| } |
| |
| static int get_output_format(int v4l2_format) |
| { |
| int format = GE2D_FORMAT_S24_YUV444; |
| |
| switch (v4l2_format) { |
| case V4L2_PIX_FMT_RGB565X: |
| format = GE2D_FORMAT_S16_RGB_565; |
| break; |
| case V4L2_PIX_FMT_YUV444: |
| format = GE2D_FORMAT_S24_YUV444; |
| break; |
| case V4L2_PIX_FMT_VYUY: |
| format = GE2D_FORMAT_S16_YUV422; |
| break; |
| case V4L2_PIX_FMT_BGR24: |
| format = GE2D_FORMAT_S24_RGB; |
| break; |
| case V4L2_PIX_FMT_RGB24: |
| format = GE2D_FORMAT_S24_BGR; |
| break; |
| case V4L2_PIX_FMT_NV12: |
| #ifdef GE2D_NV |
| format = GE2D_FORMAT_M24_NV12; |
| break; |
| #endif |
| case V4L2_PIX_FMT_NV21: |
| #ifdef GE2D_NV |
| format = GE2D_FORMAT_M24_NV21; |
| break; |
| #endif |
| case V4L2_PIX_FMT_YUV420: |
| case V4L2_PIX_FMT_YVU420: |
| format = GE2D_FORMAT_S8_Y; |
| break; |
| default: |
| break; |
| } |
| return format; |
| } |
| |
| |
| struct vm_dma_contig_memory { |
| u32 magic; |
| void *vaddr; |
| dma_addr_t dma_handle; |
| unsigned long size; |
| int is_userptr; |
| }; |
| |
| int is_need_ge2d_pre_process(struct vm_output_para output_para) |
| { |
| int ret = 0; |
| |
| switch (output_para.v4l2_format) { |
| case V4L2_PIX_FMT_RGB565X: |
| case V4L2_PIX_FMT_YUV444: |
| case V4L2_PIX_FMT_VYUY: |
| case V4L2_PIX_FMT_BGR24: |
| case V4L2_PIX_FMT_RGB24: |
| case V4L2_PIX_FMT_YUV420: |
| case V4L2_PIX_FMT_YVU420: |
| case V4L2_PIX_FMT_NV12: |
| case V4L2_PIX_FMT_NV21: |
| ret = 1; |
| break; |
| default: |
| break; |
| } |
| return ret; |
| } |
| |
| int is_need_sw_post_process(struct vm_output_para output_para) |
| { |
| int ret = 0; |
| |
| switch (output_para.v4l2_memory) { |
| case MAGIC_DC_MEM: |
| case MAGIC_RE_MEM: |
| goto exit; |
| case MAGIC_SG_MEM: |
| case MAGIC_VMAL_MEM: |
| default: |
| ret = 1; |
| break; |
| } |
| exit: |
| return ret; |
| } |
| |
| int get_canvas_index(int v4l2_format, int *depth) |
| { |
| int canvas = vm_device[0]->vm_canvas[0]; |
| *depth = 16; |
| switch (v4l2_format) { |
| case V4L2_PIX_FMT_RGB565X: |
| case V4L2_PIX_FMT_VYUY: |
| canvas = vm_device[0]->vm_canvas[0]; |
| *depth = 16; |
| break; |
| case V4L2_PIX_FMT_YUV444: |
| case V4L2_PIX_FMT_BGR24: |
| case V4L2_PIX_FMT_RGB24: |
| canvas = vm_device[0]->vm_canvas[1]; |
| *depth = 24; |
| break; |
| case V4L2_PIX_FMT_NV12: |
| case V4L2_PIX_FMT_NV21: |
| #ifdef GE2D_NV |
| canvas = vm_device[0]->vm_canvas[2] | |
| (vm_device[0]->vm_canvas[3] << 8); |
| #else |
| canvas = vm_device[0]->vm_canvas[2] | |
| (vm_device[0]->vm_canvas[3] << 8) | |
| (vm_device[0]->vm_canvas[4] << 16); |
| #endif |
| *depth = 12; |
| break; |
| case V4L2_PIX_FMT_YUV420: |
| case V4L2_PIX_FMT_YVU420: |
| canvas = vm_device[0]->vm_canvas[5] | |
| (vm_device[0]->vm_canvas[6] << 8) | |
| (vm_device[0]->vm_canvas[7] << 16); |
| *depth = 12; |
| break; |
| default: |
| break; |
| } |
| return canvas; |
| } |
| |
| int get_canvas_index_res(int ext_canvas, int v4l2_format, int *depth, int width, |
| int height, unsigned int buf) |
| { |
| int canvas = ext_canvas; |
| *depth = 16; |
| switch (v4l2_format) { |
| case V4L2_PIX_FMT_RGB565X: |
| case V4L2_PIX_FMT_VYUY: |
| canvas = ext_canvas & 0xff; |
| *depth = 16; |
| canvas_config(canvas, |
| (unsigned long)buf, |
| width * 2, height, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| break; |
| case V4L2_PIX_FMT_YUV444: |
| case V4L2_PIX_FMT_BGR24: |
| case V4L2_PIX_FMT_RGB24: |
| canvas = ext_canvas & 0xff; |
| *depth = 24; |
| canvas_config(canvas, |
| (unsigned long)buf, |
| width * 3, height, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| break; |
| case V4L2_PIX_FMT_NV12: |
| case V4L2_PIX_FMT_NV21: |
| canvas_config(ext_canvas & 0xff, |
| (unsigned long)buf, |
| width, height, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| canvas_config((ext_canvas & 0xff00) >> 8, |
| (unsigned long)(buf + width * height), |
| width, height / 2, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| canvas = ext_canvas & 0xffff; |
| *depth = 12; |
| break; |
| case V4L2_PIX_FMT_YVU420: |
| case V4L2_PIX_FMT_YUV420: |
| canvas_config(ext_canvas & 0xff, |
| (unsigned long)buf, |
| width, height, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| canvas_config((ext_canvas & 0xff00) >> 8, |
| (unsigned long)(buf + width * height), |
| width / 2, height / 2, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| canvas_config((ext_canvas & 0xff0000) >> 16, |
| (unsigned long)(buf + width * height * 5 / 4), |
| width / 2, height / 2, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| canvas = ext_canvas & 0xffffff; |
| *depth = 12; |
| break; |
| default: |
| break; |
| } |
| return canvas; |
| } |
| |
| #if 0 |
| static void vm_dump_mem(char *path, void *phy_addr, struct vm_output_para *para) |
| { |
| struct file *filp = NULL; |
| loff_t pos = 0; |
| void *buf = NULL; |
| unsigned int size = para->bytesperline * para->height; |
| mm_segment_t old_fs = get_fs(); |
| |
| set_fs(KERNEL_DS); |
| filp = filp_open(path, O_RDWR | O_CREAT, 0666); |
| |
| if (IS_ERR(filp)) { |
| pr_err("create %s error.\n", path); |
| return; |
| } |
| |
| |
| buf = phys_to_virt((unsigned long)phy_addr); |
| vfs_write(filp, buf, size, &pos); |
| |
| vfs_fsync(filp, 0); |
| filp_close(filp, NULL); |
| set_fs(old_fs); |
| } |
| |
| static void vm_x_mem(char *path, struct vm_output_para *para) |
| { |
| struct file *filp = NULL; |
| loff_t pos = 0; |
| void *buf = NULL; |
| unsigned int size = para->bytesperline * para->height; |
| unsigned int canvas_index = para->index; |
| struct canvas_s cv; |
| mm_segment_t old_fs = get_fs(); |
| |
| set_fs(KERNEL_DS); |
| filp = filp_open(path, O_CREAT | O_RDWR | O_APPEND, 0666); |
| |
| if (IS_ERR(filp)) { |
| pr_err("failed to create %s, error %p.\n", |
| path, filp); |
| return; |
| } |
| |
| for (; canvas_index != 0; canvas_index >>= 8) { |
| canvas_read(canvas_index & 0xff, &cv); |
| /* printk("index=%lx,canvas.addr=%lx, w=%d, h=%d\n", */ |
| /* canvas_index, cv.addr, cv.width, cv.height); */ |
| |
| buf = phys_to_virt(cv.addr); |
| |
| size = cv.width * cv.height; |
| |
| vfs_write(filp, buf, size, &pos); |
| vfs_fsync(filp, 0); |
| } |
| filp_close(filp, NULL); |
| set_fs(old_fs); |
| } |
| #endif |
| |
| int vm_fill_this_buffer(struct videobuf_buffer *vb, |
| struct vm_output_para *para, struct vm_init_s *info) |
| |
| { |
| int depth = 0; |
| int ret = 0; |
| int canvas_index = -1; |
| int v4l2_format = V4L2_PIX_FMT_YUV444; |
| int magic = 0; |
| int ext_canvas; |
| struct videobuf_buffer buf = {0}; |
| |
| if (!info) |
| return -1; |
| if (info->vdin_id >= VM_MAX_DEVS) { |
| pr_err("beyond the device array bound .\n"); |
| return -1; |
| } |
| /* if (info->isused == false) */ |
| /* return -2; */ |
| #if 0 |
| if (!vb) |
| goto exit; |
| #else |
| if (!vb) { |
| buf.width = 640; |
| buf.height = 480; |
| v4l2_format = V4L2_PIX_FMT_YUV444; |
| vb = &buf; |
| } |
| |
| if (!vm_device[info->vdin_id]->task_running) |
| return -1; |
| #endif |
| |
| v4l2_format = para->v4l2_format; |
| magic = para->v4l2_memory; |
| switch (magic) { |
| case MAGIC_DC_MEM: |
| /* mem = vb->priv; */ |
| canvas_config(vm_device[0]->vm_canvas[11], |
| (dma_addr_t)para->vaddr, |
| vb->bytesperline, vb->height, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| canvas_index = vm_device[0]->vm_canvas[11]; |
| depth = (vb->bytesperline << 3) / vb->width; |
| break; |
| case MAGIC_RE_MEM: |
| if (para->ext_canvas != 0) |
| canvas_index = get_canvas_index_res( |
| para->ext_canvas, v4l2_format, |
| &depth, vb->width, |
| (para->height == 0) ? vb->height : para->height, |
| (unsigned int)para->vaddr); |
| else if (info->vdin_id == 0) { |
| ext_canvas = ((vm_device[0]->vm_canvas[3]) | |
| (vm_device[0]->vm_canvas[4] << 8) | |
| (vm_device[0]->vm_canvas[5] << 16)); |
| canvas_index = |
| get_canvas_index_res(ext_canvas, |
| v4l2_format, &depth, vb->width, |
| vb->height, (unsigned int)para->vaddr); |
| } else { |
| ext_canvas = ((vm_device[1]->vm_canvas[0]) | |
| (vm_device[1]->vm_canvas[1] << 8) | |
| (vm_device[1]->vm_canvas[2] << 16)); |
| canvas_index = |
| get_canvas_index_res(ext_canvas, |
| v4l2_format, &depth, vb->width, |
| vb->height, (unsigned int)para->vaddr); |
| } |
| break; |
| case MAGIC_SG_MEM: |
| case MAGIC_VMAL_MEM: |
| if (vm_device[info->vdin_id]->buffer_start && |
| vm_device[info->vdin_id]->buffer_size) |
| canvas_index = get_canvas_index(v4l2_format, &depth); |
| break; |
| default: |
| canvas_index = vm_device[0]->vm_canvas[0]; |
| break; |
| } |
| vm_device[info->vdin_id]->output_para.width = vb->width; |
| vm_device[info->vdin_id]->output_para.height = vb->height; |
| vm_device[info->vdin_id]->output_para.bytesperline = |
| (vb->width * depth) >> 3; |
| vm_device[info->vdin_id]->output_para.index = canvas_index; |
| vm_device[info->vdin_id]->output_para.v4l2_format = v4l2_format; |
| vm_device[info->vdin_id]->output_para.v4l2_memory = magic; |
| vm_device[info->vdin_id]->output_para.mirror = para->mirror; |
| vm_device[info->vdin_id]->output_para.zoom = para->zoom; |
| vm_device[info->vdin_id]->output_para.angle = para->angle; |
| vm_device[info->vdin_id]->output_para.vaddr = para->vaddr; |
| vm_device[info->vdin_id]->output_para.ext_canvas = |
| (magic == MAGIC_RE_MEM) ? para->ext_canvas : 0; |
| complete(&vm_device[info->vdin_id]->vb_start_sema); |
| wait_for_completion(&vm_device[info->vdin_id]->vb_done_sema); |
| if (magic == MAGIC_RE_MEM) |
| vm_cache_this_flush((unsigned int)para->vaddr, |
| para->bytesperline * |
| para->height, info); |
| |
| return ret; |
| } |
| |
| /* |
| *for decoder input processing |
| * 1. output window should 1:1 as source frame size |
| * 2. keep the frame ratio |
| * 3. input format should be YUV420 , output format should be YUV444 |
| */ |
| |
| int vm_ge2d_pre_process(struct vframe_s *vf, |
| struct ge2d_context_s *context, |
| struct config_para_ex_s *ge2d_config, |
| struct vm_output_para output_para, |
| unsigned int index) |
| { |
| int ret; |
| int src_top, src_left, src_width, src_height; |
| struct canvas_s cs0, cs1, cs2, cd; |
| int current_mirror = 0; |
| int cur_angle = 0; |
| struct display_frame_s input_frame = {0}; |
| |
| ret = get_input_frame(&input_frame, vf, output_para.zoom); |
| src_top = input_frame.content_top; |
| src_left = input_frame.content_left; |
| src_width = input_frame.content_width; |
| src_height = input_frame.content_height; |
| if (vm_device[index]->test_zoom) { |
| vm_device[index]->test_zoom = 0; |
| pr_debug("top is %d , left is %d\n", |
| input_frame.content_top, |
| input_frame.content_left); |
| pr_debug("width is %d , height is %d\n", |
| input_frame.content_width, |
| input_frame.content_height); |
| } |
| #ifdef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| current_mirror = output_para.mirror; |
| if (current_mirror < 0) |
| current_mirror = 0; |
| #else |
| current_mirror = camera_mirror_flag; |
| #endif |
| |
| cur_angle = output_para.angle; |
| if (current_mirror == 1) |
| cur_angle = (360 - cur_angle % 360); |
| else |
| cur_angle = cur_angle % 360; |
| |
| /* data operating. */ |
| 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(vf->canvas0Addr & 0xff, &cs0); |
| canvas_read((vf->canvas0Addr >> 8) & 0xff, &cs1); |
| canvas_read((vf->canvas0Addr >> 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; |
| canvas_read(output_para.index & 0xff, &cd); |
| 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 = get_input_format(vf); |
| 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 = vf->width; |
| ge2d_config->src_para.height = vf->height; |
| ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID; |
| ge2d_config->dst_para.canvas_index = output_para.index & 0xff; |
| #ifdef GE2D_NV |
| if ((output_para.v4l2_format != V4L2_PIX_FMT_YUV420) |
| && (output_para.v4l2_format != V4L2_PIX_FMT_YVU420)) |
| ge2d_config->dst_para.canvas_index = output_para.index; |
| #endif |
| ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID; |
| ge2d_config->dst_para.format = |
| get_output_format(output_para.v4l2_format) | |
| GE2D_LITTLE_ENDIAN; |
| 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 = output_para.width; |
| ge2d_config->dst_para.height = output_para.height; |
| |
| if (current_mirror == 1) { |
| ge2d_config->dst_para.x_rev = 1; |
| ge2d_config->dst_para.y_rev = 0; |
| } else if (current_mirror == 2) { |
| ge2d_config->dst_para.x_rev = 0; |
| ge2d_config->dst_para.y_rev = 1; |
| } else { |
| ge2d_config->dst_para.x_rev = 0; |
| ge2d_config->dst_para.y_rev = 0; |
| } |
| |
| if (cur_angle == 90) { |
| ge2d_config->dst_xy_swap = 1; |
| ge2d_config->dst_para.x_rev ^= 1; |
| } else if (cur_angle == 180) { |
| ge2d_config->dst_para.x_rev ^= 1; |
| ge2d_config->dst_para.y_rev ^= 1; |
| } else if (cur_angle == 270) { |
| ge2d_config->dst_xy_swap = 1; |
| ge2d_config->dst_para.y_rev ^= 1; |
| } |
| |
| if (ge2d_context_config_ex(context, ge2d_config) < 0) { |
| pr_err("++ge2d configing error.\n"); |
| return -1; |
| } |
| stretchblt_noalpha(context, src_left, src_top, |
| src_width, src_height, 0, 0, |
| output_para.width, output_para.height); |
| |
| /* for cr of yuv420p or yuv420sp. */ |
| if ((output_para.v4l2_format == V4L2_PIX_FMT_YUV420) |
| || (output_para.v4l2_format == V4L2_PIX_FMT_YVU420)) { |
| /* for cb. */ |
| canvas_read((output_para.index >> 8) & 0xff, &cd); |
| 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->dst_para.canvas_index = |
| (output_para.index >> 8) & 0xff; |
| #ifndef GE2D_NV |
| ge2d_config->dst_para.format = |
| GE2D_FORMAT_S8_CB | GE2D_LITTLE_ENDIAN; |
| #else |
| ge2d_config->dst_para.format = |
| GE2D_FORMAT_S8_CR | GE2D_LITTLE_ENDIAN; |
| #endif |
| ge2d_config->dst_para.width = output_para.width / 2; |
| ge2d_config->dst_para.height = output_para.height / 2; |
| ge2d_config->dst_xy_swap = 0; |
| |
| if (current_mirror == 1) { |
| ge2d_config->dst_para.x_rev = 1; |
| ge2d_config->dst_para.y_rev = 0; |
| } else if (current_mirror == 2) { |
| ge2d_config->dst_para.x_rev = 0; |
| ge2d_config->dst_para.y_rev = 1; |
| } else { |
| ge2d_config->dst_para.x_rev = 0; |
| ge2d_config->dst_para.y_rev = 0; |
| } |
| |
| if (cur_angle == 90) { |
| ge2d_config->dst_xy_swap = 1; |
| ge2d_config->dst_para.x_rev ^= 1; |
| } else if (cur_angle == 180) { |
| ge2d_config->dst_para.x_rev ^= 1; |
| ge2d_config->dst_para.y_rev ^= 1; |
| } else if (cur_angle == 270) { |
| ge2d_config->dst_xy_swap = 1; |
| ge2d_config->dst_para.y_rev ^= 1; |
| } |
| |
| if (ge2d_context_config_ex(context, ge2d_config) < 0) { |
| pr_err("++ge2d configing error.\n"); |
| return -1; |
| } |
| stretchblt_noalpha(context, src_left, src_top, |
| src_width, src_height, |
| 0, 0, ge2d_config->dst_para.width, |
| ge2d_config->dst_para.height); |
| } |
| #ifndef GE2D_NV |
| else if (output_para.v4l2_format == V4L2_PIX_FMT_NV12 || |
| output_para.v4l2_format == V4L2_PIX_FMT_NV21) { |
| canvas_read((output_para.index >> 8) & 0xff, &cd); |
| 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->dst_para.canvas_index = |
| (output_para.index >> 8) & 0xff; |
| ge2d_config->dst_para.format = |
| GE2D_FORMAT_S8_CB | GE2D_LITTLE_ENDIAN; |
| ge2d_config->dst_para.width = output_para.width / 2; |
| ge2d_config->dst_para.height = output_para.height / 2; |
| ge2d_config->dst_xy_swap = 0; |
| |
| if (current_mirror == 1) { |
| ge2d_config->dst_para.x_rev = 1; |
| ge2d_config->dst_para.y_rev = 0; |
| } else if (current_mirror == 2) { |
| ge2d_config->dst_para.x_rev = 0; |
| ge2d_config->dst_para.y_rev = 1; |
| } else { |
| ge2d_config->dst_para.x_rev = 0; |
| ge2d_config->dst_para.y_rev = 0; |
| } |
| |
| if (cur_angle == 90) { |
| ge2d_config->dst_xy_swap = 1; |
| ge2d_config->dst_para.x_rev ^= 1; |
| } else if (cur_angle == 180) { |
| ge2d_config->dst_para.x_rev ^= 1; |
| ge2d_config->dst_para.y_rev ^= 1; |
| } else if (cur_angle == 270) { |
| ge2d_config->dst_xy_swap = 1; |
| ge2d_config->dst_para.y_rev ^= 1; |
| } |
| |
| if (ge2d_context_config_ex(context, ge2d_config) < 0) { |
| pr_err("++ge2d configing error.\n"); |
| return -1; |
| } |
| stretchblt_noalpha(context, src_left, src_top, |
| src_width, src_height, 0, 0, |
| ge2d_config->dst_para.width, |
| ge2d_config->dst_para.height); |
| } |
| #endif |
| |
| /* for cb of yuv420p or yuv420sp. */ |
| if (output_para.v4l2_format == V4L2_PIX_FMT_YUV420 || |
| output_para.v4l2_format == V4L2_PIX_FMT_YVU420 |
| #ifndef GE2D_NV |
| || output_para.v4l2_format == V4L2_PIX_FMT_NV12 || |
| output_para.v4l2_format == V4L2_PIX_FMT_NV21 |
| #endif |
| ) { |
| canvas_read((output_para.index >> 16) & 0xff, &cd); |
| 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->dst_para.canvas_index = |
| (output_para.index >> 16) & 0xff; |
| #ifndef GE2D_NV |
| ge2d_config->dst_para.format = |
| GE2D_FORMAT_S8_CR | GE2D_LITTLE_ENDIAN; |
| #else |
| ge2d_config->dst_para.format = |
| GE2D_FORMAT_S8_CB | GE2D_LITTLE_ENDIAN; |
| #endif |
| ge2d_config->dst_para.width = output_para.width / 2; |
| ge2d_config->dst_para.height = output_para.height / 2; |
| ge2d_config->dst_xy_swap = 0; |
| |
| if (current_mirror == 1) { |
| ge2d_config->dst_para.x_rev = 1; |
| ge2d_config->dst_para.y_rev = 0; |
| } else if (current_mirror == 2) { |
| ge2d_config->dst_para.x_rev = 0; |
| ge2d_config->dst_para.y_rev = 1; |
| } else { |
| ge2d_config->dst_para.x_rev = 0; |
| ge2d_config->dst_para.y_rev = 0; |
| } |
| |
| if (cur_angle == 90) { |
| ge2d_config->dst_xy_swap = 1; |
| ge2d_config->dst_para.x_rev ^= 1; |
| } else if (cur_angle == 180) { |
| ge2d_config->dst_para.x_rev ^= 1; |
| ge2d_config->dst_para.y_rev ^= 1; |
| } else if (cur_angle == 270) { |
| ge2d_config->dst_xy_swap = 1; |
| ge2d_config->dst_para.y_rev ^= 1; |
| } |
| |
| if (ge2d_context_config_ex(context, ge2d_config) < 0) { |
| pr_err("++ge2d configing error.\n"); |
| return -1; |
| } |
| stretchblt_noalpha(context, src_left, |
| src_top, src_width, src_height, |
| 0, 0, ge2d_config->dst_para.width, |
| ge2d_config->dst_para.height); |
| } |
| return output_para.index; |
| } |
| |
| int vm_sw_post_process(int canvas, uintptr_t addr, unsigned int index) |
| { |
| int poss = 0, posd = 0; |
| int i = 0; |
| void __iomem *buffer_y_start; |
| void __iomem *buffer_u_start; |
| void __iomem *buffer_v_start = 0; |
| struct io_mapping *mapping_wc; |
| int offset = 0; |
| struct canvas_s canvas_work_y; |
| struct canvas_s canvas_work_u; |
| struct canvas_s canvas_work_v; |
| struct vm_output_para output_para = vm_device[index]->output_para; |
| |
| if (!addr) |
| return -1; |
| mapping_wc = io_mapping_create_wc(vm_device[index]->buffer_start, |
| vm_device[index]->buffer_size); |
| if (!mapping_wc) |
| return -1; |
| canvas_read(canvas & 0xff, &canvas_work_y); |
| offset = 0; |
| buffer_y_start = io_mapping_map_atomic_wc(mapping_wc, offset); |
| if (buffer_y_start == NULL) { |
| pr_err(" vm.postprocess:mapping buffer error\n"); |
| io_mapping_free(mapping_wc); |
| return -1; |
| } |
| if (output_para.v4l2_format == V4L2_PIX_FMT_BGR24 || |
| output_para.v4l2_format == V4L2_PIX_FMT_RGB24 || |
| output_para.v4l2_format == V4L2_PIX_FMT_RGB565X) { |
| for (i = 0; i < output_para.height; i++) { |
| memcpy((void *)(addr + poss), |
| (void *)(buffer_y_start + posd), |
| output_para.bytesperline); |
| poss += output_para.bytesperline; |
| posd += canvas_work_y.width; |
| } |
| io_mapping_unmap_atomic(buffer_y_start); |
| |
| } else if (output_para.v4l2_format == V4L2_PIX_FMT_NV12 || |
| output_para.v4l2_format == V4L2_PIX_FMT_NV21) { |
| #ifdef GE2D_NV |
| unsigned int uv_width = output_para.width; |
| unsigned int uv_height = output_para.height >> 1; |
| |
| posd = 0; |
| for (i = output_para.height; i > 0; i--) { /* copy y */ |
| memcpy((void *)(addr + poss), |
| (void *)(buffer_y_start + posd), |
| output_para.width); |
| poss += output_para.width; |
| posd += canvas_work_y.width; |
| } |
| io_mapping_unmap_atomic(buffer_y_start); |
| |
| posd = 0; |
| canvas_read((canvas >> 8) & 0xff, &canvas_work_u); |
| offset = canvas_work_u.addr - canvas_work_y.addr; |
| buffer_u_start = io_mapping_map_atomic_wc(mapping_wc, offset); |
| for (i = uv_height; i > 0; i--) { /* copy uv */ |
| memcpy((void *)(addr + poss), |
| (void *)(buffer_u_start + posd), uv_width); |
| poss += uv_width; |
| posd += canvas_work_u.width; |
| } |
| |
| io_mapping_unmap_atomic(buffer_u_start); |
| #else |
| char *dst_buff = NULL; |
| char *src_buff = NULL; |
| char *src2_buff = NULL; |
| |
| canvas_read((canvas >> 8) & 0xff, &canvas_work_u); |
| poss = posd = 0; |
| for (i = 0; i < output_para.height; i += 2) { /* copy y */ |
| memcpy((void *)(addr + poss), |
| (void *)(buffer_y_start + posd), |
| output_para.width); |
| poss += output_para.width; |
| posd += canvas_work_y.width; |
| memcpy((void *)(addr + poss), |
| (void *)(buffer_y_start + posd), |
| output_para.width); |
| poss += output_para.width; |
| posd += canvas_work_y.width; |
| } |
| io_mapping_unmap_atomic(buffer_y_start); |
| |
| posd = 0; |
| canvas_read((canvas >> 16) & 0xff, &canvas_work_v); |
| offset = canvas_work_u.addr - canvas_work_y.addr; |
| buffer_u_start = io_mapping_map_atomic_wc(mapping_wc, offset); |
| offset = canvas_work_v.addr - canvas_work_y.addr; |
| buffer_v_start = io_mapping_map_atomic_wc(mapping_wc, offset); |
| |
| dst_buff = (char *)addr + |
| output_para.width * output_para.height; |
| src_buff = (char *)buffer_u_start; |
| src2_buff = (char *)buffer_v_start; |
| if (output_para.v4l2_format == V4L2_PIX_FMT_NV12) { |
| for (i = 0 ; i < output_para.height / 2; i++) { |
| interleave_uv(src_buff, src2_buff, |
| dst_buff, output_para.width / 2); |
| src_buff += canvas_work_u.width; |
| src2_buff += canvas_work_v.width; |
| dst_buff += output_para.width; |
| } |
| } else { |
| for (i = 0 ; i < output_para.height / 2; i++) { |
| interleave_uv(src2_buff, src_buff, |
| dst_buff, output_para.width / 2); |
| src_buff += canvas_work_u.width; |
| src2_buff += canvas_work_v.width; |
| dst_buff += output_para.width; |
| } |
| } |
| |
| |
| io_mapping_unmap_atomic(buffer_u_start); |
| io_mapping_unmap_atomic(buffer_v_start); |
| #endif |
| } else if ((output_para.v4l2_format == V4L2_PIX_FMT_YUV420) |
| || (output_para.v4l2_format == V4L2_PIX_FMT_YVU420)) { |
| int uv_width = output_para.width >> 1; |
| int uv_height = output_para.height >> 1; |
| |
| posd = 0; |
| for (i = output_para.height; i > 0; i--) { /* copy y */ |
| memcpy((void *)(addr + poss), |
| (void *)(buffer_y_start + posd), |
| output_para.width); |
| poss += output_para.width; |
| posd += canvas_work_y.width; |
| } |
| io_mapping_unmap_atomic(buffer_y_start); |
| |
| posd = 0; |
| canvas_read((canvas >> 8) & 0xff, &canvas_work_u); |
| offset = canvas_work_u.addr - canvas_work_y.addr; |
| buffer_u_start = io_mapping_map_atomic_wc(mapping_wc, offset); |
| |
| canvas_read((canvas >> 16) & 0xff, &canvas_work_v); |
| offset = canvas_work_v.addr - canvas_work_y.addr; |
| buffer_v_start = io_mapping_map_atomic_wc(mapping_wc, offset); |
| |
| #ifndef GE2D_NV |
| if (output_para.v4l2_format == V4L2_PIX_FMT_YUV420) |
| #else |
| if (output_para.v4l2_format == V4L2_PIX_FMT_YVU420) { |
| #endif |
| for (i = uv_height; i > 0; i--) { /* copy y */ |
| memcpy((void *)(addr + poss), |
| (void *)(buffer_u_start + posd), |
| uv_width); |
| poss += uv_width; |
| posd += canvas_work_u.width; |
| } |
| posd = 0; |
| for (i = uv_height; i > 0; i--) { /* copy y */ |
| memcpy((void *)(addr + poss), |
| (void *)(buffer_v_start + posd), |
| uv_width); |
| poss += uv_width; |
| posd += canvas_work_v.width; |
| } |
| } else { |
| for (i = uv_height; i > 0; i--) { /* copy v */ |
| memcpy((void *)(addr + poss), |
| (void *)(buffer_v_start + posd), |
| uv_width); |
| poss += uv_width; |
| posd += canvas_work_v.width; |
| } |
| posd = 0; |
| for (i = uv_height; i > 0; i--) { /* copy u */ |
| memcpy((void *)(addr + poss), |
| (void *)(buffer_u_start + posd), |
| uv_width); |
| poss += uv_width; |
| posd += canvas_work_u.width; |
| } |
| } |
| io_mapping_unmap_atomic(buffer_u_start); |
| io_mapping_unmap_atomic(buffer_v_start); |
| } |
| |
| if (mapping_wc) |
| io_mapping_free(mapping_wc); |
| |
| return 0; |
| } |
| |
| static struct task_struct *simulate_task_fd; |
| |
| static bool is_vf_available(unsigned int vdin_id) |
| { |
| bool ret = ((local_vf_peek(vdin_id) != NULL) || |
| (!vm_device[vdin_id]->task_running)); |
| return ret; |
| } |
| |
| /* static int reset_frame = 1; */ |
| static int vm_task(void *data) |
| { |
| int ret = 0; |
| struct vframe_s *vf; |
| int src_canvas; |
| struct vm_device_s *devp = (struct vm_device_s *) data; |
| struct sched_param param = {.sched_priority = MAX_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)); |
| sched_setscheduler(current, SCHED_FIFO, ¶m); |
| allow_signal(SIGTERM); |
| while (1) { |
| ret = wait_for_completion_interruptible( |
| &devp->vb_start_sema); |
| |
| if (kthread_should_stop()) { |
| complete(&devp->vb_done_sema); |
| break; |
| } |
| |
| /* wait for frame from 656 provider until 500ms runs out */ |
| wait_event_interruptible_timeout(devp->frame_ready, |
| is_vf_available(devp->index), |
| msecs_to_jiffies(2000)); |
| |
| if (!devp->task_running) { |
| ret = -1; |
| goto vm_exit; |
| } |
| |
| /* start to convert frame. */ |
| #ifdef CONFIG_AMLCAP_LOG_TIME_USEFORFRAMES |
| do_gettimeofday(&start); |
| #endif |
| vf = local_vf_get(devp->index); |
| |
| if (vf) { |
| src_canvas = vf->canvas0Addr; |
| |
| /* step1 convert 422 format to other format.*/ |
| if (is_need_ge2d_pre_process(devp->output_para)) |
| src_canvas = vm_ge2d_pre_process(vf, context, |
| &ge2d_config, devp->output_para, |
| devp->index); |
| #if 0 |
| if (devp->dump == 2) { |
| vm_dump_mem(devp->dump_path, |
| (void *)output_para.vaddr, &output_para); |
| devp->dump = 0; |
| } |
| #endif |
| local_vf_put(vf, devp->index); |
| #ifdef CONFIG_AMLCAP_LOG_TIME_USEFORFRAMES |
| do_gettimeofday(&end); |
| time_use = (end.tv_sec - start.tv_sec) * 1000 + |
| (end.tv_usec - start.tv_usec) / 1000; |
| pr_debug("step 1, ge2d use: %ldms\n", time_use); |
| do_gettimeofday(&start); |
| #endif |
| |
| /* step2 copy to user memory. */ |
| |
| if (is_need_sw_post_process(devp->output_para)) |
| vm_sw_post_process(src_canvas, |
| devp->output_para.vaddr, devp->index); |
| |
| #ifdef CONFIG_AMLCAP_LOG_TIME_USEFORFRAMES |
| do_gettimeofday(&end); |
| time_use = (end.tv_sec - start.tv_sec) * 1000 + |
| (end.tv_usec - start.tv_usec) / 1000; |
| pr_debug("step 2, memcpy use: %ldms\n", time_use); |
| #endif |
| } |
| if (kthread_should_stop()) { |
| complete(&devp->vb_done_sema); |
| break; |
| } |
| complete(&devp->vb_done_sema); |
| } |
| vm_exit: |
| destroy_ge2d_work_queue(context); |
| 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);/*default 10ms*/ |
| } |
| return ret; |
| } |
| |
| /*simulate v4l2 device to request*/ |
| /*filling buffer,only for test use*/ |
| static int simulate_task(void *data) |
| { |
| while (1) { |
| msleep(50); |
| pr_debug("simulate succeed\n"); |
| } |
| return 0; |
| } |
| |
| /* |
| *********************************************** |
| * |
| * init functions. |
| * |
| *********************************************** |
| */ |
| |
| int alloc_vm_canvas(struct vm_device_s *vdevp) |
| { |
| int j; |
| if (vdevp->index == 0) { |
| for (j = 0; j < MAX_CANVAS_INDEX; j++) { |
| memset(&(vdevp->vm_canvas[j]), -1, sizeof(int)); |
| vdevp->vm_canvas[j] = |
| canvas_pool_map_alloc_canvas("vm0"); |
| if (vdevp->vm_canvas[j] < 0) { |
| pr_err("alloc vm0_canvas %d failed\n", j); |
| return -1; |
| } |
| } |
| } else { |
| for (j = 0; j < MAX_CANVAS_INDEX; j++) { |
| memset(&(vdevp->vm_canvas[j]), -1, sizeof(int)); |
| vdevp->vm_canvas[j] = |
| canvas_pool_map_alloc_canvas("vm1"); |
| if (vdevp->vm_canvas[j] < 0) { |
| pr_err("alloc vm1_canvas %d failed\n", j); |
| return -1; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| int vm_buffer_init(struct vm_device_s *vdevp) |
| { |
| int i; |
| u32 canvas_width, canvas_height; |
| u32 decbuf_size; |
| resource_size_t buf_start; |
| unsigned int buf_size; |
| int buf_num = 0; |
| int local_pool_size = 0; |
| |
| init_completion(&vdevp->vb_start_sema); |
| init_completion(&vdevp->vb_done_sema); |
| |
| buf_start = vdevp->buffer_start; |
| buf_size = vdevp->buffer_size; |
| |
| if (!buf_start || !buf_size) |
| goto exit; |
| |
| for (i = 0; i < ARRAY_SIZE(vmdecbuf_size); i++) { |
| if (buf_size >= vmdecbuf_size[i]) |
| break; |
| } |
| if (i == ARRAY_SIZE(vmdecbuf_size)) { |
| pr_debug("vmbuf size=%d less than the smallest vmbuf size%d\n", |
| buf_size, vmdecbuf_size[i - 1]); |
| return -1; |
| } |
| |
| canvas_width = canvas_config_wh[i].width;/* 1920; */ |
| canvas_height = canvas_config_wh[i].height;/* 1200; */ |
| decbuf_size = vmdecbuf_size[i];/* 0x700000; */ |
| buf_num = buf_size / decbuf_size; |
| |
| if (buf_num > 0) |
| local_pool_size = 1; |
| else { |
| local_pool_size = 0; |
| pr_debug("need at least one buffer to handle 1920*1080 data.\n"); |
| } |
| |
| for (i = 0; i < local_pool_size; i++) { |
| canvas_config((vdevp->vm_canvas[0] + i), |
| (unsigned long)(buf_start + i * decbuf_size), |
| canvas_width * 2, canvas_height, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| canvas_config((vdevp->vm_canvas[2] + i), |
| (unsigned long)(buf_start + i * decbuf_size), |
| canvas_width * 3, canvas_height, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| canvas_config((vdevp->vm_canvas[4] + i), |
| (unsigned long)(buf_start + i * decbuf_size / 2), |
| canvas_width, canvas_height, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| canvas_config(vdevp->vm_canvas[6] + i, |
| (unsigned long)(buf_start + |
| (i + 1)*decbuf_size / 2), |
| canvas_width, canvas_height / 2, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| |
| canvas_config((vdevp->vm_canvas[8] + i), |
| (unsigned long)(buf_start + |
| (i + 1)*decbuf_size / 2), |
| canvas_width / 2, canvas_height / 2, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| canvas_config((vdevp->vm_canvas[10] + i), |
| (unsigned long)(buf_start + |
| (i + 3)*decbuf_size / 4), |
| canvas_width / 2, canvas_height / 2, |
| CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| vfbuf_use[i] = 0; |
| #endif |
| } |
| |
| exit: |
| return 0; |
| |
| } |
| |
| int start_vm_task(struct vm_device_s *vdevp) |
| { |
| /* init the device. */ |
| vm_local_init(); |
| if (!vdevp->task) { |
| vdevp->task = kthread_create(vm_task, vdevp, vdevp->name); |
| if (IS_ERR(vdevp->task)) { |
| pr_err("thread creating error.\n"); |
| return -1; |
| } |
| init_waitqueue_head(&vdevp->frame_ready); |
| wake_up_process(vdevp->task); |
| } |
| vdevp->task_running = 1; |
| return 0; |
| } |
| |
| int start_simulate_task(void) |
| { |
| if (!simulate_task_fd) { |
| simulate_task_fd = kthread_create(simulate_task, 0, "vm"); |
| if (IS_ERR(simulate_task_fd)) { |
| pr_err("thread creating error.\n"); |
| return -1; |
| } |
| wake_up_process(simulate_task_fd); |
| } |
| return 0; |
| } |
| |
| |
| void stop_vm_task(struct vm_device_s *vdevp) |
| { |
| if (vdevp->task) { |
| vdevp->task_running = 0; |
| send_sig(SIGTERM, vdevp->task, 1); |
| complete(&vdevp->vb_start_sema); |
| wake_up_interruptible(&vdevp->frame_ready); |
| kthread_stop(vdevp->task); |
| vdevp->task = NULL; |
| } |
| vm_local_init(); |
| } |
| |
| |
| /* |
| ********************************************************************** |
| * |
| * global status. |
| * |
| ********************************************************************** |
| */ |
| |
| static int vm_enable_flag; |
| |
| int get_vm_status(void) |
| { |
| return vm_enable_flag; |
| } |
| |
| void set_vm_status(int flag) |
| { |
| if (flag >= 0) |
| vm_enable_flag = flag; |
| else |
| vm_enable_flag = 0; |
| } |
| |
| /* |
| ********************************************************************** |
| * |
| * file op section. |
| * |
| ********************************************************************** |
| */ |
| |
| void unset_vm_buf_res(struct vm_init_s *info) |
| { |
| info->buffer_start = 0; |
| } |
| |
| static void vm_cache_this_flush(unsigned int buf_start, unsigned int buf_size, |
| struct vm_init_s *info) |
| { |
| struct vm_device_s *devp = vm_device[info->vdin_id]; |
| |
| if (devp->dev) { |
| if ((buf_start >= info->buffer_start) && |
| ((buf_start + buf_size) <= (info->buffer_start + |
| info->vm_buf_size))) |
| dma_sync_single_for_cpu(devp->dev, |
| buf_start, buf_size, DMA_FROM_DEVICE); |
| } |
| } |
| |
| static int vm_open(struct inode *inode, struct file *file) |
| { |
| struct ge2d_context_s *context = NULL; |
| |
| pr_info("open one vm device\n"); |
| file->private_data = context; |
| /* vm_device.open_count++; */ |
| return 0; |
| } |
| |
| static long vm_ioctl(struct file *filp, unsigned int cmd, unsigned long args) |
| { |
| int ret = 0; |
| struct ge2d_context_s *context; |
| void __user *argp; |
| |
| context = (struct ge2d_context_s *)filp->private_data; |
| argp = (void __user *)args; |
| switch (cmd) { |
| case VM_IOC_2OSD0: |
| break; |
| case VM_IOC_ENABLE_PP: |
| break; |
| case VM_IOC_CONFIG_FRAME: |
| break; |
| default: |
| return -ENOIOCTLCMD; |
| } |
| return ret; |
| } |
| |
| static int vm_release(struct inode *inode, struct file *file) |
| { |
| struct ge2d_context_s *context = |
| (struct ge2d_context_s *)file->private_data; |
| |
| if (context && (destroy_ge2d_work_queue(context) == 0)) { |
| /* vm_device.open_count--; */ |
| return 0; |
| } |
| pr_info("release one vm device\n"); |
| return -1; |
| } |
| |
| /* |
| ********************************************************************** |
| * |
| * file op init section. |
| * |
| ********************************************************************** |
| */ |
| |
| static const struct file_operations vm_fops = { |
| .owner = THIS_MODULE, |
| .open = vm_open, |
| .unlocked_ioctl = vm_ioctl, |
| .release = vm_release, |
| }; |
| |
| static ssize_t vm_attr_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| ssize_t len = 0; |
| struct vm_device_s *devp; |
| |
| devp = dev_get_drvdata(dev); |
| if (devp->task_running == 0) { |
| len += sprintf(buf + len, "vm does not start\n"); |
| return len; |
| } |
| |
| len += sprintf((char *)buf + len, "vm parameters below\n"); |
| |
| return len; |
| } |
| |
| static ssize_t vm_attr_store(struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t len) |
| { |
| struct vm_device_s *devp; |
| unsigned int n = 0; |
| char *buf_orig, *ps, *token; |
| char *parm[6] = {NULL}; |
| |
| if (!buf) |
| return len; |
| |
| buf_orig = kstrdup(buf, GFP_KERNEL); |
| /* printk(KERN_INFO "input cmd : %s",buf_orig); */ |
| devp = dev_get_drvdata(dev); |
| if (devp->task_running == 0) { |
| len += sprintf((char *)buf + len, "vm does not start\n"); |
| return len; |
| } |
| |
| ps = buf_orig; |
| while (1) { |
| if (n >= ARRAY_SIZE(parm)) { |
| pr_err("parm array overflow.\n"); |
| #if 0 |
| pr_err("n=%d,ARRAY_SIZE(parm)=%d\n", |
| n, ARRAY_SIZE(parm)); |
| #endif |
| return len; |
| } |
| token = strsep(&ps, "\n"); |
| if (token == NULL) |
| break; |
| if (*token == '\0') |
| continue; |
| parm[n++] = token; |
| } |
| |
| if (strcmp(parm[0], "before") == 0) { |
| devp->dump = 1; |
| devp->dump_path = parm[1]; |
| pr_debug("this not support\n"); |
| } else if (strcmp(parm[0], "after") == 0) { |
| devp->dump = 2; |
| devp->dump_path = parm[1]; |
| pr_debug("after ge2d processed, store to %s\n", parm[1]); |
| } |
| |
| kfree(buf_orig); |
| return len; |
| } |
| |
| static DEVICE_ATTR(dump, 0664, vm_attr_show, vm_attr_store); |
| |
| static int vm_add_cdev(struct cdev *cdevp, |
| const struct file_operations *fops, |
| int minor) |
| { |
| int ret; |
| dev_t devno = MKDEV(MAJOR(vm_devno), minor); |
| |
| cdev_init(cdevp, fops); |
| cdevp->owner = THIS_MODULE; |
| ret = cdev_add(cdevp, devno, 1); |
| return ret; |
| } |
| |
| int init_vm_device(struct vm_device_s *vdevp, struct platform_device *pdev) |
| { |
| int ret = 0; |
| |
| ret = vm_add_cdev(&vdevp->cdev, &vm_fops, vdevp->index); |
| if (ret) { |
| pr_err("%s: failed to add cdev. !\n", __func__); |
| goto fail_add_cdev; |
| } |
| vdevp->dev = device_create(vm_clsp, &pdev->dev, MKDEV(MAJOR(vm_devno), |
| vdevp->index), |
| NULL, "%s%d", VM_DEV_NAME, vdevp->index); |
| if (IS_ERR(vdevp->dev)) { |
| pr_err("%s: failed to create device. !\n", __func__); |
| ret = PTR_ERR(vdevp->dev); |
| goto fail_create_device; |
| } |
| ret = device_create_file(vdevp->dev, &dev_attr_dump); |
| if (ret < 0) { |
| pr_err("%s: fail to create vdin attribute files.\n", __func__); |
| goto fail_create_dev_file; |
| } |
| |
| dev_set_drvdata(vdevp->dev, vdevp); |
| platform_set_drvdata(pdev, vdevp); |
| |
| /* if (vm_buffer_init(vdevp) < 0) */ |
| /* goto unregister_dev; */ |
| #ifndef CONFIG_AMLOGIC_VM_DISABLE_VIDEOLAYER |
| vf_provider_init(&vm_vf_prov, PROVIDER_NAME, &vm_vf_provider, NULL); |
| #endif |
| /* vf_reg_provider(&vm_vf_prov); */ |
| vdevp->task = NULL; |
| vdevp->task_running = 0; |
| memset(&vdevp->output_para, 0, sizeof(struct vm_output_para)); |
| sprintf(vdevp->name, "%s%d", RECEIVER_NAME, vdevp->index); |
| sprintf(vdevp->vf_provider_name, "%s%d", "vdin", vdevp->index); |
| |
| snprintf(vdevp->vfm_map_chain, VM_MAP_NAME_SIZE, |
| "%s %s", vdevp->vf_provider_name, |
| vdevp->name); |
| snprintf(vdevp->vfm_map_id, VM_MAP_NAME_SIZE, |
| "vm-map-%d", vdevp->index); |
| if (vfm_map_add(vdevp->vfm_map_id, |
| vdevp->vfm_map_chain) < 0) { |
| pr_err("vm pipeline map creation failed %s.\n", |
| vdevp->vfm_map_id); |
| goto fail_create_dev_file; |
| } |
| |
| vf_receiver_init(&vdevp->vm_vf_recv, vdevp->name, |
| &vm_vf_receiver, vdevp); |
| |
| vf_reg_receiver(&vdevp->vm_vf_recv); |
| return 0; |
| |
| /* unregister_dev: */ |
| fail_create_dev_file: |
| device_destroy(vm_clsp, MKDEV(MAJOR(vm_devno), vdevp->index)); |
| fail_create_device: |
| cdev_del(&vdevp->cdev); |
| fail_add_cdev: |
| kfree(vdevp); |
| return ret; |
| |
| } |
| |
| int uninit_vm_device(struct platform_device *plat_dev) |
| { |
| struct vm_device_s *vdevp; |
| |
| vdevp = platform_get_drvdata(plat_dev); |
| /* stop_vm_task(vdevp); */ |
| device_remove_file(vdevp->dev, &dev_attr_dump); |
| device_destroy(vm_clsp, MKDEV(MAJOR(vm_devno), vdevp->index)); |
| cdev_del(&vdevp->cdev); |
| vm_device[vdevp->index] = NULL; |
| |
| /* free drvdata */ |
| dev_set_drvdata(vdevp->dev, NULL); |
| platform_set_drvdata(plat_dev, NULL); |
| kfree(vdevp); |
| return 0; |
| } |
| |
| int vm_init_resource(size_t size, struct vm_init_s *info) |
| { |
| struct vm_device_s *devp; |
| #ifdef CONFIG_CMA |
| devp = vm_device[info->vdin_id]; |
| if (size == 0) |
| return -1; |
| if (info->vm_pages && info->vm_buf_size != 0) { |
| |
| dma_release_from_contiguous(&devp->pdev->dev, |
| info->vm_pages, |
| info->vm_buf_size / PAGE_SIZE); |
| } |
| info->vm_pages = dma_alloc_from_contiguous(&devp->pdev->dev, |
| size / PAGE_SIZE, 0); |
| if (info->vm_pages) { |
| dma_addr_t phys; |
| |
| phys = page_to_phys(info->vm_pages); |
| info->buffer_start = phys; |
| info->vm_buf_size = size; |
| if (info->bt_path_count == 1) { |
| if (info->vdin_id == 0) |
| info->isused = true; |
| else |
| info->isused = false; |
| } else { |
| info->isused = true; |
| } |
| info->mem_alloc_succeed = true; |
| return 0; |
| } |
| |
| info->mem_alloc_succeed = false; |
| pr_err("CMA failed to allocate dma buffer\n"); |
| return -ENOMEM; |
| #else |
| if (size == 0) |
| return -1; |
| info->buffer_start = vm_device[info->vdin_id]->buffer_start; |
| info->vm_buf_size = vm_device[info->vdin_id]->buffer_size; |
| info->mem_alloc_succeed = true; |
| info->isused = true; |
| return 0; |
| #endif |
| } |
| EXPORT_SYMBOL(vm_init_resource); |
| |
| void vm_deinit_resource(struct vm_init_s *info) |
| { |
| #ifdef CONFIG_CMA |
| struct vm_device_s *devp; |
| |
| devp = vm_device[info->vdin_id]; |
| if (info->vm_buf_size == 0) { |
| pr_warn("vm buf size equals 0\n"); |
| return; |
| } |
| unset_vm_buf_res(info); |
| |
| if (info->vm_pages) { |
| dma_release_from_contiguous(&devp->pdev->dev, |
| info->vm_pages, |
| info->vm_buf_size / PAGE_SIZE); |
| info->vm_buf_size = 0; |
| info->vm_pages = NULL; |
| info->isused = false; |
| info->mem_alloc_succeed = false; |
| } |
| #else |
| if (info->vm_buf_size) { |
| info->buffer_start = 0; |
| info->vm_buf_size = 0; |
| info->isused = false; |
| info->mem_alloc_succeed = false; |
| } |
| #endif |
| } |
| EXPORT_SYMBOL(vm_deinit_resource); |
| |
| |
| /******************************************************************* |
| * |
| * interface for Linux driver |
| * |
| * ******************************************************************/ |
| |
| static int vm_mem_device_init(struct reserved_mem *rmem, struct device *dev) |
| { |
| struct platform_device *pdev = container_of(dev, |
| struct platform_device, dev); |
| struct vm_device_s *vdevp = platform_get_drvdata(pdev); |
| unsigned long mem_start, mem_end; |
| |
| vdevp->buffer_start = rmem->base; |
| vdevp->buffer_size = rmem->size; |
| mem_start = rmem->base; |
| mem_end = rmem->base + rmem->size - 1; |
| pr_info("init vm memsource %lx->%lx\n", mem_start, mem_end); |
| return 0; |
| } |
| |
| static const struct reserved_mem_ops rmem_vm_ops = { |
| .device_init = vm_mem_device_init, |
| }; |
| |
| static int __init vm_mem_setup(struct reserved_mem *rmem) |
| { |
| rmem->ops = &rmem_vm_ops; |
| pr_info("vm share mem setup\n"); |
| return 0; |
| } |
| |
| /* MODULE_AMLOG(AMLOG_DEFAULT_LEVEL, 0xff, LOG_LEVEL_DESC, LOG_MASK_DESC); */ |
| |
| |
| /* for driver. */ |
| static int vm_driver_probe(struct platform_device *pdev) |
| { |
| int idx; |
| int ret; |
| struct vm_device_s *vdevp; |
| |
| pr_info("vm memory init\n"); |
| /* malloc vdev */ |
| vdevp = kmalloc(sizeof(struct vm_device_s), GFP_KERNEL); |
| if (!vdevp) |
| return -ENOMEM; |
| |
| memset(vdevp, 0, sizeof(struct vm_device_s)); |
| |
| if (pdev->dev.of_node) { |
| ret = of_property_read_u32(pdev->dev.of_node, |
| "vm_id", &(vdevp->index)); |
| if (ret) |
| pr_err("don't find vm id.\n"); |
| } |
| vm_device[vdevp->index] = vdevp; |
| vdevp->pdev = pdev; |
| platform_set_drvdata(pdev, vdevp); |
| ret = alloc_vm_canvas(vdevp); |
| if (ret != 0) { |
| pr_err("alloc vm canvas failed\n"); |
| return ret; |
| } |
| vm_buffer_init(vdevp); |
| ret = init_vm_device(vdevp, pdev); |
| if (ret != 0) |
| return ret; |
| idx = of_reserved_mem_device_init(&pdev->dev); |
| if (idx == 0) |
| pr_info("vm probe done\n"); |
| else |
| pr_info("malloc reserved memory failed !\n"); |
| |
| /* init_vm_device(vdevp, pdev); */ |
| return 0; |
| } |
| |
| static int vm_drv_remove(struct platform_device *plat_dev) |
| { |
| int i; |
| struct vm_device_s *vdevp; |
| |
| vdevp = platform_get_drvdata(plat_dev); |
| |
| for (i = 0; i < MAX_CANVAS_INDEX; i++) { |
| if ((vdevp != NULL) && |
| (vdevp->vm_canvas[i] >= 0)) { |
| canvas_pool_map_free_canvas( |
| vdevp->vm_canvas[i]); |
| memset(&(vdevp->vm_canvas[i]), |
| -1, sizeof(int)); |
| } |
| } |
| |
| if (vdevp->vfm_map_id[0]) { |
| vfm_map_remove(vdevp->vfm_map_id); |
| vdevp->vfm_map_id[0] = 0; |
| } |
| |
| uninit_vm_device(plat_dev); |
| return 0; |
| } |
| |
| static const struct of_device_id amlogic_vm_dt_match[] = { |
| { |
| .compatible = "amlogic, vm", |
| }, |
| {}, |
| }; |
| |
| /* general interface for a linux driver .*/ |
| static struct platform_driver vm_drv = { |
| .probe = vm_driver_probe, |
| .remove = vm_drv_remove, |
| .driver = { |
| .name = "vm", |
| .owner = THIS_MODULE, |
| .of_match_table = amlogic_vm_dt_match, |
| } |
| }; |
| |
| static int __init |
| vm_init_module(void) |
| { |
| int ret = 0; |
| |
| pr_info("vm_init .\n"); |
| ret = alloc_chrdev_region(&vm_devno, 0, VM_MAX_DEVS, VM_DEV_NAME); |
| if (ret < 0) { |
| pr_err("%s: failed to allocate major number\n", __func__); |
| goto fail_alloc_cdev_region; |
| } |
| vm_clsp = class_create(THIS_MODULE, VM_CLS_NAME); |
| if (IS_ERR(vm_clsp)) { |
| ret = PTR_ERR(vm_clsp); |
| pr_err("%s: failed to create class\n", __func__); |
| goto fail_class_create; |
| } |
| ret = platform_driver_register(&vm_drv); |
| if (ret) { |
| pr_err("Failed to register vm driver (error=%d\n", |
| ret); |
| goto fail_pdrv_register; |
| } |
| return 0; |
| |
| fail_pdrv_register: |
| class_destroy(vm_clsp); |
| fail_class_create: |
| unregister_chrdev_region(vm_devno, VM_MAX_DEVS); |
| fail_alloc_cdev_region: |
| return ret; |
| } |
| |
| static void __exit |
| vm_remove_module(void) |
| { |
| /* class_remove_file(vm_clsp, &class_attr_dump); */ |
| class_destroy(vm_clsp); |
| unregister_chrdev_region(vm_devno, VM_MAX_DEVS); |
| platform_driver_unregister(&vm_drv); |
| pr_info("vm module removed.\n"); |
| } |
| |
| module_init(vm_init_module); |
| module_exit(vm_remove_module); |
| RESERVEDMEM_OF_DECLARE(vm, "amlogic, vm_memory", vm_mem_setup); |
| |
| MODULE_DESCRIPTION("Amlogic Video Input Manager"); |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("Simon Zheng <simon.zheng@amlogic.com>"); |