| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| /* Standard Linux headers */ |
| #include <linux/types.h> |
| #include <linux/errno.h> |
| #include <linux/init.h> |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/slab.h> |
| #include <linux/interrupt.h> |
| #include <linux/fs.h> |
| #include <linux/device.h> |
| #include <linux/of_device.h> |
| #include <linux/cdev.h> |
| #include <linux/platform_device.h> |
| #include <linux/mm.h> |
| #include <linux/kthread.h> |
| #include <linux/vmalloc.h> |
| #include <linux/irq.h> |
| #include <linux/poll.h> |
| #include <linux/of_irq.h> |
| #include <linux/of.h> |
| #include <linux/uaccess.h> |
| #include <linux/delay.h> |
| #include "tvafe_avin_detect.h" |
| #include "tvafe.h" |
| #include "tvafe_regs.h" |
| #include "tvafe_debug.h" |
| #include "../tvin_global.h" |
| |
| #define TVAFE_AVIN_CH1_MASK BIT(0) |
| #define TVAFE_AVIN_CH2_MASK BIT(1) |
| #define TVAFE_AVIN_MASK (TVAFE_AVIN_CH1_MASK | TVAFE_AVIN_CH2_MASK) |
| #define TVAFE_MAX_AVIN_DEVICE_NUM 2 |
| #define TVAFE_AVIN_NAME "avin_detect" |
| #define TVAFE_AVIN_NAME_CH1 "tvafe_avin_detect_ch1" |
| #define TVAFE_AVIN_NAME_CH2 "tvafe_avin_detect_ch2" |
| |
| static unsigned int avin_detect_debug_print; |
| |
| #define AVIN_DETECT_INIT BIT(0) |
| #define AVIN_DETECT_OPEN BIT(1) |
| #define AVIN_DETECT_CH1_MASK BIT(2) |
| #define AVIN_DETECT_CH2_MASK BIT(3) |
| #define AVIN_DETECT_CH1_EN BIT(4) /* 0x2e[0] */ |
| #define AVIN_DETECT_CH1_SYNC_TIP BIT(5) /* 0x2e[3] */ |
| #define AVIN_DETECT_CH2_EN BIT(6) /* 0x2e[15] */ |
| #define AVIN_DETECT_CH2_SYNC_TIP BIT(7) /* 0x2e[18] */ |
| static unsigned int avin_detect_flag; |
| |
| /*0:670mv; 1:727mv; 2:777mv; 3:823mv; 4:865mv; 5:904mv; 6:940mv; 7:972mv*/ |
| static unsigned int dc_level_adj = 4; |
| |
| /*0:635mv; 1:686mv; 2:733mv; 3:776mv; 4:816mv; 5:853mv; 6:887mv; 7:919mv*/ |
| static unsigned int comp_level_adj; |
| |
| /*0:use internal VDC to bias CVBS_in*/ |
| /*1:use ground to bias CVBS_in*/ |
| static unsigned int detect_mode; |
| |
| /*0:460mv; 1:0.225mv*/ |
| static unsigned int vdc_level = 1; |
| |
| /*0:50mv; 1:100mv; 2:150mv; 3:200mv; 4:250mv; 6:300mv; 7:310mv*/ |
| static unsigned int sync_level = 1; |
| |
| static unsigned int avplay_sync_level = 6; |
| |
| /*0:50mv; 1:100mv; 2:150mv; 3:200mv*/ |
| static unsigned int sync_hys_adj = 1; |
| |
| static unsigned int irq_mode = 5; |
| |
| static unsigned int trigger_sel = 1; |
| |
| static unsigned int irq_edge_en = 1; |
| |
| static unsigned int irq_filter; |
| |
| static unsigned int irq_pol; |
| |
| static unsigned int avin_count_times = 5; |
| |
| static unsigned int avin_timer_time = 10;/*100ms*/ |
| |
| #define TVAFE_AVIN_INTERVAL (HZ / 100)/*10ms*/ |
| static struct timer_list avin_detect_timer; |
| static unsigned int s_irq_counter0; |
| static unsigned int s_irq_counter1; |
| static unsigned int s_irq_counter0_time; |
| static unsigned int s_irq_counter1_time; |
| static unsigned int s_counter0_last_state; |
| static unsigned int s_counter1_last_state; |
| |
| static struct tvafe_avin_det_s *avdev; |
| static struct meson_avin_data *meson_data; |
| static DECLARE_WAIT_QUEUE_HEAD(tvafe_avin_waitq); |
| |
| static unsigned int tvafe_avin_irq_reg_read(unsigned int reg) |
| { |
| unsigned int val; |
| |
| if (meson_data->irq_reg_base) |
| val = readl(meson_data->irq_reg_base + (reg << 2)); |
| else |
| val = aml_read_cbus(reg); |
| return val; |
| } |
| |
| static void tvafe_avin_irq_update_bit(unsigned int reg, |
| unsigned int mask, |
| unsigned int val) |
| { |
| unsigned int tmp, orig; |
| |
| if (meson_data->irq_reg_base) { |
| orig = tvafe_avin_irq_reg_read(reg); |
| |
| tmp = orig & ~mask; |
| tmp |= val & mask; |
| writel(tmp, meson_data->irq_reg_base + (reg << 2)); |
| } else { |
| aml_cbus_update_bits(reg, mask, val); |
| } |
| } |
| |
| static int tvafe_avin_dts_parse(struct platform_device *pdev) |
| { |
| int ret; |
| int i; |
| int value; |
| struct resource *res; |
| struct tvafe_avin_det_s *avdev; |
| |
| avdev = platform_get_drvdata(pdev); |
| |
| ret = of_property_read_u32(pdev->dev.of_node, |
| "device_mask", &value); |
| if (ret) { |
| tvafe_pr_info("Failed to get device_mask.\n"); |
| goto get_avin_param_failed; |
| } else { |
| avdev->dts_param.device_mask = value; |
| tvafe_pr_info("avin device_mask is 0x%x\n", |
| avdev->dts_param.device_mask); |
| if (avdev->dts_param.device_mask == TVAFE_AVIN_MASK) { |
| avin_detect_flag |= |
| (AVIN_DETECT_CH1_MASK | AVIN_DETECT_CH2_MASK); |
| avdev->device_num = 2; |
| ret = of_property_read_u32(pdev->dev.of_node, |
| "device_sequence", &value); |
| if (ret) { |
| avdev->dts_param.device_sequence = 0; |
| } else { |
| tvafe_pr_info("find device_sequence: %d\n", |
| value); |
| avdev->dts_param.device_sequence = value; |
| } |
| if (avdev->dts_param.device_sequence == 0) { |
| avdev->report_data_s[0].channel = |
| TVAFE_AVIN_CHANNEL1; |
| avdev->report_data_s[1].channel = |
| TVAFE_AVIN_CHANNEL2; |
| } else { |
| avdev->report_data_s[0].channel = |
| TVAFE_AVIN_CHANNEL2; |
| avdev->report_data_s[1].channel = |
| TVAFE_AVIN_CHANNEL1; |
| } |
| tvafe_pr_info("avin ch1:%d, ch2:%d\n", |
| avdev->report_data_s[0].channel, |
| avdev->report_data_s[1].channel); |
| } else if (avdev->dts_param.device_mask == |
| TVAFE_AVIN_CH1_MASK) { |
| avin_detect_flag |= AVIN_DETECT_CH1_MASK; |
| avdev->device_num = 1; |
| avdev->report_data_s[0].channel = TVAFE_AVIN_CHANNEL1; |
| avdev->report_data_s[1].channel = |
| TVAFE_AVIN_CHANNEL_MAX; |
| } else if (avdev->dts_param.device_mask == |
| TVAFE_AVIN_CH2_MASK) { |
| avin_detect_flag |= AVIN_DETECT_CH2_MASK; |
| avdev->device_num = 1; |
| avdev->report_data_s[0].channel = TVAFE_AVIN_CHANNEL2; |
| avdev->report_data_s[1].channel = |
| TVAFE_AVIN_CHANNEL_MAX; |
| } else { |
| avdev->device_num = 0; |
| avdev->report_data_s[0].channel = |
| TVAFE_AVIN_CHANNEL_MAX; |
| avdev->report_data_s[1].channel = |
| TVAFE_AVIN_CHANNEL_MAX; |
| tvafe_pr_info("device_mask 0x%x invalid\n", |
| avdev->dts_param.device_mask); |
| goto get_avin_param_failed; |
| } |
| avdev->report_data_s[0].status = TVAFE_AVIN_STATUS_UNKNOWN; |
| avdev->report_data_s[1].status = TVAFE_AVIN_STATUS_UNKNOWN; |
| } |
| /* get irq no*/ |
| for (i = 0; i < avdev->device_num; i++) { |
| res = platform_get_resource(pdev, IORESOURCE_IRQ, i); |
| if (!res) { |
| tvafe_pr_err("%s: can't get avin(%d) irq resource\n", |
| __func__, i); |
| goto fail_get_resource_irq; |
| } |
| avdev->dts_param.irq[i] = res->start; |
| } |
| return 0; |
| get_avin_param_failed: |
| fail_get_resource_irq: |
| return -EINVAL; |
| } |
| |
| void tvafe_avin_detect_ch1_anlog_enable(bool enable) |
| { |
| if ((avin_detect_flag & AVIN_DETECT_OPEN) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect is not opened\n", |
| __func__); |
| } |
| return; |
| } |
| if ((avin_detect_flag & AVIN_DETECT_CH1_MASK) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect ch1 is inactive\n", |
| __func__); |
| } |
| return; |
| } |
| if (avin_detect_debug_print) |
| tvafe_pr_info("%s: %d\n", __func__, enable); |
| |
| /*txlx,txhd,tl1 the same bit:0 for ch1 en detect*/ |
| if (enable) { |
| W_HIU_BIT(meson_data->detect_cntl, 1, |
| AFE_CH1_EN_DETECT_BIT, AFE_CH1_EN_DETECT_WIDTH); |
| avin_detect_flag |= AVIN_DETECT_CH1_EN; |
| } else { |
| W_HIU_BIT(meson_data->detect_cntl, 0, |
| AFE_CH1_EN_DETECT_BIT, AFE_CH1_EN_DETECT_WIDTH); |
| avin_detect_flag &= ~AVIN_DETECT_CH1_EN; |
| } |
| } |
| |
| void tvafe_avin_detect_ch2_anlog_enable(bool enable) |
| { |
| if ((avin_detect_flag & AVIN_DETECT_OPEN) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect is not opened\n", |
| __func__); |
| } |
| return; |
| } |
| if ((avin_detect_flag & AVIN_DETECT_CH2_MASK) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect ch2 is inactive\n", |
| __func__); |
| } |
| return; |
| } |
| if (avin_detect_debug_print) |
| tvafe_pr_info("%s: %d\n", __func__, enable); |
| |
| if (enable) { |
| if (meson_data) |
| W_HIU_BIT(meson_data->detect_cntl, 1, |
| AFE_TL_CH2_EN_DETECT_BIT, AFE_TL_CH2_EN_DETECT_WIDTH); |
| else |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 1, |
| AFE_CH2_EN_DETECT_BIT, AFE_CH2_EN_DETECT_WIDTH); |
| avin_detect_flag |= AVIN_DETECT_CH2_EN; |
| } else { |
| if (meson_data) |
| W_HIU_BIT(meson_data->detect_cntl, 0, |
| AFE_TL_CH2_EN_DETECT_BIT, AFE_TL_CH2_EN_DETECT_WIDTH); |
| else |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 0, |
| AFE_CH2_EN_DETECT_BIT, AFE_CH2_EN_DETECT_WIDTH); |
| avin_detect_flag &= ~AVIN_DETECT_CH2_EN; |
| } |
| } |
| |
| /*after adc and afe is enable,this TIP bit must be set to "0"*/ |
| void tvafe_cha1_SYNCTIP_close_config(void) |
| { |
| if ((avin_detect_flag & AVIN_DETECT_OPEN) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect is not opened\n", |
| __func__); |
| } |
| return; |
| } |
| if ((avin_detect_flag & AVIN_DETECT_CH1_MASK) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect ch1 is inactive\n", |
| __func__); |
| } |
| return; |
| } |
| if (avin_detect_debug_print) |
| tvafe_pr_info("%s\n", __func__); |
| |
| if (meson_data) { |
| W_HIU_BIT(meson_data->detect_cntl, 0, AFE_CH1_EN_DC_BIAS_BIT, |
| AFE_CH1_EN_DC_BIAS_WIDTH); |
| } else { |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 0, AFE_CH1_EN_SYNC_TIP_BIT, |
| AFE_CH1_EN_SYNC_TIP_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, avplay_sync_level, |
| AFE_CH1_SYNC_LEVEL_ADJ_BIT, AFE_CH1_SYNC_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 1, |
| AFE_CH1_SYNC_HYS_ADJ_BIT, AFE_CH1_SYNC_HYS_ADJ_WIDTH); |
| } |
| avin_detect_flag &= ~AVIN_DETECT_CH1_SYNC_TIP; |
| } |
| |
| void tvafe_cha2_SYNCTIP_close_config(void) |
| { |
| if ((avin_detect_flag & AVIN_DETECT_OPEN) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect is not opened\n", |
| __func__); |
| } |
| return; |
| } |
| if ((avin_detect_flag & AVIN_DETECT_CH2_MASK) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect ch2 is inactive\n", |
| __func__); |
| } |
| return; |
| } |
| if (avin_detect_debug_print) |
| tvafe_pr_info("%s\n", __func__); |
| |
| if (meson_data) { |
| if (meson_data->cpu_id >= AVIN_CPU_TYPE_T5) |
| W_HIU_BIT(meson_data->detect_cntl, 0, |
| AFE_T5_CH2_EN_DC_BIAS_BIT, |
| AFE_T5_CH2_EN_DC_BIAS_WIDTH); |
| else |
| W_HIU_BIT(meson_data->detect_cntl, 0, |
| AFE_CH2_EN_DC_BIAS_BIT, |
| AFE_CH2_EN_DC_BIAS_WIDTH); |
| } else { |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 0, AFE_CH2_EN_SYNC_TIP_BIT, |
| AFE_CH2_EN_SYNC_TIP_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, avplay_sync_level, |
| AFE_CH2_SYNC_LEVEL_ADJ_BIT, AFE_CH2_SYNC_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 0, |
| AFE_CH2_SYNC_HYS_ADJ_BIT, AFE_CH2_SYNC_HYS_ADJ_WIDTH); |
| } |
| avin_detect_flag &= ~AVIN_DETECT_CH2_SYNC_TIP; |
| } |
| |
| /*After the CVBS is unplug,the EN_SYNC_TIP need be set to "1"*/ |
| /*to sense the plug in operation*/ |
| void tvafe_cha1_detect_restart_config(void) |
| { |
| if ((avin_detect_flag & AVIN_DETECT_OPEN) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect is not opened\n", |
| __func__); |
| } |
| return; |
| } |
| if ((avin_detect_flag & AVIN_DETECT_CH1_MASK) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect ch1 is inactive\n", |
| __func__); |
| } |
| return; |
| } |
| if (avin_detect_debug_print) |
| tvafe_pr_info("%s\n", __func__); |
| |
| if (meson_data) { |
| W_HIU_BIT(meson_data->detect_cntl, 1, AFE_CH1_EN_DC_BIAS_BIT, |
| AFE_CH1_EN_DC_BIAS_WIDTH); |
| } else { |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 1, AFE_CH1_EN_SYNC_TIP_BIT, |
| AFE_CH1_EN_SYNC_TIP_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, sync_level, |
| AFE_CH1_SYNC_LEVEL_ADJ_BIT, AFE_CH1_SYNC_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, sync_hys_adj, |
| AFE_CH1_SYNC_HYS_ADJ_BIT, AFE_CH1_SYNC_HYS_ADJ_WIDTH); |
| } |
| avin_detect_flag |= AVIN_DETECT_CH1_SYNC_TIP; |
| } |
| |
| void tvafe_cha2_detect_restart_config(void) |
| { |
| if ((avin_detect_flag & AVIN_DETECT_OPEN) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect is not opened\n", |
| __func__); |
| } |
| return; |
| } |
| if ((avin_detect_flag & AVIN_DETECT_CH2_MASK) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect ch2 is inactive\n", |
| __func__); |
| } |
| return; |
| } |
| if (avin_detect_debug_print) |
| tvafe_pr_info("%s\n", __func__); |
| |
| if (meson_data) { |
| if (meson_data->cpu_id >= AVIN_CPU_TYPE_T5) |
| W_HIU_BIT(meson_data->detect_cntl, 1, |
| AFE_T5_CH2_EN_DC_BIAS_BIT, |
| AFE_T5_CH2_EN_DC_BIAS_WIDTH); |
| else |
| W_HIU_BIT(meson_data->detect_cntl, 1, |
| AFE_CH2_EN_DC_BIAS_BIT, |
| AFE_CH2_EN_DC_BIAS_WIDTH); |
| } else { |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 1, AFE_CH2_EN_SYNC_TIP_BIT, |
| AFE_CH2_EN_SYNC_TIP_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, sync_level, |
| AFE_CH2_SYNC_LEVEL_ADJ_BIT, AFE_CH2_SYNC_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, sync_hys_adj, |
| AFE_CH2_SYNC_HYS_ADJ_BIT, AFE_CH2_SYNC_HYS_ADJ_WIDTH); |
| } |
| avin_detect_flag |= AVIN_DETECT_CH2_SYNC_TIP; |
| } |
| |
| static void tvafe_avin_detect_enable(struct tvafe_avin_det_s *avin_data) |
| { |
| if ((avin_detect_flag & AVIN_DETECT_OPEN) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect is not opened\n", |
| __func__); |
| } |
| return; |
| } |
| /*enable anlog config*/ |
| tvafe_pr_info("%s\n", __func__); |
| if (avin_data->dts_param.device_mask & TVAFE_AVIN_CH1_MASK) |
| tvafe_avin_detect_ch1_anlog_enable(1); |
| if (avin_data->dts_param.device_mask & TVAFE_AVIN_CH2_MASK) |
| tvafe_avin_detect_ch2_anlog_enable(1); |
| } |
| |
| static void tvafe_avin_detect_disable(struct tvafe_avin_det_s *avin_data) |
| { |
| if ((avin_detect_flag & AVIN_DETECT_OPEN) == 0) { |
| if (avin_detect_debug_print) { |
| tvafe_pr_info("%s: avin_detect is not opened\n", |
| __func__); |
| } |
| return; |
| } |
| /*disable anlog config*/ |
| tvafe_pr_info("%s\n", __func__); |
| if (avin_data->dts_param.device_mask & TVAFE_AVIN_CH1_MASK) |
| tvafe_avin_detect_ch1_anlog_enable(0); |
| if (avin_data->dts_param.device_mask & TVAFE_AVIN_CH2_MASK) |
| tvafe_avin_detect_ch2_anlog_enable(0); |
| } |
| |
| static void tvafe_avin_detect_anlog_config(void) |
| { |
| if (meson_data) { |
| /*ch1 config*/ |
| W_HIU_BIT(meson_data->detect_cntl, dc_level_adj, |
| AFE_CH1_DC_LEVEL_ADJ_BIT, AFE_CH1_DC_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(meson_data->detect_cntl, comp_level_adj, |
| AFE_CH1_COMP_LEVEL_ADJ_BIT, AFE_CH1_COMP_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(meson_data->detect_cntl, 0, |
| AFE_CH1_COMP_HYS_ADJ_BIT, AFE_CH1_COMP_HYS_ADJ_WIDTH); |
| W_HIU_BIT(meson_data->detect_cntl, 1, AFE_CH1_EN_DC_BIAS_BIT, |
| AFE_CH1_EN_DC_BIAS_WIDTH); |
| |
| if (meson_data->cpu_id >= AVIN_CPU_TYPE_T5) { |
| W_HIU_BIT(meson_data->detect_cntl, 0, |
| AFE_T5_AFE_MAN_MODE_BIT, |
| AFE_T5_AFE_MAN_MODE_WIDTH); |
| W_HIU_BIT(meson_data->detect_cntl, 1, |
| AFE_T5_CH2_EN_DC_BIAS_BIT, |
| AFE_T5_CH2_EN_DC_BIAS_WIDTH); |
| } else { |
| /*ch config*/ |
| W_HIU_BIT(meson_data->detect_cntl, 1, |
| AFE_CH2_EN_DC_BIAS_BIT, |
| AFE_CH2_EN_DC_BIAS_WIDTH); |
| W_HIU_BIT(meson_data->detect_cntl, dc_level_adj, |
| AFE_CH2_DC_LEVEL_ADJ_BIT, |
| AFE_CH2_DC_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(meson_data->detect_cntl, comp_level_adj, |
| AFE_CH2_COMP_LEVEL_ADJ_BIT, |
| AFE_CH2_COMP_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(meson_data->detect_cntl, 0, |
| AFE_CH2_COMP_HYS_ADJ_BIT, |
| AFE_CH2_COMP_HYS_ADJ_WIDTH); |
| } |
| } else { |
| if (detect_mode == 0) { |
| /*for ch1*/ |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 0, |
| AFE_DETECT_RSV1_BIT, AFE_DETECT_RSV1_WIDTH); |
| /*for ch2*/ |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 0, |
| AFE_DETECT_RSV3_BIT, AFE_DETECT_RSV3_WIDTH); |
| } else { |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 1, |
| AFE_DETECT_RSV1_BIT, AFE_DETECT_RSV1_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 1, |
| AFE_DETECT_RSV3_BIT, AFE_DETECT_RSV3_WIDTH); |
| } |
| |
| /*ch1 config*/ |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, sync_level, |
| AFE_CH1_SYNC_LEVEL_ADJ_BIT, AFE_CH1_SYNC_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, sync_hys_adj, |
| AFE_CH1_SYNC_HYS_ADJ_BIT, AFE_CH1_SYNC_HYS_ADJ_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, vdc_level, |
| AFE_DETECT_RSV0_BIT, AFE_DETECT_RSV0_WIDTH); |
| /*after adc and afe is enable,this bit must be set to "0"*/ |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 1, AFE_CH1_EN_SYNC_TIP_BIT, |
| AFE_CH1_EN_SYNC_TIP_WIDTH); |
| |
| /***ch2 config***/ |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, sync_level, |
| AFE_CH2_SYNC_LEVEL_ADJ_BIT, AFE_CH2_SYNC_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, sync_hys_adj, |
| AFE_CH2_SYNC_HYS_ADJ_BIT, AFE_CH2_SYNC_HYS_ADJ_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, vdc_level, |
| AFE_DETECT_RSV2_BIT, AFE_DETECT_RSV2_WIDTH); |
| W_HIU_BIT(HHI_CVBS_DETECT_CNTL, 1, AFE_CH2_EN_SYNC_TIP_BIT, |
| AFE_CH2_EN_SYNC_TIP_WIDTH); |
| } |
| avin_detect_flag |= AVIN_DETECT_CH1_SYNC_TIP; |
| avin_detect_flag |= AVIN_DETECT_CH2_SYNC_TIP; |
| } |
| |
| static void tvafe_avin_detect_digital_config(void) |
| { |
| if (!meson_data) { |
| tvafe_pr_info("%s: meson_data is null\n", __func__); |
| return; |
| } |
| |
| tvafe_avin_irq_update_bit(meson_data->irq0_cntl, |
| CVBS_IRQ_MODE_MASK << CVBS_IRQ_MODE_BIT, |
| irq_mode << CVBS_IRQ_MODE_BIT); |
| |
| tvafe_avin_irq_update_bit(meson_data->irq0_cntl, |
| CVBS_IRQ_TRIGGER_SEL_MASK << CVBS_IRQ_TRIGGER_SEL_BIT, |
| trigger_sel << CVBS_IRQ_TRIGGER_SEL_BIT); |
| tvafe_avin_irq_update_bit(meson_data->irq0_cntl, |
| CVBS_IRQ_EDGE_EN_MASK << CVBS_IRQ_EDGE_EN_BIT, |
| irq_edge_en << CVBS_IRQ_EDGE_EN_BIT); |
| tvafe_avin_irq_update_bit(meson_data->irq0_cntl, |
| CVBS_IRQ_FILTER_MASK << CVBS_IRQ_FILTER_BIT, |
| irq_filter << CVBS_IRQ_FILTER_BIT); |
| tvafe_avin_irq_update_bit(meson_data->irq0_cntl, |
| CVBS_IRQ_POL_MASK << CVBS_IRQ_POL_BIT, |
| irq_pol << CVBS_IRQ_POL_BIT); |
| } |
| |
| static int tvafe_avin_open(struct inode *inode, struct file *file) |
| { |
| struct tvafe_avin_det_s *avin_data; |
| |
| tvafe_pr_info("%s: avin open.\n", __func__); |
| avin_data = container_of(inode->i_cdev, |
| struct tvafe_avin_det_s, avin_cdev); |
| file->private_data = avin_data; |
| /*enable irq */ |
| avin_detect_flag |= AVIN_DETECT_OPEN; |
| tvafe_avin_detect_enable(avin_data); |
| return 0; |
| } |
| |
| static ssize_t tvafe_avin_read(struct file *file, char __user *buf, |
| size_t count, loff_t *ppos) |
| { |
| unsigned long ret; |
| struct tvafe_avin_det_s *avin_data = |
| (struct tvafe_avin_det_s *)file->private_data; |
| |
| ret = copy_to_user(buf, |
| (void *)(&avin_data->report_data_s[0]), |
| sizeof(struct tvafe_report_data_s) |
| * avin_data->device_num); |
| |
| return 0; |
| } |
| |
| static int tvafe_avin_release(struct inode *inode, struct file *file) |
| { |
| struct tvafe_avin_det_s *avin_data = |
| (struct tvafe_avin_det_s *)file->private_data; |
| |
| tvafe_avin_detect_disable(avin_data); |
| avin_detect_flag &= ~AVIN_DETECT_OPEN; |
| file->private_data = NULL; |
| return 0; |
| } |
| |
| static unsigned int tvafe_avin_poll(struct file *file, poll_table *wait) |
| { |
| unsigned int mask = 0; |
| |
| poll_wait(file, &tvafe_avin_waitq, wait); |
| mask |= POLLIN | POLLRDNORM; |
| |
| return mask; |
| } |
| |
| static const struct file_operations tvafe_avin_fops = { |
| .owner = THIS_MODULE, |
| .open = tvafe_avin_open, |
| .read = tvafe_avin_read, |
| .poll = tvafe_avin_poll, |
| .release = tvafe_avin_release, |
| }; |
| |
| static int tvafe_register_avin_dev(struct tvafe_avin_det_s *avin_data) |
| { |
| int ret = 0; |
| |
| ret = alloc_chrdev_region(&avin_data->avin_devno, |
| 0, 1, TVAFE_AVIN_NAME); |
| if (ret < 0) { |
| tvafe_pr_err("failed to allocate major number\n"); |
| return -ENODEV; |
| } |
| |
| /* connect the file operations with cdev */ |
| cdev_init(&avin_data->avin_cdev, &tvafe_avin_fops); |
| avin_data->avin_cdev.owner = THIS_MODULE; |
| /* connect the major/minor number to the cdev */ |
| ret = cdev_add(&avin_data->avin_cdev, avin_data->avin_devno, 1); |
| if (ret) { |
| tvafe_pr_err("failed to add device\n"); |
| unregister_chrdev_region(avin_data->avin_devno, 1); |
| return -ENODEV; |
| } |
| |
| strcpy(avin_data->config_name, TVAFE_AVIN_NAME); |
| avin_data->config_class = class_create(THIS_MODULE, |
| avin_data->config_name); |
| avin_data->config_dev = device_create(avin_data->config_class, NULL, |
| avin_data->avin_devno, NULL, avin_data->config_name); |
| if (IS_ERR(avin_data->config_dev)) { |
| tvafe_pr_err("failed to create device node\n"); |
| cdev_del(&avin_data->avin_cdev); |
| unregister_chrdev_region(avin_data->avin_devno, 1); |
| ret = PTR_ERR(avin_data->config_dev); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| static void tvafe_avin_detect_state(struct tvafe_avin_det_s *avdev) |
| { |
| tvafe_pr_info("device_num: %d\n", avdev->device_num); |
| tvafe_pr_info("\t*****dts param*****\n"); |
| tvafe_pr_info("device_mask: %d\n", avdev->dts_param.device_mask); |
| tvafe_pr_info("irq0: %d\n", avdev->dts_param.irq[0]); |
| tvafe_pr_info("irq1: %d\n", avdev->dts_param.irq[1]); |
| tvafe_pr_info("irq_counter[0]: 0x%x\n", avdev->irq_counter[0]); |
| tvafe_pr_info("irq_counter[1]: 0x%x\n", avdev->irq_counter[1]); |
| tvafe_pr_info("\t*****channel status:0->in;1->out*****\n"); |
| tvafe_pr_info("channel[%d] status: %d\n", |
| avdev->report_data_s[0].channel, |
| avdev->report_data_s[0].status); |
| tvafe_pr_info("channel[%d] status: %d\n", |
| avdev->report_data_s[1].channel, |
| avdev->report_data_s[1].status); |
| tvafe_pr_info("avin_detect_flag: 0x%02x\n", avin_detect_flag); |
| tvafe_pr_info("avin_detect_reg: 0x%02x=0x%08x\n", |
| meson_data->detect_cntl, R_HIU_REG(meson_data->detect_cntl)); |
| tvafe_pr_info("avin_irq_reg: 0x%02x=0x%08x\n", |
| meson_data->irq0_cntl, |
| tvafe_avin_irq_reg_read(meson_data->irq0_cntl)); |
| tvafe_pr_info("\t*****global param*****\n"); |
| tvafe_pr_info("dc_level_adj: %d\n", dc_level_adj); |
| tvafe_pr_info("comp_level_adj: %d\n", comp_level_adj); |
| tvafe_pr_info("detect_mode: %d\n", detect_mode); |
| tvafe_pr_info("vdc_level: %d\n", vdc_level); |
| tvafe_pr_info("sync_level: %d\n", sync_level); |
| tvafe_pr_info("sync_hys_adj: %d\n", sync_hys_adj); |
| tvafe_pr_info("irq_mode: %d\n", irq_mode); |
| tvafe_pr_info("trigger_sel: %d\n", trigger_sel); |
| tvafe_pr_info("irq_edge_en: %d\n", irq_edge_en); |
| tvafe_pr_info("irq_filter: %d\n", irq_filter); |
| tvafe_pr_info("irq_pol: %d\n", irq_pol); |
| } |
| |
| static void tvafe_avin_detect_parse_param(char *buf_orig, char **parm) |
| { |
| char *ps, *token; |
| char delim1[3] = " "; |
| char delim2[2] = "\n"; |
| unsigned int n = 0; |
| |
| ps = buf_orig; |
| strcat(delim1, delim2); |
| while (1) { |
| token = strsep(&ps, delim1); |
| if (!token) |
| break; |
| if (*token == '\0') |
| continue; |
| parm[n++] = token; |
| } |
| } |
| |
| static ssize_t debug_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| ssize_t len = 0; |
| |
| len += sprintf(buf + len, |
| "\t*****usage:*****\n"); |
| len += sprintf(buf + len, |
| "echo dc_level_adj val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo comp_level_adj val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo detect_mode val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo vdc_level val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo sync_level val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo sync_hys_adj val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo irq_mode val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo trigger_sel val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo irq_edge_en val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo irq_filter val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo irq_pol val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo ch1_enable val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo ch2_enable val(D) > debug\n"); |
| len += sprintf(buf + len, |
| "echo enable > debug\n"); |
| len += sprintf(buf + len, |
| "echo disable > debug\n"); |
| len += sprintf(buf + len, |
| "echo status > debug\n"); |
| return len; |
| } |
| |
| static ssize_t debug_store(struct device *dev, |
| struct device_attribute *attr, const char *buff, size_t count) |
| { |
| struct tvafe_avin_det_s *avdev; |
| unsigned int val; |
| char *buf_orig, *parm[10] = {NULL}; |
| |
| avdev = dev_get_drvdata(dev); |
| if (!buff) |
| return count; |
| buf_orig = kstrdup(buff, GFP_KERNEL); |
| tvafe_avin_detect_parse_param(buf_orig, (char **)&parm); |
| |
| /*tvafe_pr_info("[%s]:param0:%s.\n", __func__, parm[0]);*/ |
| if (!strcmp(parm[0], "dc_level_adj")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &dc_level_adj)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } else { |
| W_HIU_BIT(meson_data->detect_cntl, dc_level_adj, |
| AFE_CH1_DC_LEVEL_ADJ_BIT, |
| AFE_CH1_DC_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(meson_data->detect_cntl, dc_level_adj, |
| AFE_CH2_DC_LEVEL_ADJ_BIT, |
| AFE_CH2_DC_LEVEL_ADJ_WIDTH); |
| } |
| } |
| tvafe_pr_info("[%s]: dc_level_adj: %d\n", |
| __func__, dc_level_adj); |
| } else if (!strcmp(parm[0], "comp_level_adj")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &comp_level_adj)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } else { |
| W_HIU_BIT(meson_data->detect_cntl, comp_level_adj, |
| AFE_CH1_COMP_LEVEL_ADJ_BIT, |
| AFE_CH1_COMP_LEVEL_ADJ_WIDTH); |
| W_HIU_BIT(meson_data->detect_cntl, comp_level_adj, |
| AFE_CH2_COMP_LEVEL_ADJ_BIT, |
| AFE_CH2_COMP_LEVEL_ADJ_WIDTH); |
| } |
| } |
| tvafe_pr_info("[%s]: comp_level_adj: %d\n", |
| __func__, comp_level_adj); |
| } else if (!strcmp(parm[0], "detect_mode")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &detect_mode)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| } |
| tvafe_pr_info("[%s]: detect_mode: %d\n", |
| __func__, detect_mode); |
| } else if (!strcmp(parm[0], "vdc_level")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &vdc_level)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| } |
| tvafe_pr_info("[%s]: vdc_level: %d\n", |
| __func__, vdc_level); |
| } else if (!strcmp(parm[0], "sync_level")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &sync_level)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| } |
| tvafe_pr_info("[%s]: sync_level: %d\n", |
| __func__, sync_level); |
| } else if (!strcmp(parm[0], "sync_hys_adj")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &sync_hys_adj)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| } |
| tvafe_pr_info("[%s]: sync_hys_adj: %d\n", |
| __func__, sync_hys_adj); |
| } else if (!strcmp(parm[0], "irq_mode")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &irq_mode)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| } |
| tvafe_pr_info("[%s]: irq_mode: %d\n", |
| __func__, irq_mode); |
| } else if (!strcmp(parm[0], "trigger_sel")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &trigger_sel)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| } |
| tvafe_pr_info("[%s]: trigger_sel: %d\n", |
| __func__, trigger_sel); |
| } else if (!strcmp(parm[0], "irq_edge_en")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &irq_edge_en)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| } |
| tvafe_pr_info("[%s]: irq_edge_en: %d\n", |
| __func__, irq_edge_en); |
| } else if (!strcmp(parm[0], "irq_filter")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &irq_filter)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| } |
| tvafe_pr_info("[%s]: irq_filter: %d\n", |
| __func__, irq_filter); |
| } else if (!strcmp(parm[0], "irq_pol")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &irq_pol)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| } |
| tvafe_pr_info("[%s]: irq_pol: %d\n", |
| __func__, irq_pol); |
| } else if (!strcmp(parm[0], "ch1_enable")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &val)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| tvafe_pr_info("[%s]: ch1_enable: %d\n", |
| __func__, val); |
| if (val) |
| tvafe_avin_detect_ch1_anlog_enable(1); |
| else |
| tvafe_avin_detect_ch1_anlog_enable(0); |
| } |
| } else if (!strcmp(parm[0], "ch2_enable")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &val)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| tvafe_pr_info("[%s]: ch2_enable: %d\n", |
| __func__, val); |
| if (val) |
| tvafe_avin_detect_ch2_anlog_enable(1); |
| else |
| tvafe_avin_detect_ch2_anlog_enable(0); |
| } |
| } else if (!strcmp(parm[0], "enable")) { |
| avin_detect_flag |= AVIN_DETECT_OPEN; |
| tvafe_avin_detect_enable(avdev); |
| } else if (!strcmp(parm[0], "disable")) { |
| tvafe_avin_detect_disable(avdev); |
| avin_detect_flag &= ~AVIN_DETECT_OPEN; |
| av1_plugin_state = 0; |
| av2_plugin_state = 0; |
| } else if (!strcmp(parm[0], "status")) { |
| tvafe_avin_detect_state(avdev); |
| } else if (!strcmp(parm[0], "print")) { |
| if (parm[1]) { |
| if (kstrtouint(parm[1], 10, &avin_detect_debug_print)) { |
| tvafe_pr_info("[%s]:invalid parameter\n", |
| __func__); |
| goto tvafe_avin_detect_store_err; |
| } |
| } |
| tvafe_pr_info("[%s]: avin_detect_debug_print: %d\n", |
| __func__, avin_detect_debug_print); |
| } else { |
| tvafe_pr_info("[%s]:invalid command.\n", __func__); |
| } |
| |
| kfree(buf_orig); |
| return count; |
| |
| tvafe_avin_detect_store_err: |
| kfree(buf_orig); |
| return -EINVAL; |
| } |
| |
| static void tvafe_avin_detect_timer_handler(struct timer_list *avin_detect_timer) |
| { |
| unsigned int state_changed = 0; |
| |
| if (!avdev || !meson_data) { |
| tvafe_pr_info("tvin avdev or meson_data is NULL\n"); |
| return; |
| } |
| |
| if (avdev->dts_param.device_mask == TVAFE_AVIN_CH1_MASK) { |
| avdev->irq_counter[0] = tvafe_avin_irq_reg_read(meson_data->irq0_cnt); |
| if (!R_HIU_BIT(meson_data->detect_cntl, |
| AFE_CH1_EN_DETECT_BIT, AFE_CH1_EN_DETECT_WIDTH)) { |
| goto TIMER; |
| } |
| } else if (avdev->dts_param.device_mask == TVAFE_AVIN_CH2_MASK) { |
| avdev->irq_counter[0] = aml_read_cbus(meson_data->irq1_cnt); |
| if (meson_data) { |
| if (!R_HIU_BIT(meson_data->detect_cntl, |
| AFE_TL_CH2_EN_DETECT_BIT, AFE_TL_CH2_EN_DETECT_WIDTH)) |
| goto TIMER; |
| } else { |
| if (!R_HIU_BIT(HHI_CVBS_DETECT_CNTL, |
| AFE_CH2_EN_DETECT_BIT, AFE_CH2_EN_DETECT_WIDTH)) |
| goto TIMER; |
| } |
| } else if (avdev->dts_param.device_mask == TVAFE_AVIN_MASK) { |
| avdev->irq_counter[0] = aml_read_cbus(meson_data->irq0_cnt); |
| avdev->irq_counter[1] = aml_read_cbus(meson_data->irq1_cnt); |
| if (meson_data) { |
| if (!R_HIU_BIT(meson_data->detect_cntl, |
| AFE_CH1_EN_DETECT_BIT, AFE_CH1_EN_DETECT_WIDTH) || |
| !R_HIU_BIT(meson_data->detect_cntl, |
| AFE_TL_CH2_EN_DETECT_BIT, AFE_TL_CH2_EN_DETECT_WIDTH)) |
| goto TIMER; |
| } else { |
| if (!R_HIU_BIT(HHI_CVBS_DETECT_CNTL, |
| AFE_CH1_EN_DETECT_BIT, AFE_CH1_EN_DETECT_WIDTH) || |
| !R_HIU_BIT(HHI_CVBS_DETECT_CNTL, |
| AFE_CH2_EN_DETECT_BIT, AFE_CH2_EN_DETECT_WIDTH)) |
| goto TIMER; |
| } |
| if (avdev->irq_counter[1] != s_irq_counter1) { |
| if (s_counter1_last_state != 1) |
| s_irq_counter1_time = 0; |
| s_irq_counter1_time++; |
| if (s_irq_counter1_time >= avin_count_times) { |
| if (avdev->report_data_s[1].status != |
| TVAFE_AVIN_STATUS_IN) { |
| avdev->report_data_s[1].status = |
| TVAFE_AVIN_STATUS_IN; |
| state_changed = 1; |
| av2_plugin_state = 0; |
| tvafe_pr_info("avin[1].status IN.\n"); |
| /*port opened and plug in,enable clamp*/ |
| /*sync tip close*/ |
| if (avport_opened == TVAFE_PORT_AV2) |
| tvafe_cha2_SYNCTIP_close_config(); |
| } |
| s_irq_counter1_time = 0; |
| } |
| s_counter1_last_state = 1; |
| } else { |
| if (s_counter1_last_state != 0) |
| s_irq_counter1_time = 0; |
| s_irq_counter1_time++; |
| if (s_irq_counter1_time >= avin_count_times) { |
| if (avdev->report_data_s[1].status != |
| TVAFE_AVIN_STATUS_OUT) { |
| avdev->report_data_s[1].status = |
| TVAFE_AVIN_STATUS_OUT; |
| state_changed = 1; |
| av2_plugin_state = 1; |
| tvafe_pr_info("avin[1].status OUT.\n"); |
| /*port opened but plug out,need disable clamp*/ |
| if (avport_opened == TVAFE_PORT_AV2) { |
| W_APB_BIT(TVFE_CLAMP_INTF, 0, |
| CLAMP_EN_BIT, CLAMP_EN_WID); |
| tvafe_cha2_detect_restart_config(); |
| } |
| } |
| s_irq_counter1_time = 0; |
| } |
| s_counter1_last_state = 0; |
| } |
| s_irq_counter1 = avdev->irq_counter[1]; |
| } |
| /*tvafe_pr_info("irq_counter[0]:%u, last_count:%u\n"*/ |
| /* avdev->irq_counter[0], s_irq_counter0);*/ |
| if (avdev->irq_counter[0] != s_irq_counter0) { |
| if (s_counter0_last_state != 1) |
| s_irq_counter0_time = 0; |
| s_irq_counter0_time++; |
| if (s_irq_counter0_time >= avin_count_times) { |
| if (avdev->report_data_s[0].status != |
| TVAFE_AVIN_STATUS_IN) { |
| avdev->report_data_s[0].status = |
| TVAFE_AVIN_STATUS_IN; |
| state_changed = 1; |
| av1_plugin_state = 0; |
| tvafe_pr_info("avin[0].status IN.\n"); |
| /*port opened and plug in then enable clamp*/ |
| /*sync tip close*/ |
| if (avport_opened == TVAFE_PORT_AV1) |
| tvafe_cha1_SYNCTIP_close_config(); |
| } |
| s_irq_counter0_time = 0; |
| } |
| s_counter0_last_state = 1; |
| } else { |
| if (s_counter0_last_state != 0) |
| s_irq_counter0_time = 0; |
| s_irq_counter0_time++; |
| if (s_irq_counter0_time >= avin_count_times) { |
| if (avdev->report_data_s[0].status != |
| TVAFE_AVIN_STATUS_OUT) { |
| avdev->report_data_s[0].status = |
| TVAFE_AVIN_STATUS_OUT; |
| state_changed = 1; |
| av1_plugin_state = 1; |
| tvafe_pr_info("avin[0].status OUT.\n"); |
| |
| /*After the CVBS is unplug,*/ |
| /*the EN_SYNC_TIP need be set to "1"*/ |
| /*to sense the plug in operation*/ |
| /*port opened but plug out,need disable clamp*/ |
| if (avport_opened == TVAFE_PORT_AV1) { |
| W_APB_BIT(TVFE_CLAMP_INTF, 0, |
| CLAMP_EN_BIT, CLAMP_EN_WID); |
| tvafe_cha1_detect_restart_config(); |
| } |
| } |
| s_irq_counter0_time = 0; |
| } |
| s_counter0_last_state = 0; |
| } |
| s_irq_counter0 = avdev->irq_counter[0]; |
| |
| if (state_changed) |
| wake_up_interruptible(&tvafe_avin_waitq); |
| |
| TIMER: |
| avin_detect_timer->expires = jiffies + |
| (TVAFE_AVIN_INTERVAL * avin_timer_time); |
| add_timer(avin_detect_timer); |
| } |
| |
| static int tvafe_avin_init_resource(struct tvafe_avin_det_s *avdev) |
| { |
| /* add timer for avin detect*/ |
| timer_setup(&avin_detect_timer, tvafe_avin_detect_timer_handler, 0); |
| avin_detect_timer.function = tvafe_avin_detect_timer_handler; |
| avin_detect_timer.expires = jiffies + |
| (TVAFE_AVIN_INTERVAL * avin_timer_time); |
| add_timer(&avin_detect_timer); |
| |
| return 0; |
| } |
| |
| static DEVICE_ATTR_RW(debug); |
| |
| static int tvafe_avin_detect_probe(struct platform_device *pdev) |
| { |
| int ret; |
| int state = 0; |
| int size_io_reg; |
| /*const void *name;*/ |
| /*int offset, size, mem_size_m;*/ |
| struct resource *res; |
| |
| meson_data = (struct meson_avin_data *) |
| of_device_get_match_data(&pdev->dev); |
| if (meson_data) |
| tvafe_pr_info("%s: cpuid:%d,%s.\n", |
| __func__, meson_data->cpu_id, meson_data->name); |
| |
| avdev = kzalloc(sizeof(*avdev), GFP_KERNEL); |
| if (!avdev) { |
| state = -ENOMEM; |
| goto get_param_mem_fail; |
| } |
| |
| platform_set_drvdata(pdev, avdev); |
| |
| ret = tvafe_avin_dts_parse(pdev); |
| if (ret) { |
| state = ret; |
| goto get_dts_dat_fail; |
| } |
| |
| /* register char device */ |
| ret = tvafe_register_avin_dev(avdev); |
| /* create class attr file */ |
| ret = device_create_file(avdev->config_dev, &dev_attr_debug); |
| if (ret < 0) { |
| tvafe_pr_err("fail to create dbg attribute file\n"); |
| goto fail_create_dbg_file; |
| } |
| dev_set_drvdata(avdev->config_dev, avdev); |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (res) { |
| size_io_reg = resource_size(res); |
| tvafe_pr_info("%s: hiu reg base=0x%p,size=0x%x\n", |
| __func__, (void *)res->start, size_io_reg); |
| meson_data->irq_reg_base = |
| devm_ioremap_nocache(&pdev->dev, res->start, size_io_reg); |
| if (!meson_data->irq_reg_base) { |
| dev_err(&pdev->dev, "hiu ioremap failed\n"); |
| return -ENOMEM; |
| } |
| tvafe_pr_info("%s: hiu maped reg_base =0x%p, size=0x%x\n", |
| __func__, meson_data->irq_reg_base, size_io_reg); |
| } else { |
| dev_err(&pdev->dev, "missing hiu memory resource\n"); |
| meson_data->irq_reg_base = NULL; |
| } |
| |
| /*config analog part setting*/ |
| tvafe_avin_detect_anlog_config(); |
| /*config digital part setting*/ |
| tvafe_avin_detect_digital_config(); |
| |
| /* add timer for avin detect*/ |
| timer_setup(&avin_detect_timer, tvafe_avin_detect_timer_handler, 0); |
| avin_detect_timer.function = tvafe_avin_detect_timer_handler; |
| avin_detect_timer.expires = jiffies + |
| (TVAFE_AVIN_INTERVAL * avin_timer_time); |
| add_timer(&avin_detect_timer); |
| |
| avin_detect_flag |= AVIN_DETECT_INIT; |
| tvafe_pr_info("%s: ok.\n", __func__); |
| |
| return 0; |
| |
| fail_create_dbg_file: |
| get_dts_dat_fail: |
| kfree(avdev); |
| get_param_mem_fail: |
| tvafe_pr_info("%s: kzalloc error\n", __func__); |
| return state; |
| } |
| |
| static int tvafe_avin_detect_suspend(struct platform_device *pdev, |
| pm_message_t state) |
| { |
| struct tvafe_avin_det_s *avdev = platform_get_drvdata(pdev); |
| |
| del_timer_sync(&avin_detect_timer); |
| tvafe_avin_detect_disable(avdev); |
| tvafe_pr_info("%s: tvafe suspend.\n", __func__); |
| return 0; |
| } |
| |
| static int tvafe_avin_detect_resume(struct platform_device *pdev) |
| { |
| struct tvafe_avin_det_s *avdev = platform_get_drvdata(pdev); |
| |
| tvafe_avin_init_resource(avdev); |
| tvafe_avin_detect_enable(avdev); |
| tvafe_pr_info("%s: avin resume.\n", __func__); |
| return 0; |
| } |
| |
| static void tvafe_avin_detect_shutdown(struct platform_device *pdev) |
| { |
| struct tvafe_avin_det_s *avdev = platform_get_drvdata(pdev); |
| |
| cdev_del(&avdev->avin_cdev); |
| del_timer_sync(&avin_detect_timer); |
| tvafe_avin_detect_disable(avdev); |
| device_remove_file(avdev->config_dev, &dev_attr_debug); |
| tvafe_pr_info("%s: avin shutdown.\n", __func__); |
| kfree(avdev); |
| } |
| |
| int tvafe_avin_detect_remove(struct platform_device *pdev) |
| { |
| struct tvafe_avin_det_s *avdev = platform_get_drvdata(pdev); |
| |
| if (meson_data->irq_reg_base) |
| devm_iounmap(&pdev->dev, meson_data->irq_reg_base); |
| cdev_del(&avdev->avin_cdev); |
| del_timer_sync(&avin_detect_timer); |
| tvafe_avin_detect_disable(avdev); |
| device_remove_file(avdev->config_dev, &dev_attr_debug); |
| kfree(avdev); |
| |
| avin_detect_flag = 0; |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_OF |
| #ifndef CONFIG_AMLOGIC_REMOVE_OLD |
| struct meson_avin_data tl1_data = { |
| .cpu_id = AVIN_CPU_TYPE_TL1, |
| .name = "meson-tl1-avin-detect", |
| |
| .detect_cntl = HHI_CVBS_DETECT_CNTL, |
| .irq0_cntl = CVBS_IRQ0_CNTL, |
| .irq1_cntl = CVBS_IRQ1_CNTL, |
| .irq0_cnt = CVBS_IRQ0_COUNTER, |
| .irq1_cnt = CVBS_IRQ1_COUNTER, |
| }; |
| #endif |
| |
| struct meson_avin_data tm2_data = { |
| .cpu_id = AVIN_CPU_TYPE_TM2, |
| .name = "meson-tm2-avin-detect", |
| |
| .detect_cntl = HHI_CVBS_DETECT_CNTL, |
| .irq0_cntl = CVBS_IRQ0_CNTL, |
| .irq1_cntl = CVBS_IRQ1_CNTL, |
| .irq0_cnt = CVBS_IRQ0_COUNTER, |
| .irq1_cnt = CVBS_IRQ1_COUNTER, |
| }; |
| |
| struct meson_avin_data t5_data = { |
| .cpu_id = AVIN_CPU_TYPE_T5, |
| .name = "meson-t5-avin-detect", |
| |
| .detect_cntl = HHI_CVBS_DETECT_CNTL, |
| .irq0_cntl = CVBS_IRQ0_CNTL, |
| .irq1_cntl = CVBS_IRQ1_CNTL, |
| .irq0_cnt = CVBS_IRQ0_COUNTER, |
| .irq1_cnt = CVBS_IRQ1_COUNTER, |
| }; |
| |
| struct meson_avin_data t5d_data = { |
| .cpu_id = AVIN_CPU_TYPE_T5D, |
| .name = "meson-t5d-avin-detect", |
| |
| .detect_cntl = HHI_CVBS_DETECT_CNTL, |
| .irq0_cntl = CVBS_IRQ0_CNTL, |
| .irq1_cntl = CVBS_IRQ1_CNTL, |
| .irq0_cnt = CVBS_IRQ0_COUNTER, |
| .irq1_cnt = CVBS_IRQ1_COUNTER, |
| }; |
| |
| struct meson_avin_data t3_data = { |
| .cpu_id = AVIN_CPU_TYPE_T3, |
| .name = "meson-t3-avin-detect", |
| |
| .detect_cntl = ANACTRL_CVBS_DETECT_CNTL, |
| .irq0_cntl = IRQCTRL_CVBS_IRQ0_CNTL, |
| .irq1_cntl = IRQCTRL_CVBS_IRQ1_CNTL, |
| .irq0_cnt = IRQCTRL_CVBS_IRQ0_COUNTER, |
| .irq1_cnt = IRQCTRL_CVBS_IRQ1_COUNTER, |
| }; |
| |
| struct meson_avin_data t5w_data = { |
| .cpu_id = AVIN_CPU_TYPE_T5W, |
| .name = "meson-t5w-avin-detect", |
| |
| .detect_cntl = HHI_CVBS_DETECT_CNTL, |
| .irq0_cntl = CVBS_IRQ0_CNTL, |
| .irq1_cntl = CVBS_IRQ1_CNTL, |
| .irq0_cnt = CVBS_IRQ0_COUNTER, |
| .irq1_cnt = CVBS_IRQ1_COUNTER, |
| }; |
| |
| static const struct of_device_id tvafe_avin_dt_match[] = { |
| #ifndef CONFIG_AMLOGIC_REMOVE_OLD |
| { .compatible = "amlogic, tvafe_avin_detect", |
| }, |
| { .compatible = "amlogic, tl1_tvafe_avin_detect", |
| .data = &tl1_data, |
| }, |
| #endif |
| { .compatible = "amlogic, tm2_tvafe_avin_detect", |
| .data = &tm2_data, |
| }, |
| { .compatible = "amlogic, t5_tvafe_avin_detect", |
| .data = &t5_data, |
| }, |
| { .compatible = "amlogic, t5d_tvafe_avin_detect", |
| .data = &t5d_data, |
| }, |
| { .compatible = "amlogic, t3_tvafe_avin_detect", |
| .data = &t3_data, |
| }, |
| { .compatible = "amlogic, t5w_tvafe_avin_detect", |
| .data = &t5w_data, |
| }, |
| {}, |
| }; |
| #else |
| #define tvafe_avin_dt_match NULL |
| #endif |
| |
| static struct platform_driver tvafe_avin_driver = { |
| .probe = tvafe_avin_detect_probe, |
| .remove = tvafe_avin_detect_remove, |
| .suspend = tvafe_avin_detect_suspend, |
| .resume = tvafe_avin_detect_resume, |
| .shutdown = tvafe_avin_detect_shutdown, |
| .driver = { |
| .name = "tvafe_avin_detect", |
| .of_match_table = tvafe_avin_dt_match, |
| }, |
| }; |
| |
| int __init tvafe_avin_detect_init(void) |
| { |
| return platform_driver_register(&tvafe_avin_driver); |
| } |
| |
| void __exit tvafe_avin_detect_exit(void) |
| { |
| platform_driver_unregister(&tvafe_avin_driver); |
| } |
| |
| //MODULE_DESCRIPTION("Meson TVAFE AVIN detect Driver"); |
| //MODULE_LICENSE("GPL"); |
| //MODULE_AUTHOR("Amlogic, Inc."); |
| |