| /* |
| * drivers/amlogic/media/frame_provider/decoder/utils/amvdec.c |
| * |
| * Copyright (C) 2016 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. |
| * |
| */ |
| #define DEBUG |
| #include <linux/kernel.h> |
| #include <linux/types.h> |
| #include <linux/errno.h> |
| #include <linux/platform_device.h> |
| #include <linux/sched.h> |
| #include <linux/slab.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/amlogic/media/utils/vformat.h> |
| #include <linux/module.h> |
| #include <linux/delay.h> |
| #include <linux/vmalloc.h> |
| #include "vdec.h" |
| |
| #ifdef CONFIG_PM |
| #include <linux/pm.h> |
| #endif |
| |
| #ifdef CONFIG_WAKELOCK |
| #include <linux/wakelock.h> |
| #endif |
| #include "../../../stream_input/amports/amports_priv.h" |
| |
| /* #include <mach/am_regs.h> */ |
| /* #include <mach/power_gate.h> */ |
| #include <linux/amlogic/media/utils/vdec_reg.h> |
| #include "amvdec.h" |
| #include <linux/amlogic/media/utils/amports_config.h> |
| #include "firmware.h" |
| #include <linux/amlogic/tee.h> |
| #include "../../../common/chips/decoder_cpu_ver_info.h" |
| |
| #define MC_SIZE (4096 * 16) |
| |
| #ifdef CONFIG_WAKELOCK |
| static struct wake_lock amvdec_lock; |
| struct timer_list amvdevtimer; |
| #define WAKE_CHECK_INTERVAL (100*HZ/100) |
| #endif |
| #define AMVDEC_USE_STATIC_MEMORY |
| static void *mc_addr; |
| static dma_addr_t mc_addr_map; |
| |
| #ifdef CONFIG_WAKELOCK |
| static int video_running; |
| static int video_stated_changed = 1; |
| #endif |
| |
| static void amvdec_pg_enable(bool enable) |
| { |
| ulong timeout; |
| |
| if (enable) { |
| AMVDEC_CLK_GATE_ON(MDEC_CLK_PIC_DC); |
| AMVDEC_CLK_GATE_ON(MDEC_CLK_DBLK); |
| AMVDEC_CLK_GATE_ON(MC_CLK); |
| AMVDEC_CLK_GATE_ON(IQIDCT_CLK); |
| /* AMVDEC_CLK_GATE_ON(VLD_CLK); */ |
| AMVDEC_CLK_GATE_ON(AMRISC); |
| /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */ |
| if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M8) |
| WRITE_VREG(GCLK_EN, 0x3ff); |
| /* #endif */ |
| CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 31); |
| } else { |
| |
| AMVDEC_CLK_GATE_OFF(AMRISC); |
| timeout = jiffies + HZ / 100; |
| |
| while (READ_VREG(MDEC_PIC_DC_STATUS) != 0) { |
| if (time_after(jiffies, timeout)) { |
| WRITE_VREG_BITS(MDEC_PIC_DC_CTRL, 1, 0, 1); |
| WRITE_VREG_BITS(MDEC_PIC_DC_CTRL, 0, 0, 1); |
| READ_VREG(MDEC_PIC_DC_STATUS); |
| READ_VREG(MDEC_PIC_DC_STATUS); |
| READ_VREG(MDEC_PIC_DC_STATUS); |
| break; |
| } |
| } |
| |
| AMVDEC_CLK_GATE_OFF(MDEC_CLK_PIC_DC); |
| timeout = jiffies + HZ / 100; |
| |
| while (READ_VREG(DBLK_STATUS) & 1) { |
| if (time_after(jiffies, timeout)) { |
| WRITE_VREG(DBLK_CTRL, 3); |
| WRITE_VREG(DBLK_CTRL, 0); |
| READ_VREG(DBLK_STATUS); |
| READ_VREG(DBLK_STATUS); |
| READ_VREG(DBLK_STATUS); |
| break; |
| } |
| } |
| AMVDEC_CLK_GATE_OFF(MDEC_CLK_DBLK); |
| timeout = jiffies + HZ / 100; |
| |
| while (READ_VREG(MC_STATUS0) & 1) { |
| if (time_after(jiffies, timeout)) { |
| SET_VREG_MASK(MC_CTRL1, 0x9); |
| CLEAR_VREG_MASK(MC_CTRL1, 0x9); |
| READ_VREG(MC_STATUS0); |
| READ_VREG(MC_STATUS0); |
| READ_VREG(MC_STATUS0); |
| break; |
| } |
| } |
| AMVDEC_CLK_GATE_OFF(MC_CLK); |
| timeout = jiffies + HZ / 100; |
| while (READ_VREG(DCAC_DMA_CTRL) & 0x8000) { |
| if (time_after(jiffies, timeout)) |
| break; |
| } |
| AMVDEC_CLK_GATE_OFF(IQIDCT_CLK); |
| /* AMVDEC_CLK_GATE_OFF(VLD_CLK); */ |
| } |
| } |
| |
| static void amvdec2_pg_enable(bool enable) |
| { |
| if (has_vdec2()) { |
| ulong timeout; |
| |
| if (!vdec_on(VDEC_2)) |
| return; |
| if (enable) { |
| /* WRITE_VREG(VDEC2_GCLK_EN, 0x3ff); */ |
| } else { |
| timeout = jiffies + HZ / 10; |
| |
| while (READ_VREG(VDEC2_MDEC_PIC_DC_STATUS) != 0) { |
| if (time_after(jiffies, timeout)) { |
| WRITE_VREG_BITS(VDEC2_MDEC_PIC_DC_CTRL, |
| 1, 0, 1); |
| WRITE_VREG_BITS(VDEC2_MDEC_PIC_DC_CTRL, |
| 0, 0, 1); |
| READ_VREG(VDEC2_MDEC_PIC_DC_STATUS); |
| READ_VREG(VDEC2_MDEC_PIC_DC_STATUS); |
| READ_VREG(VDEC2_MDEC_PIC_DC_STATUS); |
| break; |
| } |
| } |
| |
| timeout = jiffies + HZ / 10; |
| |
| while (READ_VREG(VDEC2_DBLK_STATUS) & 1) { |
| if (time_after(jiffies, timeout)) { |
| WRITE_VREG(VDEC2_DBLK_CTRL, 3); |
| WRITE_VREG(VDEC2_DBLK_CTRL, 0); |
| READ_VREG(VDEC2_DBLK_STATUS); |
| READ_VREG(VDEC2_DBLK_STATUS); |
| READ_VREG(VDEC2_DBLK_STATUS); |
| break; |
| } |
| } |
| |
| timeout = jiffies + HZ / 10; |
| |
| while (READ_VREG(VDEC2_DCAC_DMA_CTRL) & 0x8000) { |
| if (time_after(jiffies, timeout)) |
| break; |
| } |
| } |
| } |
| } |
| |
| static void amhevc_pg_enable(bool enable) |
| { |
| if (has_hevc_vdec()) { |
| ulong timeout; |
| |
| if (!vdec_on(VDEC_HEVC)) |
| return; |
| if (enable) { |
| /* WRITE_VREG(VDEC2_GCLK_EN, 0x3ff); */ |
| } else { |
| timeout = jiffies + HZ / 10; |
| |
| while (READ_VREG(HEVC_MDEC_PIC_DC_STATUS) != 0) { |
| if (time_after(jiffies, timeout)) { |
| WRITE_VREG_BITS(HEVC_MDEC_PIC_DC_CTRL, |
| 1, 0, 1); |
| WRITE_VREG_BITS(HEVC_MDEC_PIC_DC_CTRL, |
| 0, 0, 1); |
| READ_VREG(HEVC_MDEC_PIC_DC_STATUS); |
| READ_VREG(HEVC_MDEC_PIC_DC_STATUS); |
| READ_VREG(HEVC_MDEC_PIC_DC_STATUS); |
| break; |
| } |
| } |
| |
| timeout = jiffies + HZ / 10; |
| |
| while (READ_VREG(HEVC_DBLK_STATUS) & 1) { |
| if (time_after(jiffies, timeout)) { |
| WRITE_VREG(HEVC_DBLK_CTRL, 3); |
| WRITE_VREG(HEVC_DBLK_CTRL, 0); |
| READ_VREG(HEVC_DBLK_STATUS); |
| READ_VREG(HEVC_DBLK_STATUS); |
| READ_VREG(HEVC_DBLK_STATUS); |
| break; |
| } |
| } |
| |
| timeout = jiffies + HZ / 10; |
| |
| while (READ_VREG(HEVC_DCAC_DMA_CTRL) & 0x8000) { |
| if (time_after(jiffies, timeout)) |
| break; |
| } |
| } |
| } |
| } |
| |
| #ifdef CONFIG_WAKELOCK |
| int amvdec_wake_lock(void) |
| { |
| wake_lock(&amvdec_lock); |
| return 0; |
| } |
| |
| int amvdec_wake_unlock(void) |
| { |
| wake_unlock(&amvdec_lock); |
| return 0; |
| } |
| #else |
| #define amvdec_wake_lock() |
| #define amvdec_wake_unlock() |
| #endif |
| |
| static s32 am_vdec_loadmc_ex(struct vdec_s *vdec, |
| const char *name, char *def, s32(*load)(const u32 *)) |
| { |
| int err; |
| |
| if (!vdec->mc_loaded) { |
| if (!def) { |
| err = get_decoder_firmware_data(vdec->format, |
| name, (u8 *)(vdec->mc), |
| (4096 * 4 * 4)); |
| if (err <= 0) |
| return -1; |
| } else |
| memcpy((char *)vdec->mc, def, sizeof(vdec->mc)); |
| |
| vdec->mc_loaded = true; |
| } |
| |
| err = (*load)(vdec->mc); |
| if (err < 0) { |
| pr_err("loading firmware %s to vdec ram failed!\n", name); |
| return err; |
| } |
| |
| return err; |
| } |
| |
| static s32 am_vdec_loadmc_buf_ex(struct vdec_s *vdec, |
| char *buf, int size, s32(*load)(const u32 *)) |
| { |
| int err; |
| |
| if (!vdec->mc_loaded) { |
| memcpy((u8 *)(vdec->mc), buf, size); |
| vdec->mc_loaded = true; |
| } |
| |
| err = (*load)(vdec->mc); |
| if (err < 0) { |
| pr_err("loading firmware to vdec ram failed!\n"); |
| return err; |
| } |
| |
| return err; |
| } |
| |
| static s32 am_loadmc_ex(enum vformat_e type, |
| const char *name, char *def, s32(*load)(const u32 *)) |
| { |
| char *mc_addr = vmalloc(4096 * 16); |
| char *pmc_addr = def; |
| int err; |
| |
| if (!def && mc_addr) { |
| int loaded; |
| |
| loaded = get_decoder_firmware_data(type, |
| name, mc_addr, (4096 * 16)); |
| if (loaded > 0) |
| pmc_addr = mc_addr; |
| } |
| if (!pmc_addr) { |
| vfree(mc_addr); |
| return -1; |
| } |
| err = (*load)((u32 *) pmc_addr); |
| if (err < 0) { |
| pr_err("loading firmware %s to vdec ram failed!\n", name); |
| vfree(mc_addr); |
| return err; |
| } |
| vfree(mc_addr); |
| |
| return err; |
| } |
| |
| static s32 amvdec_loadmc(const u32 *p) |
| { |
| ulong timeout; |
| s32 ret = 0; |
| |
| #ifdef AMVDEC_USE_STATIC_MEMORY |
| if (mc_addr == NULL) { |
| #else |
| { |
| #endif |
| mc_addr = kmalloc(MC_SIZE, GFP_KERNEL); |
| } |
| |
| if (!mc_addr) |
| return -ENOMEM; |
| |
| memcpy(mc_addr, p, MC_SIZE); |
| |
| mc_addr_map = dma_map_single(get_vdec_device(), |
| mc_addr, MC_SIZE, DMA_TO_DEVICE); |
| |
| WRITE_VREG(MPSR, 0); |
| WRITE_VREG(CPSR, 0); |
| |
| /* Read CBUS register for timing */ |
| timeout = READ_VREG(MPSR); |
| timeout = READ_VREG(MPSR); |
| |
| timeout = jiffies + HZ; |
| |
| WRITE_VREG(IMEM_DMA_ADR, mc_addr_map); |
| WRITE_VREG(IMEM_DMA_COUNT, 0x1000); |
| WRITE_VREG(IMEM_DMA_CTRL, (0x8000 | (7 << 16))); |
| |
| while (READ_VREG(IMEM_DMA_CTRL) & 0x8000) { |
| if (time_before(jiffies, timeout)) |
| schedule(); |
| else { |
| pr_err("vdec load mc error\n"); |
| ret = -EBUSY; |
| break; |
| } |
| } |
| |
| dma_unmap_single(get_vdec_device(), |
| mc_addr_map, MC_SIZE, DMA_TO_DEVICE); |
| |
| #ifndef AMVDEC_USE_STATIC_MEMORY |
| kfree(mc_addr); |
| mc_addr = NULL; |
| #endif |
| |
| return ret; |
| } |
| |
| s32 optee_load_fw(enum vformat_e type, const char *fw_name) |
| { |
| s32 ret = -1; |
| unsigned int format = FIRMWARE_MAX; |
| unsigned int vdec = OPTEE_VDEC_LEGENCY; |
| char *name = __getname(); |
| bool is_swap = false; |
| |
| sprintf(name, "%s", fw_name ? fw_name : "null"); |
| |
| switch ((u32)type) { |
| case VFORMAT_VC1: |
| format = VIDEO_DEC_VC1; |
| break; |
| |
| case VFORMAT_AVS: |
| if (!strcmp(name, "avs_no_cabac")) |
| format = VIDEO_DEC_AVS_NOCABAC; |
| else if (!strcmp(name, "avs_multi")) |
| format = VIDEO_DEC_AVS_MULTI; |
| else |
| format = VIDEO_DEC_AVS; |
| break; |
| |
| case VFORMAT_MPEG12: |
| if (!strcmp(name, "mpeg12")) |
| format = VIDEO_DEC_MPEG12; |
| else if (!strcmp(name, "mmpeg12")) |
| format = VIDEO_DEC_MPEG12_MULTI; |
| break; |
| |
| case VFORMAT_MJPEG: |
| if (!strcmp(name, "mmjpeg")) |
| format = VIDEO_DEC_MJPEG_MULTI; |
| else |
| format = VIDEO_DEC_MJPEG; |
| break; |
| |
| case VFORMAT_VP9: |
| if (!strcmp(name, "vp9_mc")) |
| format = VIDEO_DEC_VP9; |
| else |
| format = VIDEO_DEC_VP9_MMU; |
| break; |
| |
| case VFORMAT_AVS2: |
| format = VIDEO_DEC_AVS2_MMU; |
| vdec = OPTEE_VDEC_HEVC; |
| break; |
| |
| case VFORMAT_AV1: |
| format = VIDEO_DEC_AV1_MMU; |
| vdec = OPTEE_VDEC_HEVC; |
| break; |
| |
| case VFORMAT_HEVC: |
| if (!strcmp(name, "h265_mmu")) |
| format = VIDEO_DEC_HEVC_MMU; |
| else if (!strcmp(name, "hevc_mmu_swap")) { |
| format = VIDEO_DEC_HEVC_MMU_SWAP; |
| vdec = OPTEE_VDEC_HEVC; |
| is_swap = true; |
| } else |
| format = VIDEO_DEC_HEVC; |
| break; |
| |
| case VFORMAT_REAL: |
| if (!strcmp(name, "vreal_mc_8")) |
| format = VIDEO_DEC_REAL_V8; |
| else if (!strcmp(name, "vreal_mc_9")) |
| format = VIDEO_DEC_REAL_V9; |
| break; |
| |
| case VFORMAT_MPEG4: |
| if (!strcmp(name, "mmpeg4_mc_5")) |
| format = VIDEO_DEC_MPEG4_5_MULTI; |
| else if ((!strcmp(name, "mh263_mc"))) |
| format = VIDEO_DEC_H263_MULTI; |
| else if (!strcmp(name, "vmpeg4_mc_5")) |
| format = VIDEO_DEC_MPEG4_5; |
| else if (!strcmp(name, "h263_mc")) |
| format = VIDEO_DEC_H263; |
| /*not support now*/ |
| else if (!strcmp(name, "vmpeg4_mc_311")) |
| format = VIDEO_DEC_MPEG4_3; |
| else if (!strcmp(name, "vmpeg4_mc_4")) |
| format = VIDEO_DEC_MPEG4_4; |
| break; |
| |
| case VFORMAT_H264_4K2K: |
| if (!strcmp(name, "single_core")) |
| format = VIDEO_DEC_H264_4k2K_SINGLE; |
| else |
| format = VIDEO_DEC_H264_4k2K; |
| break; |
| |
| case VFORMAT_H264MVC: |
| format = VIDEO_DEC_H264_MVC; |
| break; |
| |
| case VFORMAT_H264: |
| if (!strcmp(name, "mh264")) |
| format = VIDEO_DEC_H264_MULTI; |
| else if (!strcmp(name, "mh264_mmu")) { |
| format = VIDEO_DEC_H264_MULTI_MMU; |
| vdec = OPTEE_VDEC_HEVC; |
| } else |
| format = VIDEO_DEC_H264; |
| break; |
| |
| default: |
| pr_info("Unknow vdec format!\n"); |
| break; |
| } |
| |
| if (format < FIRMWARE_MAX) { |
| if (is_swap) |
| ret = tee_load_video_fw_swap(format, vdec, is_swap); |
| else |
| ret = tee_load_video_fw(format, vdec); |
| } |
| |
| __putname(name); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(optee_load_fw); |
| |
| s32 amvdec_loadmc_ex(enum vformat_e type, const char *name, char *def) |
| { |
| if (tee_enabled()) |
| return optee_load_fw(type, name); |
| else |
| return am_loadmc_ex(type, name, def, &amvdec_loadmc); |
| } |
| EXPORT_SYMBOL(amvdec_loadmc_ex); |
| |
| s32 amvdec_vdec_loadmc_ex(enum vformat_e type, const char *name, |
| struct vdec_s *vdec, char *def) |
| { |
| if (tee_enabled()) |
| return optee_load_fw(type, name); |
| else |
| return am_vdec_loadmc_ex(vdec, name, def, &amvdec_loadmc); |
| } |
| EXPORT_SYMBOL(amvdec_vdec_loadmc_ex); |
| |
| s32 amvdec_vdec_loadmc_buf_ex(enum vformat_e type, const char *name, |
| struct vdec_s *vdec, char *buf, int size) |
| { |
| if (tee_enabled()) |
| return optee_load_fw(type, name); |
| else |
| return am_vdec_loadmc_buf_ex(vdec, buf, size, &amvdec_loadmc); |
| } |
| EXPORT_SYMBOL(amvdec_vdec_loadmc_buf_ex); |
| |
| static s32 amvdec2_loadmc(const u32 *p) |
| { |
| if (has_vdec2()) { |
| ulong timeout; |
| s32 ret = 0; |
| |
| #ifdef AMVDEC_USE_STATIC_MEMORY |
| if (mc_addr == NULL) { |
| #else |
| { |
| #endif |
| mc_addr = kmalloc(MC_SIZE, GFP_KERNEL); |
| } |
| |
| if (!mc_addr) |
| return -ENOMEM; |
| |
| memcpy(mc_addr, p, MC_SIZE); |
| |
| mc_addr_map = dma_map_single(get_vdec_device(), |
| mc_addr, MC_SIZE, DMA_TO_DEVICE); |
| |
| WRITE_VREG(VDEC2_MPSR, 0); |
| WRITE_VREG(VDEC2_CPSR, 0); |
| |
| /* Read CBUS register for timing */ |
| timeout = READ_VREG(VDEC2_MPSR); |
| timeout = READ_VREG(VDEC2_MPSR); |
| |
| timeout = jiffies + HZ; |
| |
| WRITE_VREG(VDEC2_IMEM_DMA_ADR, mc_addr_map); |
| WRITE_VREG(VDEC2_IMEM_DMA_COUNT, 0x1000); |
| WRITE_VREG(VDEC2_IMEM_DMA_CTRL, (0x8000 | (7 << 16))); |
| |
| while (READ_VREG(VDEC2_IMEM_DMA_CTRL) & 0x8000) { |
| if (time_before(jiffies, timeout)) |
| schedule(); |
| else { |
| pr_err("vdec2 load mc error\n"); |
| ret = -EBUSY; |
| break; |
| } |
| } |
| |
| dma_unmap_single(get_vdec_device(), |
| mc_addr_map, MC_SIZE, DMA_TO_DEVICE); |
| |
| #ifndef AMVDEC_USE_STATIC_MEMORY |
| kfree(mc_addr); |
| mc_addr = NULL; |
| #endif |
| |
| return ret; |
| } else |
| return 0; |
| } |
| |
| s32 amvdec2_loadmc_ex(enum vformat_e type, const char *name, char *def) |
| { |
| if (has_vdec2()) |
| return am_loadmc_ex(type, name, def, &amvdec2_loadmc); |
| else |
| return 0; |
| } |
| EXPORT_SYMBOL(amvdec2_loadmc_ex); |
| |
| s32 amhcodec_loadmc(const u32 *p) |
| { |
| #ifdef AMVDEC_USE_STATIC_MEMORY |
| if (mc_addr == NULL) { |
| #else |
| { |
| #endif |
| mc_addr = kmalloc(MC_SIZE, GFP_KERNEL); |
| } |
| |
| if (!mc_addr) |
| return -ENOMEM; |
| |
| memcpy(mc_addr, p, MC_SIZE); |
| |
| mc_addr_map = dma_map_single(get_vdec_device(), |
| mc_addr, MC_SIZE, DMA_TO_DEVICE); |
| |
| WRITE_VREG(HCODEC_IMEM_DMA_ADR, mc_addr_map); |
| WRITE_VREG(HCODEC_IMEM_DMA_COUNT, 0x100); |
| WRITE_VREG(HCODEC_IMEM_DMA_CTRL, (0x8000 | (7 << 16))); |
| |
| while (READ_VREG(HCODEC_IMEM_DMA_CTRL) & 0x8000) |
| udelay(1000); |
| |
| dma_unmap_single(get_vdec_device(), |
| mc_addr_map, MC_SIZE, DMA_TO_DEVICE); |
| |
| #ifndef AMVDEC_USE_STATIC_MEMORY |
| kfree(mc_addr); |
| #endif |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(amhcodec_loadmc); |
| |
| s32 amhcodec_loadmc_ex(enum vformat_e type, const char *name, char *def) |
| { |
| return am_loadmc_ex(type, name, def, &amhcodec_loadmc); |
| } |
| EXPORT_SYMBOL(amhcodec_loadmc_ex); |
| |
| static s32 amhevc_loadmc(const u32 *p) |
| { |
| ulong timeout; |
| s32 ret = 0; |
| |
| if (has_hevc_vdec()) { |
| #ifdef AMVDEC_USE_STATIC_MEMORY |
| if (mc_addr == NULL) { |
| #else |
| { |
| #endif |
| mc_addr = kmalloc(MC_SIZE, GFP_KERNEL); |
| } |
| |
| if (!mc_addr) |
| return -ENOMEM; |
| |
| memcpy(mc_addr, p, MC_SIZE); |
| |
| mc_addr_map = |
| dma_map_single(get_vdec_device(), |
| mc_addr, MC_SIZE, DMA_TO_DEVICE); |
| |
| WRITE_VREG(HEVC_MPSR, 0); |
| WRITE_VREG(HEVC_CPSR, 0); |
| |
| /* Read CBUS register for timing */ |
| timeout = READ_VREG(HEVC_MPSR); |
| timeout = READ_VREG(HEVC_MPSR); |
| |
| timeout = jiffies + HZ; |
| |
| WRITE_VREG(HEVC_IMEM_DMA_ADR, mc_addr_map); |
| WRITE_VREG(HEVC_IMEM_DMA_COUNT, 0x1000); |
| WRITE_VREG(HEVC_IMEM_DMA_CTRL, (0x8000 | (7 << 16))); |
| |
| while (READ_VREG(HEVC_IMEM_DMA_CTRL) & 0x8000) { |
| if (time_before(jiffies, timeout)) |
| schedule(); |
| else { |
| pr_err("vdec2 load mc error\n"); |
| ret = -EBUSY; |
| break; |
| } |
| } |
| |
| dma_unmap_single(get_vdec_device(), |
| mc_addr_map, MC_SIZE, DMA_TO_DEVICE); |
| |
| #ifndef AMVDEC_USE_STATIC_MEMORY |
| kfree(mc_addr); |
| mc_addr = NULL; |
| #endif |
| } |
| |
| return ret; |
| } |
| |
| s32 amhevc_loadmc_ex(enum vformat_e type, const char *name, char *def) |
| { |
| if (has_hevc_vdec()) |
| if (tee_enabled()) |
| return optee_load_fw(type, name); |
| else |
| return am_loadmc_ex(type, name, def, &amhevc_loadmc); |
| else |
| return -1; |
| } |
| EXPORT_SYMBOL(amhevc_loadmc_ex); |
| |
| s32 amhevc_vdec_loadmc_ex(enum vformat_e type, struct vdec_s *vdec, |
| const char *name, char *def) |
| { |
| if (has_hevc_vdec()) |
| if (tee_enabled()) |
| return optee_load_fw(type, name); |
| else |
| return am_vdec_loadmc_ex(vdec, name, def, &amhevc_loadmc); |
| else |
| return -1; |
| } |
| EXPORT_SYMBOL(amhevc_vdec_loadmc_ex); |
| |
| void amvdec_start(void) |
| { |
| #ifdef CONFIG_WAKELOCK |
| amvdec_wake_lock(); |
| #endif |
| |
| /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ |
| if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M6) { |
| READ_VREG(DOS_SW_RESET0); |
| READ_VREG(DOS_SW_RESET0); |
| READ_VREG(DOS_SW_RESET0); |
| |
| WRITE_VREG(DOS_SW_RESET0, (1 << 12) | (1 << 11)); |
| WRITE_VREG(DOS_SW_RESET0, 0); |
| |
| READ_VREG(DOS_SW_RESET0); |
| READ_VREG(DOS_SW_RESET0); |
| READ_VREG(DOS_SW_RESET0); |
| } else { |
| /* #else */ |
| /* additional cbus dummy register reading for timing control */ |
| READ_RESET_REG(RESET0_REGISTER); |
| READ_RESET_REG(RESET0_REGISTER); |
| READ_RESET_REG(RESET0_REGISTER); |
| READ_RESET_REG(RESET0_REGISTER); |
| |
| WRITE_RESET_REG(RESET0_REGISTER, RESET_VCPU | RESET_CCPU); |
| |
| READ_RESET_REG(RESET0_REGISTER); |
| READ_RESET_REG(RESET0_REGISTER); |
| READ_RESET_REG(RESET0_REGISTER); |
| } |
| /* #endif */ |
| |
| WRITE_VREG(MPSR, 0x0001); |
| } |
| EXPORT_SYMBOL(amvdec_start); |
| |
| void amvdec2_start(void) |
| { |
| if (has_vdec2()) { |
| #ifdef CONFIG_WAKELOCK |
| amvdec_wake_lock(); |
| #endif |
| |
| READ_VREG(DOS_SW_RESET2); |
| READ_VREG(DOS_SW_RESET2); |
| READ_VREG(DOS_SW_RESET2); |
| |
| WRITE_VREG(DOS_SW_RESET2, (1 << 12) | (1 << 11)); |
| WRITE_VREG(DOS_SW_RESET2, 0); |
| |
| READ_VREG(DOS_SW_RESET2); |
| READ_VREG(DOS_SW_RESET2); |
| READ_VREG(DOS_SW_RESET2); |
| |
| WRITE_VREG(VDEC2_MPSR, 0x0001); |
| } |
| } |
| EXPORT_SYMBOL(amvdec2_start); |
| |
| void amhcodec_start(void) |
| { |
| WRITE_VREG(HCODEC_MPSR, 0x0001); |
| } |
| EXPORT_SYMBOL(amhcodec_start); |
| |
| void amhevc_start(void) |
| { |
| |
| if (has_hevc_vdec()) { |
| #ifdef CONFIG_WAKELOCK |
| amvdec_wake_lock(); |
| #endif |
| |
| READ_VREG(DOS_SW_RESET3); |
| READ_VREG(DOS_SW_RESET3); |
| READ_VREG(DOS_SW_RESET3); |
| |
| WRITE_VREG(DOS_SW_RESET3, (1 << 12) | (1 << 11)); |
| WRITE_VREG(DOS_SW_RESET3, 0); |
| |
| READ_VREG(DOS_SW_RESET3); |
| READ_VREG(DOS_SW_RESET3); |
| READ_VREG(DOS_SW_RESET3); |
| |
| WRITE_VREG(HEVC_MPSR, 0x0001); |
| } |
| } |
| EXPORT_SYMBOL(amhevc_start); |
| |
| void amvdec_stop(void) |
| { |
| ulong timeout = jiffies + HZ/10; |
| |
| WRITE_VREG(MPSR, 0); |
| WRITE_VREG(CPSR, 0); |
| |
| while (READ_VREG(IMEM_DMA_CTRL) & 0x8000) { |
| if (time_after(jiffies, timeout)) |
| break; |
| } |
| |
| timeout = jiffies + HZ/10; |
| while (READ_VREG(LMEM_DMA_CTRL) & 0x8000) { |
| if (time_after(jiffies, timeout)) |
| break; |
| } |
| |
| /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ |
| if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M6) { |
| READ_VREG(DOS_SW_RESET0); |
| READ_VREG(DOS_SW_RESET0); |
| READ_VREG(DOS_SW_RESET0); |
| |
| WRITE_VREG(DOS_SW_RESET0, (1 << 12) | (1 << 11)); |
| WRITE_VREG(DOS_SW_RESET0, 0); |
| |
| READ_VREG(DOS_SW_RESET0); |
| READ_VREG(DOS_SW_RESET0); |
| READ_VREG(DOS_SW_RESET0); |
| } else { |
| /* #else */ |
| WRITE_RESET_REG(RESET0_REGISTER, RESET_VCPU | RESET_CCPU); |
| |
| /* additional cbus dummy register reading for timing control */ |
| READ_RESET_REG(RESET0_REGISTER); |
| READ_RESET_REG(RESET0_REGISTER); |
| READ_RESET_REG(RESET0_REGISTER); |
| READ_RESET_REG(RESET0_REGISTER); |
| } |
| /* #endif */ |
| |
| #ifdef CONFIG_WAKELOCK |
| amvdec_wake_unlock(); |
| #endif |
| } |
| EXPORT_SYMBOL(amvdec_stop); |
| |
| void amvdec2_stop(void) |
| { |
| if (has_vdec2()) { |
| ulong timeout = jiffies + HZ/10; |
| |
| WRITE_VREG(VDEC2_MPSR, 0); |
| WRITE_VREG(VDEC2_CPSR, 0); |
| |
| while (READ_VREG(VDEC2_IMEM_DMA_CTRL) & 0x8000) { |
| if (time_after(jiffies, timeout)) |
| break; |
| } |
| |
| READ_VREG(DOS_SW_RESET2); |
| READ_VREG(DOS_SW_RESET2); |
| READ_VREG(DOS_SW_RESET2); |
| |
| #ifdef CONFIG_WAKELOCK |
| amvdec_wake_unlock(); |
| #endif |
| } |
| } |
| EXPORT_SYMBOL(amvdec2_stop); |
| |
| void amhcodec_stop(void) |
| { |
| WRITE_VREG(HCODEC_MPSR, 0); |
| } |
| EXPORT_SYMBOL(amhcodec_stop); |
| |
| void amhevc_stop(void) |
| { |
| if (has_hevc_vdec()) { |
| ulong timeout = jiffies + HZ/10; |
| |
| WRITE_VREG(HEVC_MPSR, 0); |
| WRITE_VREG(HEVC_CPSR, 0); |
| |
| while (READ_VREG(HEVC_IMEM_DMA_CTRL) & 0x8000) { |
| if (time_after(jiffies, timeout)) |
| break; |
| } |
| |
| timeout = jiffies + HZ/10; |
| while (READ_VREG(HEVC_LMEM_DMA_CTRL) & 0x8000) { |
| if (time_after(jiffies, timeout)) |
| break; |
| } |
| |
| READ_VREG(DOS_SW_RESET3); |
| READ_VREG(DOS_SW_RESET3); |
| READ_VREG(DOS_SW_RESET3); |
| |
| #ifdef CONFIG_WAKELOCK |
| amvdec_wake_unlock(); |
| #endif |
| } |
| } |
| EXPORT_SYMBOL(amhevc_stop); |
| |
| void amvdec_enable(void) |
| { |
| amvdec_pg_enable(true); |
| } |
| EXPORT_SYMBOL(amvdec_enable); |
| |
| void amvdec_disable(void) |
| { |
| amvdec_pg_enable(false); |
| } |
| EXPORT_SYMBOL(amvdec_disable); |
| |
| void amvdec2_enable(void) |
| { |
| if (has_vdec2()) |
| amvdec2_pg_enable(true); |
| } |
| EXPORT_SYMBOL(amvdec2_enable); |
| |
| void amvdec2_disable(void) |
| { |
| if (has_vdec2()) |
| amvdec2_pg_enable(false); |
| } |
| EXPORT_SYMBOL(amvdec2_disable); |
| |
| void amhevc_enable(void) |
| { |
| if (has_hevc_vdec()) |
| amhevc_pg_enable(true); |
| } |
| EXPORT_SYMBOL(amhevc_enable); |
| |
| void amhevc_disable(void) |
| { |
| if (has_hevc_vdec()) |
| amhevc_pg_enable(false); |
| } |
| EXPORT_SYMBOL(amhevc_disable); |
| |
| #ifdef CONFIG_PM |
| int amvdec_suspend(struct platform_device *dev, pm_message_t event) |
| { |
| amvdec_pg_enable(false); |
| |
| /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */ |
| if (has_vdec2()) |
| amvdec2_pg_enable(false); |
| /* #endif */ |
| |
| if (has_hevc_vdec()) |
| amhevc_pg_enable(false); |
| /*vdec_set_suspend_clk(1, 0);*//*DEBUG_TMP*/ |
| return 0; |
| } |
| EXPORT_SYMBOL(amvdec_suspend); |
| |
| int amvdec_resume(struct platform_device *dev) |
| { |
| amvdec_pg_enable(true); |
| |
| /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */ |
| if (has_vdec2()) |
| amvdec2_pg_enable(true); |
| /* #endif */ |
| |
| /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ |
| if (has_hevc_vdec()) |
| amhevc_pg_enable(true); |
| /* #endif */ |
| /*vdec_set_suspend_clk(0, 0);*//*DEBUG_TMP*/ |
| return 0; |
| } |
| EXPORT_SYMBOL(amvdec_resume); |
| |
| int amhevc_suspend(struct platform_device *dev, pm_message_t event) |
| { |
| if (has_hevc_vdec()) { |
| amhevc_pg_enable(false); |
| /*vdec_set_suspend_clk(1, 1);*//*DEBUG_TMP*/ |
| } |
| return 0; |
| } |
| EXPORT_SYMBOL(amhevc_suspend); |
| |
| int amhevc_resume(struct platform_device *dev) |
| { |
| if (has_hevc_vdec()) { |
| amhevc_pg_enable(true); |
| /*vdec_set_suspend_clk(0, 1);*//*DEBUG_TMP*/ |
| } |
| return 0; |
| } |
| EXPORT_SYMBOL(amhevc_resume); |
| |
| |
| #endif |
| |
| #ifdef CONFIG_WAKELOCK |
| |
| static int vdec_is_paused(void) |
| { |
| static unsigned long old_wp = -1, old_rp = -1, old_level = -1; |
| unsigned long wp, rp, level; |
| static int paused_time; |
| |
| /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ |
| if (has_hevc_vdec()) { |
| if ((vdec_on(VDEC_HEVC)) |
| && (READ_VREG(HEVC_STREAM_CONTROL) & 1)) { |
| wp = READ_VREG(HEVC_STREAM_WR_PTR); |
| rp = READ_VREG(HEVC_STREAM_RD_PTR); |
| level = READ_VREG(HEVC_STREAM_LEVEL); |
| } else { |
| wp = READ_VREG(VLD_MEM_VIFIFO_WP); |
| rp = READ_VREG(VLD_MEM_VIFIFO_RP); |
| level = READ_VREG(VLD_MEM_VIFIFO_LEVEL); |
| } |
| } else |
| /* #endif */ |
| { |
| wp = READ_VREG(VLD_MEM_VIFIFO_WP); |
| rp = READ_VREG(VLD_MEM_VIFIFO_RP); |
| level = READ_VREG(VLD_MEM_VIFIFO_LEVEL); |
| } |
| /*have data,but output buffer is full */ |
| if ((rp == old_rp && level > 1024) || |
| (rp == old_rp && wp == old_wp && level == old_level)) { |
| /*no write && not read */ |
| paused_time++; |
| } else { |
| paused_time = 0; |
| } |
| old_wp = wp; old_rp = rp; old_level = level; |
| if (paused_time > 10) |
| return 1; |
| return 0; |
| } |
| |
| int amvdev_pause(void) |
| { |
| video_running = 0; |
| video_stated_changed = 1; |
| return 0; |
| } |
| EXPORT_SYMBOL(amvdev_pause); |
| |
| int amvdev_resume(void) |
| { |
| video_running = 1; |
| video_stated_changed = 1; |
| return 0; |
| } |
| EXPORT_SYMBOL(amvdev_resume); |
| |
| static void vdec_paused_check_timer(unsigned long arg) |
| { |
| if (video_stated_changed) { |
| if (!video_running) { |
| if (vdec_is_paused()) { |
| pr_info("vdec paused and release wakelock now\n"); |
| amvdec_wake_unlock(); |
| video_stated_changed = 0; |
| } |
| } else { |
| amvdec_wake_lock(); |
| video_stated_changed = 0; |
| } |
| } |
| mod_timer(&amvdevtimer, jiffies + WAKE_CHECK_INTERVAL); |
| } |
| #else |
| int amvdev_pause(void) |
| { |
| return 0; |
| } |
| |
| int amvdev_resume(void) |
| { |
| return 0; |
| } |
| #endif |
| |
| int amvdec_init(void) |
| { |
| #ifdef CONFIG_WAKELOCK |
| /* |
| *wake_lock_init(&amvdec_lock, WAKE_LOCK_IDLE, "amvdec_lock"); |
| *tmp mark for compile, no "WAKE_LOCK_IDLE" definition in kernel 3.8 |
| */ |
| wake_lock_init(&amvdec_lock, /*WAKE_LOCK_IDLE */ WAKE_LOCK_SUSPEND, |
| "amvdec_lock"); |
| |
| init_timer(&amvdevtimer); |
| |
| amvdevtimer.data = (ulong) &amvdevtimer; |
| amvdevtimer.function = vdec_paused_check_timer; |
| #endif |
| return 0; |
| } |
| EXPORT_SYMBOL(amvdec_init); |
| |
| void amvdec_exit(void) |
| { |
| #ifdef CONFIG_WAKELOCK |
| del_timer_sync(&amvdevtimer); |
| #endif |
| } |
| EXPORT_SYMBOL(amvdec_exit); |
| |
| #if 0 |
| int __init amvdec_init(void) |
| { |
| #ifdef CONFIG_WAKELOCK |
| /* |
| *wake_lock_init(&amvdec_lock, WAKE_LOCK_IDLE, "amvdec_lock"); |
| *tmp mark for compile, no "WAKE_LOCK_IDLE" definition in kernel 3.8 |
| */ |
| wake_lock_init(&amvdec_lock, /*WAKE_LOCK_IDLE */ WAKE_LOCK_SUSPEND, |
| "amvdec_lock"); |
| |
| init_timer(&amvdevtimer); |
| |
| amvdevtimer.data = (ulong) &amvdevtimer; |
| amvdevtimer.function = vdec_paused_check_timer; |
| #endif |
| return 0; |
| } |
| |
| static void __exit amvdec_exit(void) |
| { |
| #ifdef CONFIG_WAKELOCK |
| del_timer_sync(&amvdevtimer); |
| #endif |
| } |
| |
| module_init(amvdec_init); |
| module_exit(amvdec_exit); |
| |
| |
| MODULE_DESCRIPTION("Amlogic Video Decoder Utility Driver"); |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>"); |
| #endif |