| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| /* Linux Headers */ |
| #include <linux/version.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/errno.h> |
| #include <linux/string.h> |
| #include <linux/io.h> |
| #include <linux/interrupt.h> |
| #include <linux/mutex.h> |
| #include <linux/platform_device.h> |
| #include <linux/fs.h> |
| #include <linux/slab.h> |
| #include <linux/ctype.h> |
| #include <linux/of.h> |
| #include <linux/clk.h> |
| #include <linux/delay.h> |
| #include <linux/of_device.h> |
| #ifdef CONFIG_AMLOGIC_VPU |
| #include <linux/amlogic/media/vpu/vpu.h> |
| #endif |
| #include <linux/amlogic/media/vout/vout_notify.h> |
| #include <linux/amlogic/gki_module.h> |
| |
| /* Local Headers */ |
| #include "vout_func.h" |
| #include "vout_reg.h" |
| |
| struct dummy_venc_data_s { |
| unsigned int vid_clk_ctrl_reg; |
| unsigned int vid_clk_ctrl2_reg; |
| unsigned int vid_clk_div_reg; |
| unsigned int vid2_clk_ctrl_reg; |
| unsigned int vid2_clk_div_reg; |
| |
| unsigned int projection_valid; |
| |
| void (*clktree_probe)(struct device *dev); |
| void (*clktree_remove)(struct device *dev); |
| void (*encp_clk_gate_switch)(int flag); |
| void (*enci_clk_gate_switch)(int flag); |
| void (*encl_clk_gate_switch)(int flag); |
| }; |
| |
| static struct dummy_venc_data_s *dummy_venc_data; |
| |
| struct dummy_venc_driver_s { |
| struct device *dev; |
| unsigned char status; |
| unsigned char vout_valid; |
| unsigned char viu_sel; |
| |
| unsigned char clk_gate_state; |
| |
| struct clk *top_gate; |
| struct clk *int_gate0; |
| struct clk *int_gate1; |
| #ifdef CONFIG_AMLOGIC_VPU |
| struct vpu_dev_s *vpu_dev; |
| #endif |
| }; |
| |
| static struct dummy_venc_driver_s *dummy_encp_drv; |
| static struct dummy_venc_driver_s *dummy_enci_drv; |
| static struct dummy_venc_driver_s *dummy_encl_drv; |
| |
| #define DUMMY_L_NAME "dummy_encl" |
| #define DUMMY_I_NAME "dummy_enci" |
| #define DUMMY_P_NAME "dummy_encp" |
| |
| /* ********************************************************** |
| * dummy_encp support |
| * ********************************************************** |
| */ |
| static unsigned int dummy_encp_index = 0xff; |
| static struct vinfo_s *dummy_encp_infos; |
| static struct vinfo_s dummy_encp_vinfo[] = { |
| { |
| .name = "dummy_p", |
| .mode = VMODE_DUMMY_ENCP, |
| .frac = 0, |
| .width = 720, |
| .height = 480, |
| .field_height = 480, |
| .aspect_ratio_num = 4, |
| .aspect_ratio_den = 3, |
| .sync_duration_num = 50, |
| .sync_duration_den = 1, |
| .video_clk = 25000000, |
| .htotal = 952, |
| .vtotal = 525, |
| .fr_adj_type = VOUT_FR_ADJ_NONE, |
| .viu_color_fmt = COLOR_FMT_YUV444, |
| .viu_mux = VIU_MUX_ENCP, |
| .vout_device = NULL, |
| }, |
| { |
| .name = "dummy_panel", |
| .mode = VMODE_DUMMY_ENCP, |
| .frac = 0, |
| .width = 1920, |
| .height = 1080, |
| .field_height = 1080, |
| .aspect_ratio_num = 16, |
| .aspect_ratio_den = 9, |
| .sync_duration_num = 60, |
| .sync_duration_den = 1, |
| .video_clk = 148500000, |
| .htotal = 2200, |
| .vtotal = 1125, |
| .fr_adj_type = VOUT_FR_ADJ_NONE, |
| .viu_color_fmt = COLOR_FMT_RGB444, |
| .viu_mux = VIU_MUX_ENCP, |
| .vout_device = NULL, |
| } |
| }; |
| |
| static struct vinfo_s dummy_encp_vinfo_t7[] = { |
| { |
| .name = "dummy_p", |
| .mode = VMODE_DUMMY_ENCP, |
| .frac = 0, |
| .width = 720, |
| .height = 480, |
| .field_height = 480, |
| .aspect_ratio_num = 4, |
| .aspect_ratio_den = 3, |
| .sync_duration_num = 50, |
| .sync_duration_den = 1, |
| .video_clk = 25000000, |
| .htotal = 952, |
| .vtotal = 525, |
| .fr_adj_type = VOUT_FR_ADJ_NONE, |
| .viu_color_fmt = COLOR_FMT_YUV444, |
| .viu_mux = VIU_MUX_ENCP, |
| .vout_device = NULL, |
| }, |
| {//no dummy_panel for t7/t3, use projection function in lcd driver |
| .name = "dummy_panel", |
| .mode = VMODE_DUMMY_ENCP, |
| .frac = 0, |
| .width = 1920, |
| .height = 1080, |
| .field_height = 1080, |
| .aspect_ratio_num = 16, |
| .aspect_ratio_den = 9, |
| .sync_duration_num = 60, |
| .sync_duration_den = 1, |
| .video_clk = 148500000, |
| .htotal = 2200, |
| .vtotal = 1125, |
| .fr_adj_type = VOUT_FR_ADJ_NONE, |
| .viu_color_fmt = COLOR_FMT_RGB444, |
| .viu_mux = VIU_MUX_PROJECT, |
| .vout_device = NULL, |
| } |
| }; |
| |
| static void dummy_encp_venc_set(void) |
| { |
| unsigned int temp; |
| |
| VOUTPR("%s: dummy_encp_index=%d\n", __func__, dummy_encp_index); |
| |
| if (dummy_encp_index == 1) { |
| vout_vcbus_write(ENCP_VIDEO_EN, 0); |
| |
| vout_vcbus_write(ENCP_VIDEO_MODE, 0x8000); |
| vout_vcbus_write(ENCP_VIDEO_MODE_ADV, 0x0418); |
| |
| temp = vout_vcbus_read(ENCL_VIDEO_MAX_PXCNT); |
| vout_vcbus_write(ENCP_VIDEO_MAX_PXCNT, temp); |
| temp = vout_vcbus_read(ENCL_VIDEO_MAX_LNCNT); |
| vout_vcbus_write(ENCP_VIDEO_MAX_LNCNT, temp); |
| temp = vout_vcbus_read(ENCL_VIDEO_HAVON_BEGIN); |
| vout_vcbus_write(ENCP_VIDEO_HAVON_BEGIN, temp); |
| temp = vout_vcbus_read(ENCL_VIDEO_HAVON_END); |
| vout_vcbus_write(ENCP_VIDEO_HAVON_END, temp); |
| temp = vout_vcbus_read(ENCL_VIDEO_VAVON_BLINE); |
| vout_vcbus_write(ENCP_VIDEO_VAVON_BLINE, temp); |
| temp = vout_vcbus_read(ENCL_VIDEO_VAVON_ELINE); |
| vout_vcbus_write(ENCP_VIDEO_VAVON_ELINE, temp); |
| |
| temp = vout_vcbus_read(ENCL_VIDEO_HSO_BEGIN); |
| vout_vcbus_write(ENCP_VIDEO_HSO_BEGIN, temp); |
| temp = vout_vcbus_read(ENCL_VIDEO_HSO_END); |
| vout_vcbus_write(ENCP_VIDEO_HSO_END, temp); |
| temp = vout_vcbus_read(ENCL_VIDEO_VSO_BEGIN); |
| vout_vcbus_write(ENCP_VIDEO_VSO_BEGIN, temp); |
| temp = vout_vcbus_read(ENCL_VIDEO_VSO_END); |
| vout_vcbus_write(ENCP_VIDEO_VSO_END, temp); |
| temp = vout_vcbus_read(ENCL_VIDEO_VSO_BLINE); |
| vout_vcbus_write(ENCP_VIDEO_VSO_BLINE, temp); |
| temp = vout_vcbus_read(ENCL_VIDEO_VSO_ELINE); |
| vout_vcbus_write(ENCP_VIDEO_VSO_ELINE, temp); |
| |
| temp = vout_vcbus_read(ENCL_VIDEO_VSO_ELINE); |
| vout_vcbus_write(ENCP_VIDEO_RGBIN_CTRL, temp); |
| |
| vout_vcbus_write(ENCP_VIDEO_EN, 1); |
| } else { |
| vout_vcbus_write(ENCP_VIDEO_EN, 0); |
| |
| vout_vcbus_write(ENCP_VIDEO_MODE, 0x8000); |
| vout_vcbus_write(ENCP_VIDEO_MODE_ADV, 0x0418); |
| |
| vout_vcbus_write(ENCP_VIDEO_MAX_PXCNT, 951); |
| vout_vcbus_write(ENCP_VIDEO_MAX_LNCNT, 524); |
| vout_vcbus_write(ENCP_VIDEO_HAVON_BEGIN, 80); |
| vout_vcbus_write(ENCP_VIDEO_HAVON_END, 799); |
| vout_vcbus_write(ENCP_VIDEO_VAVON_BLINE, 22); |
| vout_vcbus_write(ENCP_VIDEO_VAVON_ELINE, 501); |
| |
| vout_vcbus_write(ENCP_VIDEO_HSO_BEGIN, 0); |
| vout_vcbus_write(ENCP_VIDEO_HSO_END, 20); |
| vout_vcbus_write(ENCP_VIDEO_VSO_BEGIN, 0); |
| vout_vcbus_write(ENCP_VIDEO_VSO_END, 0); |
| vout_vcbus_write(ENCP_VIDEO_VSO_BLINE, 0); |
| vout_vcbus_write(ENCP_VIDEO_VSO_ELINE, 5); |
| |
| vout_vcbus_write(ENCP_VIDEO_RGBIN_CTRL, 1); |
| |
| vout_vcbus_write(ENCP_VIDEO_EN, 1); |
| } |
| } |
| |
| static void dummy_encp_clk_ctrl(int flag) |
| { |
| unsigned int temp; |
| |
| if (!dummy_venc_data) |
| return; |
| |
| if (dummy_encp_index == 1) { |
| if (flag) { |
| temp = vout_clk_getb(dummy_venc_data->vid2_clk_div_reg, |
| ENCL_CLK_SEL, 4); |
| vout_clk_setb(dummy_venc_data->vid_clk_div_reg, |
| temp, ENCP_CLK_SEL, 4); |
| |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl2_reg, |
| 1, ENCP_GATE_VCLK, 1); |
| } else { |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl2_reg, |
| 0, ENCP_GATE_VCLK, 1); |
| } |
| } else { |
| if (flag) { |
| /* clk source sel: fckl_div5 */ |
| vout_clk_setb(dummy_venc_data->vid_clk_div_reg, |
| 0xf, VCLK_XD0, 8); |
| usleep_range(5, 6); |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl_reg, |
| 6, VCLK_CLK_IN_SEL, 3); |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl_reg, |
| 1, VCLK_EN0, 1); |
| usleep_range(5, 6); |
| vout_clk_setb(dummy_venc_data->vid_clk_div_reg, |
| 0, ENCP_CLK_SEL, 4); |
| vout_clk_setb(dummy_venc_data->vid_clk_div_reg, |
| 1, VCLK_XD_EN, 1); |
| usleep_range(5, 6); |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl_reg, |
| 1, VCLK_DIV1_EN, 1); |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl_reg, |
| 1, VCLK_SOFT_RST, 1); |
| usleep_range(10, 11); |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl_reg, |
| 0, VCLK_SOFT_RST, 1); |
| usleep_range(5, 6); |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl2_reg, |
| 1, ENCP_GATE_VCLK, 1); |
| } else { |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl2_reg, |
| 0, ENCP_GATE_VCLK, 1); |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl_reg, |
| 0, VCLK_DIV1_EN, 1); |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl_reg, |
| 0, VCLK_EN0, 1); |
| vout_clk_setb(dummy_venc_data->vid_clk_div_reg, |
| 0, VCLK_XD_EN, 1); |
| } |
| } |
| } |
| |
| static void dummy_encp_clk_gate_switch(int flag) |
| { |
| int ret = 0; |
| |
| if (flag) { |
| if (dummy_encp_drv->clk_gate_state) |
| return; |
| if (IS_ERR_OR_NULL(dummy_encp_drv->top_gate)) |
| ret |= (1 << 0); |
| else |
| clk_prepare_enable(dummy_encp_drv->top_gate); |
| if (IS_ERR_OR_NULL(dummy_encp_drv->int_gate0)) |
| ret |= (1 << 1); |
| else |
| clk_prepare_enable(dummy_encp_drv->int_gate0); |
| if (IS_ERR_OR_NULL(dummy_encp_drv->int_gate1)) |
| ret |= (1 << 2); |
| else |
| clk_prepare_enable(dummy_encp_drv->int_gate1); |
| dummy_encp_drv->clk_gate_state = 1; |
| } else { |
| if (dummy_encp_drv->clk_gate_state == 0) |
| return; |
| dummy_encp_drv->clk_gate_state = 0; |
| if (IS_ERR_OR_NULL(dummy_encp_drv->int_gate0)) |
| ret |= (1 << 1); |
| else |
| clk_disable_unprepare(dummy_encp_drv->int_gate0); |
| if (IS_ERR_OR_NULL(dummy_encp_drv->int_gate1)) |
| ret |= (1 << 2); |
| else |
| clk_disable_unprepare(dummy_encp_drv->int_gate1); |
| if (IS_ERR_OR_NULL(dummy_encp_drv->top_gate)) |
| ret |= (1 << 0); |
| else |
| clk_disable_unprepare(dummy_encp_drv->top_gate); |
| } |
| |
| if (ret) |
| VOUTERR("%s: ret=0x%x\n", __func__, ret); |
| } |
| |
| static void dummy_encp_vinfo_update(void) |
| { |
| unsigned int lcd_viu_sel = 0; |
| const struct vinfo_s *vinfo = NULL; |
| |
| /* only dummy_panel need update vinfo */ |
| if (dummy_encp_index == 0) |
| return; |
| |
| if (dummy_encp_drv->viu_sel == 1) { |
| vinfo = get_current_vinfo2(); |
| lcd_viu_sel = 2; |
| } else if (dummy_encp_drv->viu_sel == 2) { |
| vinfo = get_current_vinfo(); |
| lcd_viu_sel = 1; |
| } |
| |
| if (vinfo) { |
| if (vinfo->mode != VMODE_LCD) { |
| VOUTERR("display%d is not panel\n", lcd_viu_sel); |
| vinfo = NULL; |
| } |
| } |
| if (!vinfo) |
| return; |
| |
| if (!dummy_encp_infos) |
| return; |
| |
| dummy_encp_infos->width = vinfo->width; |
| dummy_encp_infos->height = vinfo->height; |
| dummy_encp_infos->field_height = vinfo->field_height; |
| dummy_encp_infos->aspect_ratio_num = vinfo->aspect_ratio_num; |
| dummy_encp_infos->aspect_ratio_den = vinfo->aspect_ratio_den; |
| dummy_encp_infos->sync_duration_num = vinfo->sync_duration_num; |
| dummy_encp_infos->sync_duration_den = vinfo->sync_duration_den; |
| dummy_encp_infos->video_clk = vinfo->video_clk; |
| dummy_encp_infos->htotal = vinfo->htotal; |
| dummy_encp_infos->vtotal = vinfo->vtotal; |
| dummy_encp_infos->viu_color_fmt = vinfo->viu_color_fmt; |
| } |
| |
| static struct vinfo_s *dummy_encp_get_current_info(void *data) |
| { |
| if (dummy_encp_index > 1) |
| return NULL; |
| |
| return dummy_encp_infos; |
| } |
| |
| static int dummy_encp_set_current_vmode(enum vmode_e mode, void *data) |
| { |
| if (dummy_encp_index > 1) |
| return -1; |
| |
| dummy_encp_vinfo_update(); |
| |
| #ifdef CONFIG_AMLOGIC_VPU |
| vpu_dev_clk_request(dummy_encp_drv->vpu_dev, |
| dummy_encp_vinfo[dummy_encp_index].video_clk); |
| vpu_dev_mem_power_on(dummy_encp_drv->vpu_dev); |
| #endif |
| if (dummy_venc_data && dummy_venc_data->encp_clk_gate_switch) |
| dummy_venc_data->encp_clk_gate_switch(1); |
| |
| dummy_encp_clk_ctrl(1); |
| dummy_encp_venc_set(); |
| |
| dummy_encp_drv->status = 1; |
| dummy_encp_drv->vout_valid = 1; |
| VOUTPR("%s finished\n", __func__); |
| |
| return 0; |
| } |
| |
| static enum vmode_e dummy_encp_validate_vmode(char *name, unsigned int frac, |
| void *data) |
| { |
| enum vmode_e vmode = VMODE_MAX; |
| int i; |
| |
| if (frac) |
| return VMODE_MAX; |
| |
| dummy_encp_infos = NULL; |
| if (dummy_venc_data && dummy_venc_data->projection_valid) { |
| for (i = 0; i < 2; i++) { |
| if (strcmp(dummy_encp_vinfo_t7[i].name, name) == 0) { |
| dummy_encp_infos = &dummy_encp_vinfo_t7[i]; |
| vmode = dummy_encp_vinfo_t7[i].mode; |
| dummy_encp_index = i; |
| break; |
| } |
| } |
| |
| } else { |
| for (i = 0; i < 2; i++) { |
| if (strcmp(dummy_encp_vinfo[i].name, name) == 0) { |
| dummy_encp_infos = &dummy_encp_vinfo[i]; |
| vmode = dummy_encp_vinfo[i].mode; |
| dummy_encp_index = i; |
| break; |
| } |
| } |
| } |
| |
| return vmode; |
| } |
| |
| static int dummy_encp_vmode_is_supported(enum vmode_e mode, void *data) |
| { |
| if ((mode & VMODE_MODE_BIT_MASK) == VMODE_DUMMY_ENCP) |
| return true; |
| |
| return false; |
| } |
| |
| static int dummy_encp_disable(enum vmode_e cur_vmod, void *data) |
| { |
| dummy_encp_drv->status = 0; |
| dummy_encp_drv->vout_valid = 0; |
| |
| dummy_encp_index = 0xff; |
| |
| vout_vcbus_write(ENCP_VIDEO_EN, 0); /* disable encp */ |
| dummy_encp_clk_ctrl(0); |
| if (dummy_venc_data && dummy_venc_data->encp_clk_gate_switch) |
| dummy_venc_data->encp_clk_gate_switch(0); |
| #ifdef CONFIG_AMLOGIC_VPU |
| vpu_dev_mem_power_down(dummy_encp_drv->vpu_dev); |
| vpu_dev_clk_release(dummy_encp_drv->vpu_dev); |
| #endif |
| |
| VOUTPR("%s finished\n", __func__); |
| return 0; |
| } |
| |
| static int dummy_encp_suspend(void *data) |
| { |
| dummy_encp_drv->status = 0; |
| |
| vout_vcbus_write(ENCP_VIDEO_EN, 0); /* disable encp */ |
| dummy_encp_clk_ctrl(0); |
| if (dummy_venc_data && dummy_venc_data->encp_clk_gate_switch) |
| dummy_venc_data->encp_clk_gate_switch(0); |
| #ifdef CONFIG_AMLOGIC_VPU |
| vpu_dev_mem_power_down(dummy_encp_drv->vpu_dev); |
| vpu_dev_clk_release(dummy_encp_drv->vpu_dev); |
| #endif |
| |
| VOUTPR("%s finished\n", __func__); |
| return 0; |
| } |
| |
| static int dummy_encp_resume(void *data) |
| { |
| dummy_encp_set_current_vmode(VMODE_DUMMY_ENCP, NULL); |
| VOUTPR("%s finished\n", __func__); |
| return 0; |
| } |
| |
| static int dummy_encp_vout_state; |
| static int dummy_encp_vout_set_state(int bit, void *data) |
| { |
| dummy_encp_vout_state |= (1 << bit); |
| dummy_encp_drv->viu_sel = bit; |
| return 0; |
| } |
| |
| static int dummy_encp_vout_clr_state(int bit, void *data) |
| { |
| dummy_encp_vout_state &= ~(1 << bit); |
| if (dummy_encp_drv->viu_sel == bit) |
| dummy_encp_drv->viu_sel = 0; |
| return 0; |
| } |
| |
| static int dummy_encp_vout_get_state(void *data) |
| { |
| return dummy_encp_vout_state; |
| } |
| |
| static struct vout_server_s dummy_encp_vout_server = { |
| .name = "dummy_encp_vout_server", |
| .op = { |
| .get_vinfo = dummy_encp_get_current_info, |
| .set_vmode = dummy_encp_set_current_vmode, |
| .validate_vmode = dummy_encp_validate_vmode, |
| .vmode_is_supported = dummy_encp_vmode_is_supported, |
| .disable = dummy_encp_disable, |
| .set_state = dummy_encp_vout_set_state, |
| .clr_state = dummy_encp_vout_clr_state, |
| .get_state = dummy_encp_vout_get_state, |
| .set_bist = NULL, |
| #ifdef CONFIG_PM |
| .vout_suspend = NULL, |
| .vout_resume = NULL, |
| #endif |
| }, |
| .data = NULL, |
| }; |
| |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| static struct vout_server_s dummy_encp_vout2_server = { |
| .name = "dummy_encp_vout2_server", |
| .op = { |
| .get_vinfo = dummy_encp_get_current_info, |
| .set_vmode = dummy_encp_set_current_vmode, |
| .validate_vmode = dummy_encp_validate_vmode, |
| .vmode_is_supported = dummy_encp_vmode_is_supported, |
| .disable = dummy_encp_disable, |
| .set_state = dummy_encp_vout_set_state, |
| .clr_state = dummy_encp_vout_clr_state, |
| .get_state = dummy_encp_vout_get_state, |
| .set_bist = NULL, |
| #ifdef CONFIG_PM |
| .vout_suspend = NULL, |
| .vout_resume = NULL, |
| #endif |
| }, |
| .data = NULL, |
| }; |
| #endif |
| |
| static void dummy_encp_vout_server_init(void) |
| { |
| vout_register_server(&dummy_encp_vout_server); |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| vout2_register_server(&dummy_encp_vout2_server); |
| #endif |
| } |
| |
| static void dummy_encp_vout_server_remove(void) |
| { |
| vout_unregister_server(&dummy_encp_vout_server); |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| vout2_unregister_server(&dummy_encp_vout2_server); |
| #endif |
| } |
| |
| /* ********************************************************** |
| * dummy_enci support |
| * ********************************************************** |
| */ |
| static struct vinfo_s dummy_enci_vinfo = { |
| .name = "dummy_i", |
| .mode = VMODE_DUMMY_ENCI, |
| .frac = 0, |
| .width = 720, |
| .height = 480, |
| .field_height = 240, |
| .aspect_ratio_num = 4, |
| .aspect_ratio_den = 3, |
| .sync_duration_num = 55, |
| .sync_duration_den = 1, |
| .video_clk = 25000000, |
| .htotal = 1716, |
| .vtotal = 525, |
| .fr_adj_type = VOUT_FR_ADJ_NONE, |
| .viu_color_fmt = COLOR_FMT_YUV444, |
| .viu_mux = VIU_MUX_ENCI, |
| .vout_device = NULL, |
| }; |
| |
| static void dummy_enci_venc_set(void) |
| { |
| vout_vcbus_write(ENCI_VIDEO_EN, 0); |
| |
| vout_vcbus_write(ENCI_CFILT_CTRL, 0x12); |
| vout_vcbus_write(ENCI_CFILT_CTRL2, 0x12); |
| vout_vcbus_write(ENCI_VIDEO_MODE, 0); |
| vout_vcbus_write(ENCI_VIDEO_MODE_ADV, 0); |
| vout_vcbus_write(ENCI_SYNC_HSO_BEGIN, 5); |
| vout_vcbus_write(ENCI_SYNC_HSO_END, 129); |
| vout_vcbus_write(ENCI_SYNC_VSO_EVNLN, 0x0003); |
| vout_vcbus_write(ENCI_SYNC_VSO_ODDLN, 0x0104); |
| vout_vcbus_write(ENCI_MACV_MAX_AMP, 0x810b); |
| vout_vcbus_write(VENC_VIDEO_PROG_MODE, 0xf0); |
| vout_vcbus_write(ENCI_VIDEO_MODE, 0x08); |
| vout_vcbus_write(ENCI_VIDEO_MODE_ADV, 0x26); |
| vout_vcbus_write(ENCI_VIDEO_SCH, 0x20); |
| vout_vcbus_write(ENCI_SYNC_MODE, 0x07); |
| vout_vcbus_write(ENCI_YC_DELAY, 0x333); |
| vout_vcbus_write(ENCI_VFIFO2VD_PIXEL_START, 0xe3); |
| vout_vcbus_write(ENCI_VFIFO2VD_PIXEL_END, 0x0683); |
| vout_vcbus_write(ENCI_VFIFO2VD_LINE_TOP_START, 0x12); |
| vout_vcbus_write(ENCI_VFIFO2VD_LINE_TOP_END, 0x102); |
| vout_vcbus_write(ENCI_VFIFO2VD_LINE_BOT_START, 0x13); |
| vout_vcbus_write(ENCI_VFIFO2VD_LINE_BOT_END, 0x103); |
| vout_vcbus_write(VENC_SYNC_ROUTE, 0); |
| vout_vcbus_write(ENCI_DBG_PX_RST, 0); |
| vout_vcbus_write(VENC_INTCTRL, 0x2); |
| vout_vcbus_write(ENCI_VFIFO2VD_CTL, 0x4e01); |
| vout_vcbus_write(VENC_UPSAMPLE_CTRL0, 0x0061); |
| vout_vcbus_write(VENC_UPSAMPLE_CTRL1, 0x4061); |
| vout_vcbus_write(VENC_UPSAMPLE_CTRL2, 0x5061); |
| vout_vcbus_write(ENCI_DACSEL_0, 0x0011); |
| vout_vcbus_write(ENCI_DACSEL_1, 0x11); |
| vout_vcbus_write(ENCI_VIDEO_EN, 1); |
| vout_vcbus_write(ENCI_VIDEO_SAT, 0x12); |
| vout_vcbus_write(ENCI_SYNC_ADJ, 0x9c00); |
| vout_vcbus_write(ENCI_VIDEO_CONT, 0x3); |
| } |
| |
| static void dummy_enci_clk_ctrl(int flag) |
| { |
| if (!dummy_venc_data) |
| return; |
| |
| if (flag) { |
| /* clk source sel: fckl_div5 */ |
| vout_clk_setb(dummy_venc_data->vid2_clk_div_reg, |
| 0xf, VCLK2_XD, 8); |
| usleep_range(5, 6); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 6, VCLK2_CLK_IN_SEL, 3); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 1, VCLK2_EN, 1); |
| usleep_range(5, 6); |
| |
| vout_clk_setb(dummy_venc_data->vid_clk_div_reg, |
| 8, ENCI_CLK_SEL, 4); |
| vout_clk_setb(dummy_venc_data->vid2_clk_div_reg, |
| 1, VCLK2_XD_EN, 1); |
| usleep_range(5, 6); |
| |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 1, VCLK2_DIV1_EN, 1); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 1, VCLK2_SOFT_RST, 1); |
| usleep_range(10, 11); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 0, VCLK2_SOFT_RST, 1); |
| usleep_range(5, 6); |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl2_reg, |
| 1, ENCI_GATE_VCLK, 1); |
| } else { |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl2_reg, |
| 0, ENCI_GATE_VCLK, 1); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 0, VCLK2_DIV1_EN, 1); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 0, VCLK2_EN, 1); |
| vout_clk_setb(dummy_venc_data->vid2_clk_div_reg, |
| 0, VCLK2_XD_EN, 1); |
| } |
| } |
| |
| static void dummy_enci_clk_gate_switch(int flag) |
| { |
| int ret = 0; |
| |
| if (flag) { |
| if (dummy_enci_drv->clk_gate_state) |
| return; |
| if (IS_ERR_OR_NULL(dummy_enci_drv->top_gate)) |
| ret |= (1 << 0); |
| else |
| clk_prepare_enable(dummy_enci_drv->top_gate); |
| if (IS_ERR_OR_NULL(dummy_enci_drv->int_gate0)) |
| ret |= (1 << 1); |
| else |
| clk_prepare_enable(dummy_enci_drv->int_gate0); |
| if (IS_ERR_OR_NULL(dummy_enci_drv->int_gate1)) |
| ret |= (1 << 2); |
| else |
| clk_prepare_enable(dummy_enci_drv->int_gate1); |
| dummy_enci_drv->clk_gate_state = 1; |
| } else { |
| if (dummy_enci_drv->clk_gate_state == 0) |
| return; |
| dummy_enci_drv->clk_gate_state = 0; |
| if (IS_ERR_OR_NULL(dummy_enci_drv->int_gate0)) |
| ret |= (1 << 1); |
| else |
| clk_disable_unprepare(dummy_enci_drv->int_gate0); |
| if (IS_ERR_OR_NULL(dummy_enci_drv->int_gate1)) |
| ret |= (1 << 2); |
| else |
| clk_disable_unprepare(dummy_enci_drv->int_gate1); |
| if (IS_ERR_OR_NULL(dummy_enci_drv->top_gate)) |
| ret |= (1 << 0); |
| else |
| clk_disable_unprepare(dummy_enci_drv->top_gate); |
| } |
| |
| if (ret) |
| VOUTERR("%s: ret=0x%x\n", __func__, ret); |
| } |
| |
| static struct vinfo_s *dummy_enci_get_current_info(void *data) |
| { |
| return &dummy_enci_vinfo; |
| } |
| |
| static int dummy_enci_set_current_vmode(enum vmode_e mode, void *data) |
| { |
| #ifdef CONFIG_AMLOGIC_VPU |
| vpu_dev_clk_request(dummy_enci_drv->vpu_dev, |
| dummy_enci_vinfo.video_clk); |
| vpu_dev_mem_power_on(dummy_enci_drv->vpu_dev); |
| #endif |
| if (dummy_venc_data && dummy_venc_data->enci_clk_gate_switch) |
| dummy_venc_data->enci_clk_gate_switch(1); |
| |
| dummy_enci_clk_ctrl(1); |
| dummy_enci_venc_set(); |
| |
| dummy_enci_drv->status = 1; |
| dummy_enci_drv->vout_valid = 1; |
| VOUTPR("%s finished\n", __func__); |
| |
| return 0; |
| } |
| |
| static enum vmode_e dummy_enci_validate_vmode(char *name, unsigned int frac, |
| void *data) |
| { |
| enum vmode_e vmode = VMODE_MAX; |
| |
| if (frac) |
| return VMODE_MAX; |
| |
| if (strcmp(dummy_enci_vinfo.name, name) == 0) |
| vmode = dummy_enci_vinfo.mode; |
| |
| return vmode; |
| } |
| |
| static int dummy_enci_vmode_is_supported(enum vmode_e mode, void *data) |
| { |
| if ((mode & VMODE_MODE_BIT_MASK) == VMODE_DUMMY_ENCI) |
| return true; |
| |
| return false; |
| } |
| |
| static int dummy_enci_disable(enum vmode_e cur_vmod, void *data) |
| { |
| dummy_enci_drv->status = 0; |
| dummy_enci_drv->vout_valid = 0; |
| |
| vout_vcbus_write(ENCI_VIDEO_EN, 0); /* disable encp */ |
| dummy_enci_clk_ctrl(0); |
| if (dummy_venc_data && dummy_venc_data->enci_clk_gate_switch) |
| dummy_venc_data->enci_clk_gate_switch(0); |
| #ifdef CONFIG_AMLOGIC_VPU |
| vpu_dev_mem_power_down(dummy_enci_drv->vpu_dev); |
| vpu_dev_clk_release(dummy_enci_drv->vpu_dev); |
| #endif |
| |
| VOUTPR("%s finished\n", __func__); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_PM |
| static int dummy_enci_suspend(void *data) |
| { |
| dummy_enci_disable(VMODE_DUMMY_ENCI, NULL); |
| VOUTPR("%s finished\n", __func__); |
| return 0; |
| } |
| |
| static int dummy_enci_resume(void *data) |
| { |
| dummy_enci_set_current_vmode(VMODE_DUMMY_ENCI, NULL); |
| VOUTPR("%s finished\n", __func__); |
| return 0; |
| } |
| #endif |
| |
| static int dummy_enci_vout_state; |
| static int dummy_enci_vout_set_state(int bit, void *data) |
| { |
| dummy_enci_vout_state |= (1 << bit); |
| dummy_enci_drv->viu_sel = bit; |
| return 0; |
| } |
| |
| static int dummy_enci_vout_clr_state(int bit, void *data) |
| { |
| dummy_enci_vout_state &= ~(1 << bit); |
| if (dummy_enci_drv->viu_sel == bit) |
| dummy_enci_drv->viu_sel = 0; |
| return 0; |
| } |
| |
| static int dummy_enci_vout_get_state(void *data) |
| { |
| return dummy_enci_vout_state; |
| } |
| |
| static struct vout_server_s dummy_enci_vout_server = { |
| .name = "dummy_enci_vout_server", |
| .op = { |
| .get_vinfo = dummy_enci_get_current_info, |
| .set_vmode = dummy_enci_set_current_vmode, |
| .validate_vmode = dummy_enci_validate_vmode, |
| .vmode_is_supported = dummy_enci_vmode_is_supported, |
| .disable = dummy_enci_disable, |
| .set_state = dummy_enci_vout_set_state, |
| .clr_state = dummy_enci_vout_clr_state, |
| .get_state = dummy_enci_vout_get_state, |
| .set_bist = NULL, |
| #ifdef CONFIG_PM |
| .vout_suspend = NULL, |
| .vout_resume = NULL, |
| #endif |
| }, |
| .data = NULL, |
| }; |
| |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| static struct vout_server_s dummy_enci_vout2_server = { |
| .name = "dummy_enci_vout2_server", |
| .op = { |
| .get_vinfo = dummy_enci_get_current_info, |
| .set_vmode = dummy_enci_set_current_vmode, |
| .validate_vmode = dummy_enci_validate_vmode, |
| .vmode_is_supported = dummy_enci_vmode_is_supported, |
| .disable = dummy_enci_disable, |
| .set_state = dummy_enci_vout_set_state, |
| .clr_state = dummy_enci_vout_clr_state, |
| .get_state = dummy_enci_vout_get_state, |
| .set_bist = NULL, |
| #ifdef CONFIG_PM |
| .vout_suspend = NULL, |
| .vout_resume = NULL, |
| #endif |
| }, |
| .data = NULL, |
| }; |
| #endif |
| |
| static void dummy_enci_vout_server_init(void) |
| { |
| vout_register_server(&dummy_enci_vout_server); |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| vout2_register_server(&dummy_enci_vout2_server); |
| #endif |
| } |
| |
| static void dummy_enci_vout_server_remove(void) |
| { |
| vout_unregister_server(&dummy_enci_vout_server); |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| vout2_unregister_server(&dummy_enci_vout2_server); |
| #endif |
| } |
| |
| /* ********************************************************** |
| * dummy_encl support |
| * ********************************************************** |
| */ |
| static struct vinfo_s dummy_encl_vinfo = { |
| .name = "dummy_l", |
| .mode = VMODE_DUMMY_ENCL, |
| .frac = 0, |
| .width = 720, |
| .height = 480, |
| .field_height = 480, |
| .aspect_ratio_num = 4, |
| .aspect_ratio_den = 3, |
| .sync_duration_num = 50, |
| .sync_duration_den = 1, |
| .video_clk = 25000000, |
| .htotal = 952, |
| .vtotal = 525, |
| .viu_color_fmt = COLOR_FMT_RGB444, |
| .viu_mux = VIU_MUX_ENCL, |
| .vout_device = NULL, |
| }; |
| |
| static void dummy_encl_venc_set(void) |
| { |
| vout_vcbus_write(ENCL_VIDEO_EN, 0); |
| |
| vout_vcbus_write(ENCL_VIDEO_MODE, 0x8000); |
| vout_vcbus_write(ENCL_VIDEO_MODE_ADV, 0x18); |
| vout_vcbus_write(ENCL_VIDEO_FILT_CTRL, 0x1000); |
| |
| vout_vcbus_write(ENCL_VIDEO_MAX_PXCNT, 951); |
| vout_vcbus_write(ENCL_VIDEO_MAX_LNCNT, 524); |
| vout_vcbus_write(ENCL_VIDEO_HAVON_BEGIN, 80); |
| vout_vcbus_write(ENCL_VIDEO_HAVON_END, 799); |
| vout_vcbus_write(ENCL_VIDEO_VAVON_BLINE, 22); |
| vout_vcbus_write(ENCL_VIDEO_VAVON_ELINE, 501); |
| |
| vout_vcbus_write(ENCL_VIDEO_HSO_BEGIN, 0); |
| vout_vcbus_write(ENCL_VIDEO_HSO_END, 20); |
| vout_vcbus_write(ENCL_VIDEO_VSO_BEGIN, 0); |
| vout_vcbus_write(ENCL_VIDEO_VSO_END, 0); |
| vout_vcbus_write(ENCL_VIDEO_VSO_BLINE, 0); |
| vout_vcbus_write(ENCL_VIDEO_VSO_ELINE, 5); |
| |
| vout_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, 3); |
| |
| vout_vcbus_write(ENCL_VIDEO_EN, 1); |
| } |
| |
| static void dummy_encl_clk_ctrl(int flag) |
| { |
| if (!dummy_venc_data) |
| return; |
| |
| if (flag) { |
| /* clk source sel: fckl_div5 */ |
| vout_clk_setb(dummy_venc_data->vid2_clk_div_reg, |
| 0xf, VCLK2_XD, 8); |
| usleep_range(5, 6); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 6, VCLK2_CLK_IN_SEL, 3); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 1, VCLK2_EN, 1); |
| usleep_range(5, 6); |
| vout_clk_setb(dummy_venc_data->vid2_clk_div_reg, |
| 8, ENCL_CLK_SEL, 4); |
| vout_clk_setb(dummy_venc_data->vid2_clk_div_reg, |
| 1, VCLK2_XD_EN, 1); |
| usleep_range(5, 6); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 1, VCLK2_DIV1_EN, 1); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 1, VCLK2_SOFT_RST, 1); |
| usleep_range(10, 11); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 0, VCLK2_SOFT_RST, 1); |
| usleep_range(5, 6); |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl2_reg, |
| 1, ENCL_GATE_VCLK, 1); |
| } else { |
| vout_clk_setb(dummy_venc_data->vid_clk_ctrl2_reg, |
| 0, ENCL_GATE_VCLK, 1); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 0, VCLK2_DIV1_EN, 1); |
| vout_clk_setb(dummy_venc_data->vid2_clk_ctrl_reg, |
| 0, VCLK2_EN, 1); |
| vout_clk_setb(dummy_venc_data->vid2_clk_div_reg, |
| 0, VCLK2_XD_EN, 1); |
| } |
| } |
| |
| static void dummy_encl_clk_gate_switch(int flag) |
| { |
| int ret = 0; |
| |
| if (flag) { |
| if (dummy_encl_drv->clk_gate_state) |
| return; |
| if (IS_ERR_OR_NULL(dummy_encl_drv->top_gate)) |
| ret |= (1 << 0); |
| else |
| clk_prepare_enable(dummy_encl_drv->top_gate); |
| if (IS_ERR_OR_NULL(dummy_encl_drv->int_gate0)) |
| ret |= (1 << 1); |
| else |
| clk_prepare_enable(dummy_encl_drv->int_gate0); |
| dummy_encl_drv->clk_gate_state = 1; |
| } else { |
| if (dummy_encl_drv->clk_gate_state == 0) |
| return; |
| dummy_encl_drv->clk_gate_state = 0; |
| if (IS_ERR_OR_NULL(dummy_encl_drv->int_gate0)) |
| ret |= (1 << 1); |
| else |
| clk_disable_unprepare(dummy_encl_drv->int_gate0); |
| if (IS_ERR_OR_NULL(dummy_encl_drv->top_gate)) |
| ret |= (1 << 0); |
| else |
| clk_disable_unprepare(dummy_encl_drv->top_gate); |
| } |
| |
| if (ret) |
| VOUTERR("%s: ret=0x%x\n", __func__, ret); |
| } |
| |
| static struct vinfo_s *dummy_encl_get_current_info(void *data) |
| { |
| return &dummy_encl_vinfo; |
| } |
| |
| static int dummy_encl_set_current_vmode(enum vmode_e mode, void *data) |
| { |
| #ifdef CONFIG_AMLOGIC_VPU |
| vpu_dev_clk_request(dummy_encl_drv->vpu_dev, |
| dummy_encl_vinfo.video_clk); |
| vpu_dev_mem_power_on(dummy_encl_drv->vpu_dev); |
| #endif |
| if (dummy_venc_data && dummy_venc_data->encl_clk_gate_switch) |
| dummy_venc_data->encl_clk_gate_switch(1); |
| |
| dummy_encl_clk_ctrl(1); |
| dummy_encl_venc_set(); |
| |
| dummy_encl_drv->status = 1; |
| dummy_encl_drv->vout_valid = 1; |
| VOUTPR("%s finished\n", __func__); |
| |
| return 0; |
| } |
| |
| static enum vmode_e dummy_encl_validate_vmode(char *name, unsigned int frac, |
| void *data) |
| { |
| enum vmode_e vmode = VMODE_MAX; |
| |
| if (frac) |
| return VMODE_MAX; |
| |
| if (strcmp(dummy_encl_vinfo.name, name) == 0) |
| vmode = dummy_encl_vinfo.mode; |
| |
| return vmode; |
| } |
| |
| static int dummy_encl_vmode_is_supported(enum vmode_e mode, void *data) |
| { |
| if ((mode & VMODE_MODE_BIT_MASK) == VMODE_DUMMY_ENCL) |
| return true; |
| |
| return false; |
| } |
| |
| static int dummy_encl_disable(enum vmode_e cur_vmod, void *data) |
| { |
| dummy_encl_drv->status = 0; |
| dummy_encl_drv->vout_valid = 0; |
| |
| vout_vcbus_write(ENCL_VIDEO_EN, 0); |
| dummy_encl_clk_ctrl(0); |
| if (dummy_venc_data && dummy_venc_data->encl_clk_gate_switch) |
| dummy_venc_data->encl_clk_gate_switch(0); |
| #ifdef CONFIG_AMLOGIC_VPU |
| vpu_dev_mem_power_down(dummy_encl_drv->vpu_dev); |
| vpu_dev_clk_release(dummy_encl_drv->vpu_dev); |
| #endif |
| |
| VOUTPR("%s finished\n", __func__); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_PM |
| static int dummy_encl_suspend(void *data) |
| { |
| dummy_encl_disable(VMODE_DUMMY_ENCL, NULL); |
| VOUTPR("%s finished\n", __func__); |
| return 0; |
| } |
| |
| static int dummy_encl_resume(void *data) |
| { |
| dummy_encl_set_current_vmode(VMODE_DUMMY_ENCL, NULL); |
| VOUTPR("%s finished\n", __func__); |
| return 0; |
| } |
| #endif |
| |
| static int dummy_encl_vout_state; |
| static int dummy_encl_vout_set_state(int bit, void *data) |
| { |
| dummy_encl_vout_state |= (1 << bit); |
| dummy_encl_drv->viu_sel = bit; |
| return 0; |
| } |
| |
| static int dummy_encl_vout_clr_state(int bit, void *data) |
| { |
| dummy_encl_vout_state &= ~(1 << bit); |
| if (dummy_encl_drv->viu_sel == bit) |
| dummy_encl_drv->viu_sel = 0; |
| return 0; |
| } |
| |
| static int dummy_encl_vout_get_state(void *data) |
| { |
| return dummy_encl_vout_state; |
| } |
| |
| static struct vout_server_s dummy_encl_vout_server = { |
| .name = "dummy_encl_vout_server", |
| .op = { |
| .get_vinfo = dummy_encl_get_current_info, |
| .set_vmode = dummy_encl_set_current_vmode, |
| .validate_vmode = dummy_encl_validate_vmode, |
| .vmode_is_supported = dummy_encl_vmode_is_supported, |
| .disable = dummy_encl_disable, |
| .set_state = dummy_encl_vout_set_state, |
| .clr_state = dummy_encl_vout_clr_state, |
| .get_state = dummy_encl_vout_get_state, |
| .set_bist = NULL, |
| #ifdef CONFIG_PM |
| .vout_suspend = NULL, |
| .vout_resume = NULL, |
| #endif |
| }, |
| .data = NULL, |
| }; |
| |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| static struct vout_server_s dummy_encl_vout2_server = { |
| .name = "dummy_encl_vout2_server", |
| .op = { |
| .get_vinfo = dummy_encl_get_current_info, |
| .set_vmode = dummy_encl_set_current_vmode, |
| .validate_vmode = dummy_encl_validate_vmode, |
| .vmode_is_supported = dummy_encl_vmode_is_supported, |
| .disable = dummy_encl_disable, |
| .set_state = dummy_encl_vout_set_state, |
| .clr_state = dummy_encl_vout_clr_state, |
| .get_state = dummy_encl_vout_get_state, |
| .set_bist = NULL, |
| #ifdef CONFIG_PM |
| .vout_suspend = NULL, |
| .vout_resume = NULL, |
| #endif |
| }, |
| .data = NULL, |
| }; |
| #endif |
| |
| static void dummy_encl_vout_server_init(void) |
| { |
| vout_register_server(&dummy_encl_vout_server); |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| vout2_register_server(&dummy_encl_vout2_server); |
| #endif |
| } |
| |
| static void dummy_encl_vout_server_remove(void) |
| { |
| vout_unregister_server(&dummy_encl_vout_server); |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| vout2_unregister_server(&dummy_encl_vout2_server); |
| #endif |
| } |
| |
| /* ********************************************************* */ |
| static int dummy_venc_vout_mode_update(void) |
| { |
| enum vmode_e mode = VMODE_DUMMY_ENCP; |
| |
| VOUTPR("%s\n", __func__); |
| |
| if (dummy_encp_drv->viu_sel == 1) |
| vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE_PRE, &mode); |
| else if (dummy_encp_drv->viu_sel == 2) |
| vout2_notifier_call_chain(VOUT_EVENT_MODE_CHANGE_PRE, &mode); |
| dummy_encp_set_current_vmode(mode, NULL); |
| if (dummy_encp_drv->viu_sel == 1) |
| vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE, &mode); |
| else if (dummy_encp_drv->viu_sel == 2) |
| vout2_notifier_call_chain(VOUT_EVENT_MODE_CHANGE, &mode); |
| |
| return 0; |
| } |
| |
| static int dummy_encp_vout_notify_callback(struct notifier_block *block, |
| unsigned long cmd, void *para) |
| { |
| const struct vinfo_s *vinfo; |
| |
| if (dummy_encp_drv->status == 0) |
| return 0; |
| if (dummy_encp_index == 0) |
| return 0; |
| |
| switch (cmd) { |
| case VOUT_EVENT_MODE_CHANGE: |
| vinfo = get_current_vinfo(); |
| if (!vinfo) |
| break; |
| if (vinfo->mode != VMODE_LCD) |
| break; |
| dummy_venc_vout_mode_update(); |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| static int dummy_encp_vout2_notify_callback(struct notifier_block *block, |
| unsigned long cmd, void *para) |
| { |
| const struct vinfo_s *vinfo; |
| |
| if (dummy_encp_drv->status == 0) |
| return 0; |
| if (dummy_encp_index == 0) |
| return 0; |
| |
| switch (cmd) { |
| case VOUT_EVENT_MODE_CHANGE: |
| vinfo = get_current_vinfo2(); |
| if (!vinfo) |
| break; |
| if (vinfo->mode != VMODE_LCD) |
| break; |
| dummy_venc_vout_mode_update(); |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| #endif |
| |
| static struct notifier_block dummy_encp_vout_notifier = { |
| .notifier_call = dummy_encp_vout_notify_callback, |
| }; |
| |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| static struct notifier_block dummy_encp_vout2_notifier = { |
| .notifier_call = dummy_encp_vout2_notify_callback, |
| }; |
| #endif |
| |
| /* ********************************************************* */ |
| static const char *dummy_venc_debug_usage_str = { |
| "Usage:\n" |
| " echo reg > /sys/class/dummy_venc/encp ; dump regs for encp\n" |
| " echo reg > /sys/class/dummy_venc/enci ; dump regs for enci\n" |
| " echo reg > /sys/class/dummy_venc/encl ; dump regs for encl\n" |
| }; |
| |
| static ssize_t dummy_venc_debug_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| return sprintf(buf, "%s\n", dummy_venc_debug_usage_str); |
| } |
| |
| static unsigned int dummy_encp_reg_dump[] = { |
| VPU_VIU_VENC_MUX_CTRL, |
| ENCP_VIDEO_EN, |
| ENCP_VIDEO_MODE, |
| ENCP_VIDEO_MODE_ADV, |
| ENCP_VIDEO_MAX_PXCNT, |
| ENCP_VIDEO_MAX_LNCNT, |
| ENCP_VIDEO_HAVON_BEGIN, |
| ENCP_VIDEO_HAVON_END, |
| ENCP_VIDEO_VAVON_BLINE, |
| ENCP_VIDEO_VAVON_ELINE, |
| ENCP_VIDEO_HSO_BEGIN, |
| ENCP_VIDEO_HSO_END, |
| ENCP_VIDEO_VSO_BEGIN, |
| ENCP_VIDEO_VSO_END, |
| ENCP_VIDEO_VSO_BLINE, |
| ENCP_VIDEO_VSO_ELINE, |
| ENCP_VIDEO_RGBIN_CTRL, |
| 0xffff |
| }; |
| |
| static unsigned int dummy_enci_reg_dump[] = { |
| VPU_VIU_VENC_MUX_CTRL, |
| ENCI_VIDEO_EN, |
| ENCI_CFILT_CTRL, |
| ENCI_CFILT_CTRL2, |
| ENCI_VIDEO_MODE, |
| ENCI_VIDEO_MODE_ADV, |
| ENCI_SYNC_HSO_BEGIN, |
| ENCI_SYNC_HSO_END, |
| ENCI_SYNC_VSO_EVNLN, |
| ENCI_SYNC_VSO_ODDLN, |
| ENCI_MACV_MAX_AMP, |
| VENC_VIDEO_PROG_MODE, |
| ENCI_VIDEO_SCH, |
| ENCI_SYNC_MODE, |
| ENCI_YC_DELAY, |
| ENCI_VFIFO2VD_PIXEL_START, |
| ENCI_VFIFO2VD_PIXEL_END, |
| ENCI_VFIFO2VD_LINE_TOP_START, |
| ENCI_VFIFO2VD_LINE_TOP_END, |
| ENCI_VFIFO2VD_LINE_BOT_START, |
| ENCI_VFIFO2VD_LINE_BOT_END, |
| VENC_SYNC_ROUTE, |
| ENCI_DBG_PX_RST, |
| VENC_INTCTRL, |
| ENCI_VFIFO2VD_CTL, |
| VENC_UPSAMPLE_CTRL0, |
| VENC_UPSAMPLE_CTRL1, |
| VENC_UPSAMPLE_CTRL2, |
| ENCI_DACSEL_0, |
| ENCI_DACSEL_1, |
| ENCI_VIDEO_SAT, |
| ENCI_SYNC_ADJ, |
| ENCI_VIDEO_CONT, |
| 0xffff |
| }; |
| |
| static unsigned int dummy_encl_reg_dump[] = { |
| VPU_VIU_VENC_MUX_CTRL, |
| ENCL_VIDEO_EN, |
| ENCL_VIDEO_MODE, |
| ENCL_VIDEO_MODE_ADV, |
| ENCL_VIDEO_MAX_PXCNT, |
| ENCL_VIDEO_MAX_LNCNT, |
| ENCL_VIDEO_HAVON_BEGIN, |
| ENCL_VIDEO_HAVON_END, |
| ENCL_VIDEO_VAVON_BLINE, |
| ENCL_VIDEO_VAVON_ELINE, |
| ENCL_VIDEO_HSO_BEGIN, |
| ENCL_VIDEO_HSO_END, |
| ENCL_VIDEO_VSO_BEGIN, |
| ENCL_VIDEO_VSO_END, |
| ENCL_VIDEO_VSO_BLINE, |
| ENCL_VIDEO_VSO_ELINE, |
| ENCL_VIDEO_RGBIN_CTRL, |
| 0xffff |
| }; |
| |
| static ssize_t dummy_encp_debug_store(struct class *class, |
| struct class_attribute *attr, |
| const char *buf, size_t count) |
| { |
| unsigned int i; |
| |
| switch (buf[0]) { |
| case 'r': |
| pr_info("dummy_encp register dump:\n"); |
| i = 0; |
| while (i < ARRAY_SIZE(dummy_encp_reg_dump)) { |
| if (dummy_encp_reg_dump[i] == 0xffff) |
| break; |
| pr_info("vcbus [0x%04x] = 0x%08x\n", |
| dummy_encp_reg_dump[i], |
| vout_vcbus_read(dummy_encp_reg_dump[i])); |
| i++; |
| } |
| break; |
| default: |
| pr_info("invalid data\n"); |
| break; |
| } |
| |
| return count; |
| } |
| |
| static ssize_t dummy_enci_debug_store(struct class *class, |
| struct class_attribute *attr, |
| const char *buf, size_t count) |
| { |
| unsigned int i; |
| |
| switch (buf[0]) { |
| case 'r': |
| pr_info("dummy_enci register dump:\n"); |
| i = 0; |
| while (i < ARRAY_SIZE(dummy_enci_reg_dump)) { |
| if (dummy_enci_reg_dump[i] == 0xffff) |
| break; |
| pr_info("vcbus [0x%04x] = 0x%08x\n", |
| dummy_enci_reg_dump[i], |
| vout_vcbus_read(dummy_enci_reg_dump[i])); |
| i++; |
| } |
| break; |
| default: |
| pr_info("invalid data\n"); |
| break; |
| } |
| |
| return count; |
| } |
| |
| static ssize_t dummy_encl_debug_store(struct class *class, |
| struct class_attribute *attr, |
| const char *buf, size_t count) |
| { |
| unsigned int i; |
| |
| switch (buf[0]) { |
| case 'r': |
| pr_info("dummy_encl register dump:\n"); |
| i = 0; |
| while (i < ARRAY_SIZE(dummy_encl_reg_dump)) { |
| if (dummy_encl_reg_dump[i] == 0xffff) |
| break; |
| pr_info("vcbus [0x%04x] = 0x%08x\n", |
| dummy_encl_reg_dump[i], |
| vout_vcbus_read(dummy_encl_reg_dump[i])); |
| i++; |
| } |
| break; |
| default: |
| pr_info("invalid data\n"); |
| break; |
| } |
| |
| return count; |
| } |
| |
| static struct class_attribute dummy_venc_class_attrs[] = { |
| __ATTR(encp, 0644, |
| dummy_venc_debug_show, dummy_encp_debug_store), |
| __ATTR(enci, 0644, |
| dummy_venc_debug_show, dummy_enci_debug_store), |
| __ATTR(encl, 0644, |
| dummy_venc_debug_show, dummy_encl_debug_store) |
| }; |
| |
| static struct class *debug_class; |
| static int dummy_venc_creat_class(void) |
| { |
| int i; |
| |
| debug_class = class_create(THIS_MODULE, "dummy_venc"); |
| if (IS_ERR(debug_class)) { |
| VOUTERR("create debug class failed\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(dummy_venc_class_attrs); i++) { |
| if (class_create_file(debug_class, |
| &dummy_venc_class_attrs[i])) { |
| VOUTERR("create debug attribute %s failed\n", |
| dummy_venc_class_attrs[i].attr.name); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int dummy_venc_remove_class(void) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(dummy_venc_class_attrs); i++) |
| class_remove_file(debug_class, &dummy_venc_class_attrs[i]); |
| |
| class_destroy(debug_class); |
| debug_class = NULL; |
| |
| return 0; |
| } |
| |
| /* ********************************************************* */ |
| static void dummy_venc_clktree_probe(struct device *dev) |
| { |
| int ret = 0; |
| |
| /* encp */ |
| dummy_encp_drv->clk_gate_state = 0; |
| |
| dummy_encp_drv->top_gate = devm_clk_get(dev, "encp_top_gate"); |
| if (IS_ERR_OR_NULL(dummy_encp_drv->top_gate)) |
| ret |= (1 << 0); |
| |
| dummy_encp_drv->int_gate0 = devm_clk_get(dev, "encp_int_gate0"); |
| if (IS_ERR_OR_NULL(dummy_encp_drv->int_gate0)) |
| ret |= (1 << 1); |
| |
| dummy_encp_drv->int_gate1 = devm_clk_get(dev, "encp_int_gate1"); |
| if (IS_ERR_OR_NULL(dummy_encp_drv->int_gate1)) |
| ret |= (1 << 2); |
| |
| /* enci */ |
| dummy_enci_drv->clk_gate_state = 0; |
| |
| dummy_enci_drv->top_gate = devm_clk_get(dev, "enci_top_gate"); |
| if (IS_ERR_OR_NULL(dummy_enci_drv->top_gate)) |
| ret |= (1 << 3); |
| |
| dummy_enci_drv->int_gate0 = devm_clk_get(dev, "enci_int_gate0"); |
| if (IS_ERR_OR_NULL(dummy_enci_drv->int_gate0)) |
| ret |= (1 << 4); |
| |
| dummy_enci_drv->int_gate1 = devm_clk_get(dev, "enci_int_gate1"); |
| if (IS_ERR_OR_NULL(dummy_enci_drv->int_gate1)) |
| ret |= (1 << 5); |
| |
| /* encl */ |
| dummy_encl_drv->clk_gate_state = 0; |
| |
| dummy_encl_drv->top_gate = devm_clk_get(dev, "encl_top_gate"); |
| if (IS_ERR_OR_NULL(dummy_encl_drv->top_gate)) |
| ret |= (1 << 6); |
| |
| dummy_encl_drv->int_gate0 = devm_clk_get(dev, "encl_int_gate"); |
| if (IS_ERR_OR_NULL(dummy_encl_drv->int_gate0)) |
| ret |= (1 << 7); |
| |
| if (ret) |
| VOUTERR("%s: ret=0x%x\n", __func__, ret); |
| } |
| |
| static void dummy_venc_clktree_remove(struct device *dev) |
| { |
| if (!IS_ERR_OR_NULL(dummy_encp_drv->top_gate)) |
| devm_clk_put(dev, dummy_encp_drv->top_gate); |
| if (!IS_ERR_OR_NULL(dummy_encp_drv->int_gate0)) |
| devm_clk_put(dev, dummy_encp_drv->int_gate0); |
| if (!IS_ERR_OR_NULL(dummy_encp_drv->int_gate1)) |
| devm_clk_put(dev, dummy_encp_drv->int_gate1); |
| |
| if (!IS_ERR_OR_NULL(dummy_enci_drv->top_gate)) |
| devm_clk_put(dev, dummy_enci_drv->top_gate); |
| if (!IS_ERR_OR_NULL(dummy_enci_drv->int_gate0)) |
| devm_clk_put(dev, dummy_enci_drv->int_gate0); |
| if (!IS_ERR_OR_NULL(dummy_enci_drv->int_gate1)) |
| devm_clk_put(dev, dummy_enci_drv->int_gate1); |
| |
| if (!IS_ERR_OR_NULL(dummy_encl_drv->top_gate)) |
| devm_clk_put(dev, dummy_encl_drv->top_gate); |
| if (!IS_ERR_OR_NULL(dummy_encl_drv->int_gate0)) |
| devm_clk_put(dev, dummy_encl_drv->int_gate0); |
| } |
| |
| static struct dummy_venc_data_s dummy_venc_match_data = { |
| .vid_clk_ctrl_reg = HHI_VID_CLK_CNTL, |
| .vid_clk_ctrl2_reg = HHI_VID_CLK_CNTL2, |
| .vid_clk_div_reg = HHI_VID_CLK_DIV, |
| .vid2_clk_ctrl_reg = HHI_VIID_CLK_CNTL, |
| .vid2_clk_div_reg = HHI_VIID_CLK_DIV, |
| |
| .projection_valid = 0, |
| |
| .clktree_probe = dummy_venc_clktree_probe, |
| .clktree_remove = dummy_venc_clktree_remove, |
| .encp_clk_gate_switch = dummy_encp_clk_gate_switch, |
| .enci_clk_gate_switch = dummy_enci_clk_gate_switch, |
| .encl_clk_gate_switch = dummy_encl_clk_gate_switch, |
| }; |
| |
| static struct dummy_venc_data_s dummy_venc_match_data_sc2 = { |
| .vid_clk_ctrl_reg = CLKCTRL_VID_CLK_CTRL, |
| .vid_clk_ctrl2_reg = CLKCTRL_VID_CLK_CTRL2, |
| .vid_clk_div_reg = CLKCTRL_VID_CLK_DIV, |
| .vid2_clk_ctrl_reg = CLKCTRL_VIID_CLK_CTRL, |
| .vid2_clk_div_reg = CLKCTRL_VIID_CLK_DIV, |
| |
| .projection_valid = 0, |
| |
| .clktree_probe = NULL, |
| .clktree_remove = NULL, |
| .encp_clk_gate_switch = NULL, |
| .enci_clk_gate_switch = NULL, |
| .encl_clk_gate_switch = NULL, |
| }; |
| |
| static struct dummy_venc_data_s dummy_venc_match_data_t7 = { |
| .vid_clk_ctrl_reg = CLKCTRL_VID_CLK0_CTRL, |
| .vid_clk_ctrl2_reg = CLKCTRL_VID_CLK0_CTRL2, |
| .vid_clk_div_reg = CLKCTRL_VID_CLK0_DIV, |
| .vid2_clk_ctrl_reg = CLKCTRL_VIID_CLK0_CTRL, |
| .vid2_clk_div_reg = CLKCTRL_VIID_CLK0_DIV, |
| |
| .projection_valid = 1, |
| |
| .clktree_probe = NULL, |
| .clktree_remove = NULL, |
| .encp_clk_gate_switch = NULL, |
| .enci_clk_gate_switch = NULL, |
| .encl_clk_gate_switch = NULL, |
| }; |
| |
| static const struct of_device_id dummy_venc_dt_match_table[] = { |
| { |
| .compatible = "amlogic, dummy_venc", |
| .data = &dummy_venc_match_data, |
| }, |
| { |
| .compatible = "amlogic, dummy_venc_sc2", |
| .data = &dummy_venc_match_data_sc2, |
| }, |
| { |
| .compatible = "amlogic, dummy_venc_s4", |
| .data = &dummy_venc_match_data_sc2, |
| }, |
| { |
| .compatible = "amlogic, dummy_venc_t7", |
| .data = &dummy_venc_match_data_t7, |
| }, |
| { |
| .compatible = "amlogic, dummy_venc_t3", |
| .data = &dummy_venc_match_data_t7, |
| }, |
| {} |
| }; |
| |
| static int dummy_venc_probe(struct platform_device *pdev) |
| { |
| const struct of_device_id *match; |
| |
| match = of_match_device(dummy_venc_dt_match_table, &pdev->dev); |
| if (!match) { |
| VOUTERR("%s: no match table\n", __func__); |
| return -1; |
| } |
| dummy_venc_data = (struct dummy_venc_data_s *)match->data; |
| if (!dummy_venc_data) { |
| VOUTERR("%s: no match data\n", __func__); |
| return -1; |
| } |
| |
| dummy_encp_drv = kzalloc(sizeof(*dummy_encp_drv), GFP_KERNEL); |
| if (!dummy_encp_drv) { |
| VOUTERR("%s: dummy_venc driver no enough memory\n", __func__); |
| return -ENOMEM; |
| } |
| dummy_encp_drv->dev = &pdev->dev; |
| #ifdef CONFIG_AMLOGIC_VPU |
| /*vpu dev register for encp*/ |
| dummy_encp_drv->vpu_dev = vpu_dev_register(VPU_VENCP, DUMMY_P_NAME); |
| #endif |
| |
| dummy_enci_drv = kzalloc(sizeof(*dummy_enci_drv), GFP_KERNEL); |
| if (!dummy_enci_drv) { |
| VOUTERR("%s: dummy_venc driver no enough memory\n", __func__); |
| kfree(dummy_encp_drv); |
| return -ENOMEM; |
| } |
| dummy_enci_drv->dev = &pdev->dev; |
| #ifdef CONFIG_AMLOGIC_VPU |
| /*vpu dev register for encp*/ |
| dummy_enci_drv->vpu_dev = vpu_dev_register(VPU_VENCI, DUMMY_I_NAME); |
| #endif |
| |
| dummy_encl_drv = kzalloc(sizeof(*dummy_encl_drv), GFP_KERNEL); |
| if (!dummy_encl_drv) { |
| VOUTERR("%s: dummy_venc driver no enough memory\n", __func__); |
| kfree(dummy_encp_drv); |
| kfree(dummy_enci_drv); |
| return -ENOMEM; |
| } |
| dummy_encl_drv->dev = &pdev->dev; |
| #ifdef CONFIG_AMLOGIC_VPU |
| /*vpu dev register for encp*/ |
| dummy_encl_drv->vpu_dev = vpu_dev_register(VPU_VENCL, DUMMY_L_NAME); |
| #endif |
| |
| if (dummy_venc_data->clktree_probe) |
| dummy_venc_data->clktree_probe(&pdev->dev); |
| |
| dummy_encp_vout_server_init(); |
| dummy_enci_vout_server_init(); |
| dummy_encl_vout_server_init(); |
| |
| /* only need for encp dummy_panel */ |
| vout_register_client(&dummy_encp_vout_notifier); |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| vout2_register_client(&dummy_encp_vout2_notifier); |
| #endif |
| |
| dummy_venc_creat_class(); |
| |
| VOUTPR("%s OK\n", __func__); |
| |
| return 0; |
| } |
| |
| static int dummy_venc_remove(struct platform_device *pdev) |
| { |
| if (!dummy_venc_data) |
| return 0; |
| if (!dummy_encp_drv) |
| return 0; |
| if (!dummy_enci_drv) |
| return 0; |
| if (!dummy_encl_drv) |
| return 0; |
| |
| dummy_venc_remove_class(); |
| vout_unregister_client(&dummy_encp_vout_notifier); |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| vout2_unregister_client(&dummy_encp_vout2_notifier); |
| #endif |
| dummy_encp_vout_server_remove(); |
| dummy_enci_vout_server_remove(); |
| dummy_encl_vout_server_remove(); |
| if (dummy_venc_data->clktree_remove) |
| dummy_venc_data->clktree_remove(&pdev->dev); |
| |
| kfree(dummy_encp_drv); |
| dummy_encp_drv = NULL; |
| kfree(dummy_enci_drv); |
| dummy_enci_drv = NULL; |
| kfree(dummy_encl_drv); |
| dummy_encl_drv = NULL; |
| |
| VOUTPR("%s\n", __func__); |
| return 0; |
| } |
| |
| static int dummy_venc_resume(struct platform_device *pdev) |
| { |
| if (dummy_encp_drv && dummy_encp_drv->vout_valid) |
| dummy_encp_resume(NULL); |
| if (dummy_enci_drv && dummy_enci_drv->vout_valid) |
| dummy_enci_resume(NULL); |
| if (dummy_encl_drv && dummy_encl_drv->vout_valid) |
| dummy_encl_resume(NULL); |
| |
| return 0; |
| } |
| |
| static int dummy_venc_suspend(struct platform_device *pdev, pm_message_t state) |
| { |
| if (dummy_encp_drv && dummy_encp_drv->status) |
| dummy_encp_suspend(NULL); |
| if (dummy_enci_drv && dummy_enci_drv->status) |
| dummy_enci_suspend(NULL); |
| if (dummy_encl_drv && dummy_encl_drv->status) |
| dummy_encl_suspend(NULL); |
| |
| return 0; |
| } |
| |
| static void dummy_venc_shutdown(struct platform_device *pdev) |
| { |
| if (!dummy_encp_drv) |
| return; |
| if (dummy_encp_drv->status) |
| dummy_encp_disable(VMODE_DUMMY_ENCP, NULL); |
| |
| if (!dummy_enci_drv) |
| return; |
| if (dummy_enci_drv->status) |
| dummy_enci_disable(VMODE_DUMMY_ENCI, NULL); |
| |
| if (!dummy_encl_drv) |
| return; |
| if (dummy_encl_drv->status) |
| dummy_encl_disable(VMODE_DUMMY_ENCL, NULL); |
| } |
| |
| static struct platform_driver dummy_venc_platform_driver = { |
| .probe = dummy_venc_probe, |
| .remove = dummy_venc_remove, |
| .suspend = dummy_venc_suspend, |
| .resume = dummy_venc_resume, |
| .shutdown = dummy_venc_shutdown, |
| .driver = { |
| .name = "dummy_venc", |
| .owner = THIS_MODULE, |
| #ifdef CONFIG_OF |
| .of_match_table = dummy_venc_dt_match_table, |
| #endif |
| }, |
| }; |
| |
| int __init dummy_venc_init(void) |
| { |
| if (platform_driver_register(&dummy_venc_platform_driver)) { |
| VOUTERR("failed to register dummy_venc driver module\n"); |
| return -ENODEV; |
| } |
| |
| return 0; |
| } |
| |
| void __exit dummy_venc_exit(void) |
| { |
| platform_driver_unregister(&dummy_venc_platform_driver); |
| } |
| |