blob: 3367144535e0a9b568fd34758878f10d13a51f94 [file] [log] [blame]
/*
* drivers/amlogic/media/vin/tvin/tvafe/tvafe.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
/* 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/interrupt.h>
#include <linux/errno.h>
/*#include <asm/uaccess.h>*/
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/amlogic/iomap.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
#include <linux/of_irq.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/dma-contiguous.h>
#include <linux/clk.h>
/* Amlogic headers */
#include <linux/amlogic/media/canvas/canvas.h>
/*#include <mach/am_regs.h>*/
#include <linux/amlogic/media/vfm/vframe.h>
/* Local include */
#include <linux/amlogic/media/frame_provider/tvin/tvin.h>
#include "../tvin_frontend.h"
#include "../tvin_global.h"
#include "../tvin_format_table.h"
#include "tvafe_regs.h"
#include "tvafe_cvd.h"
#include "tvafe_general.h"
#include "tvafe.h"
#include "tvafe_debug.h"
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AVDETECT
#include "tvafe_avin_detect.h"
#endif
#include "../vdin/vdin_sm.h"
#define TVAFE_NAME "tvafe"
#define TVAFE_DRIVER_NAME "tvafe"
#define TVAFE_MODULE_NAME "tvafe"
#define TVAFE_DEVICE_NAME "tvafe"
#define TVAFE_CLASS_NAME "tvafe"
static dev_t tvafe_devno;
static struct class *tvafe_clsp;
struct mutex pll_mutex;
#define TVAFE_TIMER_INTERVAL (HZ/100) /* 10ms, #define HZ 100 */
#define TVAFE_RATIO_CNT 40
static struct am_regs_s tvaferegs;
static struct tvafe_pin_mux_s tvafe_pinmux;
static struct meson_tvafe_data *s_tvafe_data;
static struct tvafe_clkgate_type tvafe_clkgate;
static bool enable_db_reg = true;
module_param(enable_db_reg, bool, 0644);
MODULE_PARM_DESC(enable_db_reg, "enable/disable tvafe load reg");
bool tvafe_dbg_enable;
module_param(tvafe_dbg_enable, bool, 0644);
MODULE_PARM_DESC(tvafe_dbg_enable, "enable/disable tvafe debug enable");
static int cutwindow_val_v = TVAFE_VS_VE_VAL;
static int cutwindow_val_v_level0 = 4;
static int cutwindow_val_v_level1 = 8;
static int cutwindow_val_v_level2 = 14;
static int cutwindow_val_v_level3 = 16;
static int cutwindow_val_v_level4 = 24;
static int cutwindow_val_h_level1 = 10;
static int cutwindow_val_h_level2 = 18;
static int cutwindow_val_h_level3 = 20;
static int cutwindow_val_h_level4 = 62;/*48-->62 for ntsc-m*/
/*tvconfig snow config*/
static bool snow_cfg;
/*1: snow function on;*/
/*0: off snow function*/
bool tvafe_snow_function_flag;
/*1: tvafe clk enabled;*/
/*0: tvafe clk disabled*/
/*read write cvd acd reg will crash when clk disabled*/
bool tvafe_clk_status;
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AVDETECT
/*opened port,1:av1, 2:av2, 0:none av*/
unsigned int avport_opened;
/*0:in, 1:out*/
unsigned int av1_plugin_state;
unsigned int av2_plugin_state;
#endif
#ifdef CONFIG_AMLOGIC_ATV_DEMOD
static struct tvafe_info_s *g_tvafe_info;
#endif
/*
* tvafe check support port
*/
int tvafe_dec_support(struct tvin_frontend_s *fe, enum tvin_port_e port)
{
struct tvafe_dev_s *devp = container_of(fe,
struct tvafe_dev_s, frontend);
/* check afe port and index */
if (((port < TVIN_PORT_CVBS0) || (port > TVIN_PORT_CVBS3)) ||
(fe->index != devp->index))
return -1;
return 0;
}
#ifdef CONFIG_CMA
void tvafe_cma_alloc(struct tvafe_dev_s *devp)
{
unsigned int mem_size = devp->cma_mem_size;
int flags = CODEC_MM_FLAGS_CMA_FIRST|CODEC_MM_FLAGS_CMA_CLEAR|
CODEC_MM_FLAGS_CPU;
if (devp->cma_config_en == 0)
return;
if (devp->cma_mem_alloc == 1)
return;
devp->cma_mem_alloc = 1;
if (devp->cma_config_flag == 1) {
devp->mem.start = codec_mm_alloc_for_dma("tvafe",
mem_size/PAGE_SIZE, 0, flags);
devp->mem.size = mem_size;
if (devp->mem.start == 0)
tvafe_pr_err("tvafe codec alloc fail!!!\n");
else {
tvafe_pr_info("mem_start = 0x%x, mem_size = 0x%x\n",
devp->mem.start, devp->mem.size);
tvafe_pr_info("codec cma alloc ok!\n");
}
} else if (devp->cma_config_flag == 0) {
devp->venc_pages =
dma_alloc_from_contiguous(&(devp->this_pdev->dev),
mem_size >> PAGE_SHIFT, 0);
if (devp->venc_pages) {
devp->mem.start = page_to_phys(devp->venc_pages);
devp->mem.size = mem_size;
tvafe_pr_info("mem_start = 0x%x, mem_size = 0x%x\n",
devp->mem.start, devp->mem.size);
tvafe_pr_info("cma alloc ok!\n");
} else {
tvafe_pr_err("tvafe cma mem undefined2.\n");
}
}
}
void tvafe_cma_release(struct tvafe_dev_s *devp)
{
if (devp->cma_config_en == 0)
return;
if ((devp->cma_config_flag == 1) && devp->mem.start
&& (devp->cma_mem_alloc == 1)) {
codec_mm_free_for_dma("tvafe", devp->mem.start);
devp->mem.start = 0;
devp->mem.size = 0;
devp->cma_mem_alloc = 0;
tvafe_pr_info("codec cma release ok!\n");
} else if (devp->venc_pages
&& devp->cma_mem_size
&& (devp->cma_mem_alloc == 1)
&& (devp->cma_config_flag == 0)) {
devp->cma_mem_alloc = 0;
dma_release_from_contiguous(&(devp->this_pdev->dev),
devp->venc_pages,
devp->cma_mem_size>>PAGE_SHIFT);
tvafe_pr_info("cma release ok!\n");
}
}
#endif
#ifdef CONFIG_AMLOGIC_ATV_DEMOD
static int tvafe_get_v_fmt(void)
{
int fmt = 0;
if (tvin_get_sm_status(0) != TVIN_SM_STATUS_STABLE) {
tvafe_pr_info("%s tvafe is not STABLE\n", __func__);
return 0;
}
if (g_tvafe_info)
fmt = tvafe_cvd2_get_format(&g_tvafe_info->cvd2);
return fmt;
}
#endif
/*
* tvafe open port and init register
*/
int tvafe_dec_open(struct tvin_frontend_s *fe, enum tvin_port_e port)
{
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
mutex_lock(&devp->afe_mutex);
if (devp->flags & TVAFE_FLAG_DEV_OPENED) {
tvafe_pr_err("%s(%d), %s opened already\n", __func__,
devp->index, tvin_port_str(port));
mutex_unlock(&devp->afe_mutex);
return 1;
}
if ((port < TVIN_PORT_CVBS0) || (port > TVIN_PORT_CVBS3)) {
tvafe_pr_err("%s(%d), %s unsupport\n", __func__,
devp->index, tvin_port_str(port));
mutex_unlock(&devp->afe_mutex);
return 1;
}
#ifdef CONFIG_CMA
tvafe_cma_alloc(devp);
#endif
/* init variable */
memset(tvafe, 0, sizeof(struct tvafe_info_s));
/**enable and reset tvafe clock**/
tvafe_enable_module(true);
devp->flags &= (~TVAFE_POWERDOWN_IN_IDLE);
/* set APB bus register accessing error exception */
tvafe_set_apb_bus_err_ctrl();
/**set cvd2 reset to high**/
/*tvafe_cvd2_hold_rst();?????*/
/* init tvafe registers */
tvafe_init_reg(&tvafe->cvd2, &devp->mem, port, devp->pinmux);
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AVDETECT
/*only txlx chip enabled*/
if (tvafe_cpu_type() == CPU_TYPE_TXLX ||
tvafe_cpu_type() == CPU_TYPE_TL1) {
/*synctip set to 0 when tvafe working&&av connected*/
/*enable clamp if av connected*/
if (port == TVIN_PORT_CVBS1) {
avport_opened = TVAFE_PORT_AV1;
if (av1_plugin_state == 0) {
W_APB_BIT(TVFE_CLAMP_INTF, 1,
CLAMP_EN_BIT, CLAMP_EN_WID);
/*channel1*/
tvafe_cha1_SYNCTIP_close_config();
} else
W_APB_BIT(TVFE_CLAMP_INTF, 0,
CLAMP_EN_BIT, CLAMP_EN_WID);
} else if (port == TVIN_PORT_CVBS2) {
avport_opened = TVAFE_PORT_AV2;
if (av2_plugin_state == 0) {
W_APB_BIT(TVFE_CLAMP_INTF, 1,
CLAMP_EN_BIT, CLAMP_EN_WID);
/*channel2*/
tvafe_cha2_SYNCTIP_close_config();
} else
W_APB_BIT(TVFE_CLAMP_INTF, 0,
CLAMP_EN_BIT, CLAMP_EN_WID);
} else {
avport_opened = 0;
W_APB_BIT(TVFE_CLAMP_INTF, 1,
CLAMP_EN_BIT, CLAMP_EN_WID);
}
} else
W_APB_BIT(TVFE_CLAMP_INTF, 1,
CLAMP_EN_BIT, CLAMP_EN_WID);
#else
W_APB_BIT(TVFE_CLAMP_INTF, 1,
CLAMP_EN_BIT, CLAMP_EN_WID);
#endif
tvafe->parm.port = port;
/* set the flag to enabble ioctl access */
devp->flags |= TVAFE_FLAG_DEV_OPENED;
tvafe_clk_status = true;
#ifdef CONFIG_AMLOGIC_ATV_DEMOD
g_tvafe_info = tvafe;
/* register aml_fe hook for atv search */
aml_fe_hook_cvd(tvafe_cvd2_get_atv_format, tvafe_cvd2_get_hv_lock,
tvafe_get_v_fmt);
#endif
tvafe_pr_info("%s open port:0x%x ok.\n", __func__, port);
mutex_unlock(&devp->afe_mutex);
return 0;
}
/*
* tvafe start after signal stable
*/
void tvafe_dec_start(struct tvin_frontend_s *fe, enum tvin_sig_fmt_e fmt)
{
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
enum tvin_port_e port = devp->tvafe.parm.port;
mutex_lock(&devp->afe_mutex);
manual_flag = 0;
if (!(devp->flags & TVAFE_FLAG_DEV_OPENED)) {
tvafe_pr_err("tvafe_dec_start(%d) decode havn't opened\n",
devp->index);
mutex_unlock(&devp->afe_mutex);
return;
}
if ((port < TVIN_PORT_CVBS0) || (port > TVIN_PORT_CVBS3)) {
tvafe_pr_err("%s(%d), %s unsupport\n", __func__,
devp->index, tvin_port_str(port));
mutex_unlock(&devp->afe_mutex);
return;
}
if (devp->flags & TVAFE_FLAG_DEV_STARTED) {
tvafe_pr_err("%s(%d), %s started already\n", __func__,
devp->index, tvin_port_str(port));
mutex_unlock(&devp->afe_mutex);
return;
}
/*fix vg877 machine ntsc443 flash*/
if ((fmt == TVIN_SIG_FMT_CVBS_NTSC_443) &&
((port == TVIN_PORT_CVBS1) || (port == TVIN_PORT_CVBS2)))
W_APB_REG(CVD2_H_LOOP_MAXSTATE, 0x9);
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AVDETECT
if (tvafe_cpu_type() == CPU_TYPE_TXLX ||
tvafe_cpu_type() == CPU_TYPE_TL1) {
if (port == TVIN_PORT_CVBS1)
tvafe_avin_detect_ch1_anlog_enable(0);
else if (port == TVIN_PORT_CVBS2)
tvafe_avin_detect_ch2_anlog_enable(0);
}
#endif
tvafe->parm.info.fmt = fmt;
tvafe->parm.info.status = TVIN_SIG_STATUS_STABLE;
devp->flags |= TVAFE_FLAG_DEV_STARTED;
tvafe_pr_info("%s start fmt:%s ok.\n",
__func__, tvin_sig_fmt_str(fmt));
mutex_unlock(&devp->afe_mutex);
}
/*
* tvafe stop port
*/
void tvafe_dec_stop(struct tvin_frontend_s *fe, enum tvin_port_e port)
{
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
mutex_lock(&devp->afe_mutex);
if (!(devp->flags & TVAFE_FLAG_DEV_STARTED)) {
tvafe_pr_err("tvafe_dec_stop(%d) decode havn't started\n",
devp->index);
mutex_unlock(&devp->afe_mutex);
return;
}
if ((port < TVIN_PORT_CVBS0) || (port > TVIN_PORT_CVBS3)) {
tvafe_pr_err("%s(%d), %s unsupport\n", __func__,
devp->index, tvin_port_str(port));
mutex_unlock(&devp->afe_mutex);
return;
}
/* init variable */
memset(&tvafe->cvd2.info, 0, sizeof(struct tvafe_cvd2_info_s));
memset(&tvafe->parm.info, 0, sizeof(struct tvin_info_s));
tvafe->parm.port = port;
/* need to do ... */
/** write 7740 register for cvbs clamp **/
if ((port >= TVIN_PORT_CVBS0) && (port <= TVIN_PORT_CVBS3) &&
!(devp->flags & TVAFE_POWERDOWN_IN_IDLE)) {
tvafe->cvd2.fmt_loop_cnt = 0;
/* reset loop cnt after channel switch */
#ifdef TVAFE_SET_CVBS_PGA_EN
tvafe_cvd2_reset_pga();
#endif
#ifdef TVAFE_SET_CVBS_CDTO_EN
tvafe_cvd2_set_default_cdto(&tvafe->cvd2);
#endif
tvafe_cvd2_set_default_de(&tvafe->cvd2);
}
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AVDETECT
if (tvafe_cpu_type() == CPU_TYPE_TXLX ||
tvafe_cpu_type() == CPU_TYPE_TL1) {
if (port == TVIN_PORT_CVBS1)
tvafe_avin_detect_ch1_anlog_enable(1);
else if (port == TVIN_PORT_CVBS2)
tvafe_avin_detect_ch2_anlog_enable(1);
}
#endif
devp->flags &= (~TVAFE_FLAG_DEV_STARTED);
tvafe_pr_info("%s stop port:0x%x ok.\n", __func__, port);
mutex_unlock(&devp->afe_mutex);
}
/*
* tvafe close port
*/
void tvafe_dec_close(struct tvin_frontend_s *fe)
{
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
tvafe_pr_info("%s begin to close afe.\n", __func__);
mutex_lock(&devp->afe_mutex);
if (!(devp->flags & TVAFE_FLAG_DEV_OPENED) ||
(devp->flags & TVAFE_POWERDOWN_IN_IDLE)) {
tvafe_pr_err("tvafe_dec_close(%d) decode havn't opened\n",
devp->index);
mutex_unlock(&devp->afe_mutex);
return;
}
if ((tvafe->parm.port < TVIN_PORT_CVBS0) ||
(tvafe->parm.port > TVIN_PORT_CVBS3)) {
tvafe_pr_err("%s(%d), %s unsupport\n", __func__,
devp->index, tvin_port_str(tvafe->parm.port));
mutex_unlock(&devp->afe_mutex);
return;
}
tvafe_clk_status = false;
/*del_timer_sync(&devp->timer);*/
#ifdef CONFIG_AMLOGIC_ATV_DEMOD
g_tvafe_info = NULL;
/* register aml_fe hook for atv search */
aml_fe_hook_cvd(NULL, NULL, NULL);
#endif
/**set cvd2 reset to high**/
tvafe_cvd2_hold_rst();
/**disable av out**/
tvafe_enable_avout(tvafe->parm.port, false);
#ifdef TVAFE_POWERDOWN_IN_IDLE
/**disable tvafe clock**/
devp->flags |= TVAFE_POWERDOWN_IN_IDLE;
tvafe_enable_module(false);
if (tvafe->parm.port == TVIN_PORT_CVBS3)
adc_set_pll_cntl(0, ADC_EN_ATV_DEMOD, NULL);
else if ((tvafe->parm.port >= TVIN_PORT_CVBS0) &&
(tvafe->parm.port <= TVIN_PORT_CVBS2))
adc_set_pll_cntl(0, ADC_EN_TVAFE, NULL);
#endif
#ifdef CONFIG_CMA
tvafe_cma_release(devp);
#endif
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AVDETECT
if (tvafe_cpu_type() == CPU_TYPE_TXLX ||
tvafe_cpu_type() == CPU_TYPE_TL1) {
/*avsync tip set 1 to resume av detect*/
if (tvafe->parm.port == TVIN_PORT_CVBS1) {
avport_opened = 0;
/*channel1*/
tvafe_cha1_detect_restart_config();
} else if (tvafe->parm.port == TVIN_PORT_CVBS2) {
avport_opened = 0;
/*channel2*/
tvafe_cha2_detect_restart_config();
} else {
tvafe_cha1_detect_restart_config();
tvafe_cha2_detect_restart_config();
}
}
#endif
/* init variable */
memset(tvafe, 0, sizeof(struct tvafe_info_s));
devp->flags &= (~TVAFE_FLAG_DEV_STARTED);
devp->flags &= (~TVAFE_FLAG_DEV_OPENED);
tvafe_pr_info("%s close afe ok.\n", __func__);
mutex_unlock(&devp->afe_mutex);
}
/*
* tvafe vsync interrupt function
*/
int tvafe_dec_isr(struct tvin_frontend_s *fe, unsigned int hcnt64)
{
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
enum tvin_port_e port = tvafe->parm.port;
enum tvin_aspect_ratio_e aspect_ratio = TVIN_ASPECT_NULL;
static int count[10] = {0};
int i;
if (!(devp->flags & TVAFE_FLAG_DEV_OPENED) ||
(devp->flags & TVAFE_POWERDOWN_IN_IDLE)) {
tvafe_pr_err("tvafe havn't opened, isr error!!!\n");
return TVIN_BUF_SKIP;
}
if (force_stable)
return TVIN_BUF_NULL;
/* if there is any error or overflow, do some reset, then rerurn -1;*/
if ((tvafe->parm.info.status != TVIN_SIG_STATUS_STABLE) ||
(tvafe->parm.info.fmt == TVIN_SIG_FMT_NULL)) {
if (devp->flags & TVAFE_FLAG_DEV_SNOW_FLAG)
return TVIN_BUF_NULL;
else
return TVIN_BUF_SKIP;
}
/* TVAFE CVD2 3D works abnormally => reset cvd2 */
if ((port >= TVIN_PORT_CVBS0) && (port <= TVIN_PORT_CVBS3))
tvafe_cvd2_check_3d_comb(&tvafe->cvd2);
#ifdef TVAFE_SET_CVBS_PGA_EN
if ((port >= TVIN_PORT_CVBS0) && (port <= TVIN_PORT_CVBS3) &&
(port != TVIN_PORT_CVBS3))
tvafe_cvd2_adj_pga(&tvafe->cvd2);
#endif
#ifdef TVAFE_SET_CVBS_CDTO_EN
if (tvafe->parm.info.fmt == TVIN_SIG_FMT_CVBS_PAL_I)
tvafe_cvd2_adj_cdto(&tvafe->cvd2, hcnt64);
#endif
if (tvafe->parm.info.fmt == TVIN_SIG_FMT_CVBS_PAL_I)
tvafe_cvd2_adj_hs(&tvafe->cvd2, hcnt64);
else if (tvafe->parm.info.fmt == TVIN_SIG_FMT_CVBS_NTSC_M)
tvafe_cvd2_adj_hs_ntsc(&tvafe->cvd2, hcnt64);
if ((port >= TVIN_PORT_CVBS0) && (port <= TVIN_PORT_CVBS3)) {
aspect_ratio = tvafe_cvd2_get_wss();
switch (aspect_ratio) {
case TVIN_ASPECT_NULL:
count[TVIN_ASPECT_NULL]++;
break;
case TVIN_ASPECT_1x1:
count[TVIN_ASPECT_1x1]++;
break;
case TVIN_ASPECT_4x3_FULL:
count[TVIN_ASPECT_4x3_FULL]++;
break;
case TVIN_ASPECT_14x9_FULL:
count[TVIN_ASPECT_14x9_FULL]++;
break;
case TVIN_ASPECT_14x9_LB_CENTER:
count[TVIN_ASPECT_14x9_LB_CENTER]++;
break;
case TVIN_ASPECT_14x9_LB_TOP:
count[TVIN_ASPECT_14x9_LB_TOP]++;
break;
case TVIN_ASPECT_16x9_FULL:
count[TVIN_ASPECT_16x9_FULL]++;
break;
case TVIN_ASPECT_16x9_LB_CENTER:
count[TVIN_ASPECT_16x9_LB_CENTER]++;
break;
case TVIN_ASPECT_16x9_LB_TOP:
count[TVIN_ASPECT_16x9_LB_TOP]++;
break;
case TVIN_ASPECT_MAX:
break;
}
/*over 30/40 times,ratio is effective*/
if (++(tvafe->aspect_ratio_cnt) > TVAFE_RATIO_CNT) {
for (i = 0; i < TVIN_ASPECT_MAX; i++) {
if (count[i] > 30) {
tvafe->aspect_ratio = i;
break;
}
}
for (i = 0; i < TVIN_ASPECT_MAX; i++)
count[i] = 0;
tvafe->aspect_ratio_cnt = 0;
}
}
return TVIN_BUF_NULL;
}
static struct tvin_decoder_ops_s tvafe_dec_ops = {
.support = tvafe_dec_support,
.open = tvafe_dec_open,
.start = tvafe_dec_start,
.stop = tvafe_dec_stop,
.close = tvafe_dec_close,
.decode_isr = tvafe_dec_isr,
.callmaster_det = NULL,
};
/*
* tvafe signal signal status: signal on/off
*/
bool tvafe_is_nosig(struct tvin_frontend_s *fe)
{
bool ret = false;
/* Get the per-device structure that contains this frontend */
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
enum tvin_port_e port = tvafe->parm.port;
if (!(devp->flags & TVAFE_FLAG_DEV_OPENED) ||
(devp->flags & TVAFE_POWERDOWN_IN_IDLE)) {
tvafe_pr_err("tvafe havn't opened OR suspend:flags:0x%x!!!\n",
devp->flags);
return true;
}
if (force_stable)
return ret;
if ((port >= TVIN_PORT_CVBS0) && (port <= TVIN_PORT_CVBS3)) {
ret = tvafe_cvd2_no_sig(&tvafe->cvd2, &devp->mem);
/*fix black side when config atv snow*/
if (ret && (port == TVIN_PORT_CVBS3) &&
(devp->flags & TVAFE_FLAG_DEV_SNOW_FLAG) &&
(tvafe->cvd2.config_fmt == TVIN_SIG_FMT_CVBS_PAL_I) &&
(tvafe->cvd2.info.state != TVAFE_CVD2_STATE_FIND))
tvafe_snow_config_acd();
else if ((tvafe->cvd2.config_fmt == TVIN_SIG_FMT_CVBS_PAL_I) &&
(tvafe->cvd2.info.state == TVAFE_CVD2_STATE_FIND) &&
(port == TVIN_PORT_CVBS3))
tvafe_snow_config_acd_resume();
/* normal sigal & adc reg error, reload source mux */
if (tvafe->cvd2.info.adc_reload_en && !ret)
tvafe_set_source_muxing(port, devp->pinmux);
}
return ret;
}
/*
* tvafe signal mode status: change/unchange
*/
bool tvafe_fmt_chg(struct tvin_frontend_s *fe)
{
bool ret = false;
/* Get the per-device structure that contains this frontend */
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
enum tvin_port_e port = tvafe->parm.port;
if (!(devp->flags & TVAFE_FLAG_DEV_OPENED)) {
tvafe_pr_err("tvafe havn't opened, get fmt chg error!!!\n");
return true;
}
if (force_stable)
return ret;
if ((port >= TVIN_PORT_CVBS0) && (port <= TVIN_PORT_CVBS3))
ret = tvafe_cvd2_fmt_chg(&tvafe->cvd2);
return ret;
}
/*
* tvafe adc lock status: lock/unlock
*/
bool tvafe_pll_lock(struct tvin_frontend_s *fe)
{
bool ret = true;
return ret;
}
/*
* tvafe search format number
*/
enum tvin_sig_fmt_e tvafe_get_fmt(struct tvin_frontend_s *fe)
{
enum tvin_sig_fmt_e fmt = TVIN_SIG_FMT_NULL;
/* Get the per-device structure that contains this frontend */
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
enum tvin_port_e port = tvafe->parm.port;
if (!(devp->flags & TVAFE_FLAG_DEV_OPENED)) {
tvafe_pr_err("tvafe havn't opened, get sig fmt error!!!\n");
return fmt;
}
if ((port >= TVIN_PORT_CVBS0) && (port <= TVIN_PORT_CVBS3))
fmt = tvafe_cvd2_get_format(&tvafe->cvd2);
tvafe->parm.info.fmt = fmt;
if (tvafe_dbg_enable)
tvafe_pr_info("%s fmt:%s.\n", __func__,
tvin_sig_fmt_str(fmt));
return fmt;
}
/*
* tvafe signal property: 2D/3D, color format, aspect ratio, pixel repeat
*/
void tvafe_get_sig_property(struct tvin_frontend_s *fe,
struct tvin_sig_property_s *prop)
{
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
enum tvin_port_e port = tvafe->parm.port;
unsigned int hs_adj_lev = cutwindow_val_h_level1;
unsigned int vs_adj_lev = cutwindow_val_v_level1;
if (!(devp->flags & TVAFE_FLAG_DEV_OPENED) ||
(devp->flags & TVAFE_POWERDOWN_IN_IDLE)) {
tvafe_pr_err("%s tvafe not opened OR suspend:flags:0x%x!\n",
__func__, devp->flags);
return;
}
prop->trans_fmt = TVIN_TFMT_2D;
prop->color_format = TVIN_YUV444;
prop->dest_cfmt = TVIN_YUV422;
#ifdef TVAFE_CVD2_AUTO_DE_ENABLE
if ((port >= TVIN_PORT_CVBS0) && (port <= TVIN_PORT_CVBS3)) {
if (tvafe->cvd2.info.vs_adj_en) {
if (tvafe->cvd2.info.vs_adj_level == 0)
vs_adj_lev = cutwindow_val_v_level0;
else if (tvafe->cvd2.info.vs_adj_level == 1)
vs_adj_lev = cutwindow_val_v_level1;
else if (tvafe->cvd2.info.vs_adj_level == 2)
vs_adj_lev = cutwindow_val_v_level2;
else if (tvafe->cvd2.info.vs_adj_level == 3)
vs_adj_lev = cutwindow_val_v_level3;
else if (tvafe->cvd2.info.vs_adj_level == 4)
vs_adj_lev = cutwindow_val_v_level4;
else
vs_adj_lev = 0;
prop->vs = vs_adj_lev;
prop->ve = vs_adj_lev;
} else {
prop->vs = 0;
prop->ve = 0;
}
if (tvafe->cvd2.info.hs_adj_en) {
if (tvafe->cvd2.info.hs_adj_level == 1)
hs_adj_lev = cutwindow_val_h_level1;
else if (tvafe->cvd2.info.hs_adj_level == 2)
hs_adj_lev = cutwindow_val_h_level2;
else if (tvafe->cvd2.info.hs_adj_level == 3)
hs_adj_lev = cutwindow_val_h_level3;
else if (tvafe->cvd2.info.hs_adj_level == 4) {
hs_adj_lev = cutwindow_val_h_level4;
prop->vs = cutwindow_val_v;
prop->ve = cutwindow_val_v;
} else
hs_adj_lev = 0;
if (tvafe->cvd2.info.hs_adj_dir == true) {
prop->hs = 0;
prop->he = hs_adj_lev;
} else {
prop->hs = hs_adj_lev;
prop->he = 0;
}
} else {
prop->hs = 0;
prop->he = 0;
}
}
#endif
prop->color_fmt_range = TVIN_YUV_LIMIT;
prop->aspect_ratio = tvafe->aspect_ratio;
prop->decimation_ratio = 0;
prop->dvi_info = 0;
/*4 is the test result@20171101 on fluke-54200 and DVD*/
prop->skip_vf_num = 4;
}
/*
*get cvbs secam source's phase
*/
static bool tvafe_cvbs_get_secam_phase(struct tvin_frontend_s *fe)
{
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
if (tvafe->cvd2.config_fmt == TVIN_SIG_FMT_CVBS_SECAM)
return tvafe->cvd2.hw.secam_phase;
else
return 0;
}
bool tvafe_get_snow_cfg(void)
{
return snow_cfg;
}
EXPORT_SYMBOL(tvafe_get_snow_cfg);
void tvafe_set_snow_cfg(bool cfg)
{
snow_cfg = cfg;
}
EXPORT_SYMBOL(tvafe_set_snow_cfg);
/**check frame skip,only for av input*/
static bool tvafe_cvbs_check_frame_skip(struct tvin_frontend_s *fe)
{
struct tvafe_dev_s *devp = container_of(fe, struct tvafe_dev_s,
frontend);
struct tvafe_info_s *tvafe = &devp->tvafe;
struct tvafe_cvd2_s *cvd2 = &tvafe->cvd2;
enum tvin_port_e port = tvafe->parm.port;
bool ret = false;
if (!devp->frame_skip_enable ||
(devp->flags&TVAFE_FLAG_DEV_SNOW_FLAG)) {
ret = false;
} else if ((cvd2->hw.no_sig || !cvd2->hw.h_lock || !cvd2->hw.v_lock) &&
((port >= TVIN_PORT_CVBS1) && (port <= TVIN_PORT_CVBS2))) {
if (tvafe_dbg_enable)
tvafe_pr_err("cvbs signal unstable, skip frame!!!\n");
ret = true;
}
return ret;
}
static struct tvin_state_machine_ops_s tvafe_sm_ops = {
.nosig = tvafe_is_nosig,
.fmt_changed = tvafe_fmt_chg,
.get_fmt = tvafe_get_fmt,
.fmt_config = NULL,
.adc_cal = NULL,
.pll_lock = tvafe_pll_lock,
.get_sig_property = tvafe_get_sig_property,
.vga_set_param = NULL,
.vga_get_param = NULL,
.check_frame_skip = tvafe_cvbs_check_frame_skip,
.get_secam_phase = tvafe_cvbs_get_secam_phase,
};
static int tvafe_open(struct inode *inode, struct file *file)
{
struct tvafe_dev_s *devp;
/* Get the per-device structure that contains this cdev */
devp = container_of(inode->i_cdev, struct tvafe_dev_s, cdev);
file->private_data = devp;
/* ... */
tvafe_pr_info("%s: open device\n", __func__);
return 0;
}
static int tvafe_release(struct inode *inode, struct file *file)
{
struct tvafe_dev_s *devp = file->private_data;
file->private_data = NULL;
/* Release some other fields */
/* ... */
tvafe_pr_info("tvafe: device %d release ok.\n", devp->index);
return 0;
}
static long tvafe_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
long ret = 0;
unsigned int snowcfg = 0;
void __user *argp = (void __user *)arg;
struct tvafe_dev_s *devp = file->private_data;
struct tvafe_info_s *tvafe = &devp->tvafe;
enum tvafe_cvbs_video_e cvbs_lock_status = TVAFE_CVBS_VIDEO_HV_UNLOCKED;
if (_IOC_TYPE(cmd) != _TM_T) {
tvafe_pr_err("%s invalid command: %u\n", __func__, cmd);
return -EINVAL;
}
/* tvafe_pr_info("%s command: %u\n", __func__, cmd); */
if (disableapi)
return -EPERM;
mutex_lock(&devp->afe_mutex);
if (!(devp->flags & TVAFE_FLAG_DEV_OPENED) &&
cmd != TVIN_IOC_S_AFE_SONWCFG) {
tvafe_pr_info("%s, tvafe device is disable, ignore the command %d\n",
__func__, cmd);
mutex_unlock(&devp->afe_mutex);
return -EPERM;
}
switch (cmd) {
case TVIN_IOC_LOAD_REG:
{
if (copy_from_user(&tvaferegs, argp,
sizeof(struct am_regs_s))) {
tvafe_pr_info("load reg errors: can't get buffer length\n");
ret = -EINVAL;
break;
}
if (!tvaferegs.length || (tvaferegs.length > 512)) {
tvafe_pr_info("load regs error: buffer length overflow!!!, length=0x%x\n",
tvaferegs.length);
ret = -EINVAL;
break;
}
if (enable_db_reg)
tvafe_set_regmap(&tvaferegs);
break;
}
case TVIN_IOC_S_AFE_SONWCFG:
/*tl1/txhd tvconfig snow en/disable*/
if (copy_from_user(&snowcfg, argp,
sizeof(unsigned int))) {
tvafe_pr_info("snowcfg: get param err\n");
ret = -EINVAL;
break;
}
if (snowcfg == 1)
tvafe_set_snow_cfg(true);
else
tvafe_set_snow_cfg(false);
tvafe_pr_info("tvconfig snow:%d\n", snow_cfg);
break;
case TVIN_IOC_S_AFE_SONWON:
devp->flags |= TVAFE_FLAG_DEV_SNOW_FLAG;
tvafe_snow_function_flag = true;
tvafe_snow_config(1);
tvafe_snow_config_clamp(1);
if (tvafe_dbg_enable)
tvafe_pr_info("TVIN_IOC_S_AFE_SONWON\n");
break;
case TVIN_IOC_S_AFE_SONWOFF:
tvafe_snow_config(0);
tvafe_snow_config_clamp(0);
devp->flags &= (~TVAFE_FLAG_DEV_SNOW_FLAG);
if (tvafe_dbg_enable)
tvafe_pr_info("TVIN_IOC_S_AFE_SONWOFF\n");
break;
case TVIN_IOC_G_AFE_CVBS_LOCK:
{
cvbs_lock_status =
tvafe_cvd2_get_lock_status(&tvafe->cvd2);
if (copy_to_user(argp,
&cvbs_lock_status, sizeof(int))) {
ret = -EFAULT;
break;
}
tvafe_pr_info("%s: get cvd2 lock status :%d.\n",
__func__, cvbs_lock_status);
break;
}
case TVIN_IOC_S_AFE_CVBS_STD:
{
enum tvin_sig_fmt_e fmt = TVIN_SIG_FMT_NULL;
if (copy_from_user(&fmt, argp,
sizeof(enum tvin_sig_fmt_e))) {
ret = -EFAULT;
break;
}
tvafe->cvd2.manual_fmt = fmt;
tvafe_pr_info("%s: ioctl set cvd2 manual fmt:%s.\n",
__func__, tvin_sig_fmt_str(fmt));
if (fmt != TVIN_SIG_FMT_NULL)
manual_flag = 1;
break;
}
case TVIN_IOC_G_AFE_CVBS_STD:
{
enum tvin_sig_fmt_e fmt = TVIN_SIG_FMT_NULL;
if (tvafe->cvd2.info.state == TVAFE_CVD2_STATE_FIND)
fmt = tvafe->cvd2.config_fmt;
if (copy_to_user(argp, &fmt,
sizeof(enum tvin_sig_fmt_e)))
ret = -EFAULT;
tvafe_pr_info("%s: ioctl get fmt:%s.\n",
__func__, tvin_sig_fmt_str(fmt));
break;
}
default:
ret = -ENOIOCTLCMD;
break;
}
mutex_unlock(&devp->afe_mutex);
return ret;
}
#ifdef CONFIG_COMPAT
static long tvafe_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
unsigned long ret;
arg = (unsigned long)compat_ptr(arg);
ret = tvafe_ioctl(file, cmd, arg);
return ret;
}
#endif
/* File operations structure. Defined in linux/fs.h */
static const struct file_operations tvafe_fops = {
.owner = THIS_MODULE, /* Owner */
.open = tvafe_open, /* Open method */
.release = tvafe_release, /* Release method */
.unlocked_ioctl = tvafe_ioctl, /* Ioctl method */
#ifdef CONFIG_COMPAT
.compat_ioctl = tvafe_compat_ioctl,
#endif
/* ... */
};
static int tvafe_add_cdev(struct cdev *cdevp,
const struct file_operations *fops, int minor)
{
int ret;
dev_t devno = MKDEV(MAJOR(tvafe_devno), minor);
cdev_init(cdevp, fops);
cdevp->owner = THIS_MODULE;
ret = cdev_add(cdevp, devno, 1);
return ret;
}
static struct device *tvafe_create_device(struct device *parent, int id)
{
dev_t devno = MKDEV(MAJOR(tvafe_devno), id);
return device_create(tvafe_clsp, parent, devno, NULL, "%s0",
TVAFE_DEVICE_NAME);
/* @to do this after Middleware API modified */
/*return device_create(tvafe_clsp, parent, devno, NULL, "%s",*/
/* TVAFE_DEVICE_NAME); */
}
static void tvafe_delete_device(int minor)
{
dev_t devno = MKDEV(MAJOR(tvafe_devno), minor);
device_destroy(tvafe_clsp, devno);
}
void __iomem *tvafe_reg_base;
int tvafe_reg_read(unsigned int reg, unsigned int *val)
{
*val = readl(tvafe_reg_base+reg);
return 0;
}
EXPORT_SYMBOL(tvafe_reg_read);
int tvafe_reg_write(unsigned int reg, unsigned int val)
{
writel(val, (tvafe_reg_base+reg));
return 0;
}
EXPORT_SYMBOL(tvafe_reg_write);
int tvafe_vbi_reg_read(unsigned int reg, unsigned int *val)
{
if (tvafe_clk_status)
*val = readl(tvafe_reg_base+reg);
else
return -1;
return 0;
}
EXPORT_SYMBOL(tvafe_vbi_reg_read);
int tvafe_vbi_reg_write(unsigned int reg, unsigned int val)
{
if (tvafe_clk_status)
writel(val, (tvafe_reg_base+reg));
else
return -1;
return 0;
}
EXPORT_SYMBOL(tvafe_vbi_reg_write);
int tvafe_hiu_reg_read(unsigned int reg, unsigned int *val)
{
*val = aml_read_hiubus(reg);
return 0;
}
EXPORT_SYMBOL(tvafe_hiu_reg_read);
int tvafe_hiu_reg_write(unsigned int reg, unsigned int val)
{
aml_write_hiubus(reg, val);
return 0;
}
EXPORT_SYMBOL(tvafe_hiu_reg_write);
int tvafe_cpu_type(void)
{
return s_tvafe_data->cpu_id;
}
EXPORT_SYMBOL(tvafe_cpu_type);
void tvafe_clk_gate_ctrl(int status)
{
if (status) {
if (tvafe_clkgate.clk_gate_state) {
tvafe_pr_info("clk_gate is already on\n");
return;
}
if (IS_ERR(tvafe_clkgate.vdac_clk_gate))
tvafe_pr_err("error: %s: vdac_clk_gate\n", __func__);
else
clk_prepare_enable(tvafe_clkgate.vdac_clk_gate);
tvafe_clkgate.clk_gate_state = 1;
} else {
if (tvafe_clkgate.clk_gate_state == 0) {
tvafe_pr_info("clk_gate is already off\n");
return;
}
if (IS_ERR(tvafe_clkgate.vdac_clk_gate))
tvafe_pr_err("error: %s: vdac_clk_gate\n", __func__);
else
clk_disable_unprepare(tvafe_clkgate.vdac_clk_gate);
tvafe_clkgate.clk_gate_state = 0;
}
}
static void tvafe_clktree_probe(struct device *dev)
{
tvafe_clkgate.clk_gate_state = 0;
tvafe_clkgate.vdac_clk_gate = devm_clk_get(dev, "vdac_clk_gate");
if (IS_ERR(tvafe_clkgate.vdac_clk_gate))
tvafe_pr_err("error: %s: clk vdac_clk_gate\n", __func__);
}
struct meson_tvafe_data meson_gxtvbb_tvafe_data = {
.cpu_id = CPU_TYPE_GXTVBB,
.name = "meson-gxtvbb-tvafe",
};
struct meson_tvafe_data meson_txl_tvafe_data = {
.cpu_id = CPU_TYPE_TXL,
.name = "meson-txl-tvafe",
};
struct meson_tvafe_data meson_txlx_tvafe_data = {
.cpu_id = CPU_TYPE_TXLX,
.name = "meson-txlx-tvafe",
};
struct meson_tvafe_data meson_txhd_tvafe_data = {
.cpu_id = CPU_TYPE_TXHD,
.name = "meson-txhd-tvafe",
};
struct meson_tvafe_data meson_tl1_tvafe_data = {
.cpu_id = CPU_TYPE_TL1,
.name = "meson-tl1-tvafe",
};
static const struct of_device_id meson_tvafe_dt_match[] = {
{
.compatible = "amlogic, tvafe-gxtvbb",
.data = &meson_gxtvbb_tvafe_data,
}, {
.compatible = "amlogic, tvafe-txl",
.data = &meson_txl_tvafe_data,
}, {
.compatible = "amlogic, tvafe-txlx",
.data = &meson_txlx_tvafe_data,
}, {
.compatible = "amlogic, tvafe-txhd",
.data = &meson_txhd_tvafe_data,
}, {
.compatible = "amlogic, tvafe-tl1",
.data = &meson_tl1_tvafe_data,
},
{},
};
static unsigned int tvafe_use_reserved_mem;
static struct resource tvafe_memobj;
static int tvafe_drv_probe(struct platform_device *pdev)
{
int ret = 0;
struct tvafe_dev_s *tdevp;
int size_io_reg;
/*const void *name;*/
/*int offset, size, mem_size_m;*/
struct resource *res;
const struct of_device_id *match;
/* struct tvin_frontend_s * frontend; */
match = of_match_device(meson_tvafe_dt_match, &pdev->dev);
if (match == NULL) {
tvafe_pr_err("%s,no matched table\n", __func__);
return -1;
}
s_tvafe_data = (struct meson_tvafe_data *)match->data;
tvafe_pr_info("%s:cpu_id:%d,name:%s\n", __func__,
s_tvafe_data->cpu_id, s_tvafe_data->name);
tvafe_clktree_probe(&pdev->dev);
/* allocate memory for the per-device structure */
tdevp = kzalloc(sizeof(struct tvafe_dev_s), GFP_KERNEL);
if (!tdevp)
goto fail_kzalloc_tdev;
if (pdev->dev.of_node) {
ret = of_property_read_u32(pdev->dev.of_node,
"tvafe_id", &(tdevp->index));
if (ret) {
tvafe_pr_err("Can't find tvafe id.\n");
goto fail_get_id;
}
}
tdevp->flags = 0;
/* create cdev and reigser with sysfs */
ret = tvafe_add_cdev(&tdevp->cdev, &tvafe_fops, tdevp->index);
if (ret) {
tvafe_pr_err("%s: failed to add cdev\n", __func__);
goto fail_add_cdev;
}
/* create /dev nodes */
tdevp->dev = tvafe_create_device(&pdev->dev, tdevp->index);
if (IS_ERR(tdevp->dev)) {
tvafe_pr_err("failed to create device node\n");
/* @todo do with error */
ret = PTR_ERR(tdevp->dev);
goto fail_create_device;
}
/*create sysfs attribute files*/
ret = tvafe_device_create_file(tdevp->dev);
if (ret < 0) {
tvafe_pr_err("%s: create attribute files fail.\n",
__func__);
goto fail_create_dbg_file;
}
/* get device memory */
/* res = platform_get_resource(pdev, IORESOURCE_MEM, 0); */
res = &tvafe_memobj;
ret = of_reserved_mem_device_init(&pdev->dev);
if (ret == 0)
tvafe_pr_info("tvafe memory resource done.\n");
else
tvafe_pr_info("can't get memory resource\n");
#ifdef CONFIG_CMA
if (!tvafe_use_reserved_mem) {
ret = of_property_read_u32(pdev->dev.of_node,
"flag_cma", &(tdevp->cma_config_flag));
if (ret) {
tvafe_pr_err("don't find match flag_cma\n");
tdevp->cma_config_flag = 0;
}
if (tdevp->cma_config_flag == 1) {
ret = of_property_read_u32(pdev->dev.of_node,
"cma_size", &(tdevp->cma_mem_size));
if (ret)
tvafe_pr_err("don't find match cma_size\n");
else
tdevp->cma_mem_size *= SZ_1M;
} else if (tdevp->cma_config_flag == 0)
tdevp->cma_mem_size =
dma_get_cma_size_int_byte(&pdev->dev);
tdevp->this_pdev = pdev;
tdevp->cma_mem_alloc = 0;
tdevp->cma_config_en = 1;
tvafe_pr_info("cma_mem_size = %d MB\n",
(u32)tdevp->cma_mem_size/SZ_1M);
}
#endif
tvafe_use_reserved_mem = 0;
if (tdevp->cma_config_en != 1) {
tdevp->mem.start = res->start;
tdevp->mem.size = res->end - res->start + 1;
tvafe_pr_info("tvafe cvd memory addr is:0x%x, cvd mem_size is:0x%x .\n",
tdevp->mem.start,
tdevp->mem.size);
}
if (of_property_read_u32_array(pdev->dev.of_node, "tvafe_pin_mux",
(u32 *)tvafe_pinmux.pin, TVAFE_SRC_SIG_MAX_NUM)) {
tvafe_pr_err("Can't get pinmux data.\n");
}
tdevp->pinmux = &tvafe_pinmux;
if (!tdevp->pinmux) {
tvafe_pr_err("tvafe: no platform data!\n");
ret = -ENODEV;
}
if (of_get_property(pdev->dev.of_node, "pinctrl-names", NULL)) {
struct pinctrl *p = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(p))
tvafe_pr_err("tvafe request pinmux error!\n");
}
/*reg mem*/
tvafe_pr_info("%s:tvafe start get ioremap .\n", __func__);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "missing memory resource\n");
return -ENODEV;
}
size_io_reg = resource_size(res);
tvafe_pr_info("%s:tvafe reg base=%p,size=%x\n",
__func__, (void *)res->start, size_io_reg);
if (!devm_request_mem_region(&pdev->dev,
res->start, size_io_reg, pdev->name)) {
dev_err(&pdev->dev, "Memory region busy\n");
return -EBUSY;
}
tvafe_reg_base =
devm_ioremap_nocache(&pdev->dev, res->start, size_io_reg);
if (!tvafe_reg_base) {
dev_err(&pdev->dev, "tvafe ioremap failed\n");
return -ENOMEM;
}
tvafe_pr_info("%s: tvafe maped reg_base =%p, size=%x\n",
__func__, tvafe_reg_base, size_io_reg);
/* frontend */
tvin_frontend_init(&tdevp->frontend, &tvafe_dec_ops,
&tvafe_sm_ops, tdevp->index);
sprintf(tdevp->frontend.name, "%s", TVAFE_NAME);
tvin_reg_frontend(&tdevp->frontend);
mutex_init(&tdevp->afe_mutex);
mutex_init(&pll_mutex);
dev_set_drvdata(tdevp->dev, tdevp);
platform_set_drvdata(pdev, tdevp);
/**disable tvafe clock**/
tdevp->flags |= TVAFE_POWERDOWN_IN_IDLE;
tvafe_enable_module(false);
/*init tvafe param*/
tdevp->frame_skip_enable = 1;
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AVDETECT
avport_opened = 0;
av1_plugin_state = 0;
av2_plugin_state = 0;
#endif
tdevp->sizeof_tvafe_dev_s = sizeof(struct tvafe_dev_s);
disableapi = false;
force_stable = false;
tvafe_pr_info("driver probe ok\n");
return 0;
fail_create_dbg_file:
tvafe_delete_device(tdevp->index);
fail_create_device:
cdev_del(&tdevp->cdev);
fail_add_cdev:
fail_get_id:
kfree(tdevp);
fail_kzalloc_tdev:
tvafe_pr_err("tvafe: kzalloc memory failed.\n");
return ret;
}
static int tvafe_drv_remove(struct platform_device *pdev)
{
struct tvafe_dev_s *tdevp;
tdevp = platform_get_drvdata(pdev);
if (tvafe_reg_base) {
devm_iounmap(&pdev->dev, tvafe_reg_base);
devm_release_mem_region(&pdev->dev, tvafe_memobj.start,
resource_size(&tvafe_memobj));
}
mutex_destroy(&tdevp->afe_mutex);
mutex_destroy(&pll_mutex);
tvin_unreg_frontend(&tdevp->frontend);
tvafe_remove_device_files(tdevp->dev);
tvafe_delete_device(tdevp->index);
cdev_del(&tdevp->cdev);
kfree(tdevp);
tvafe_pr_info("driver removed ok.\n");
return 0;
}
#ifdef CONFIG_PM
static int tvafe_drv_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct tvafe_dev_s *tdevp;
struct tvafe_info_s *tvafe;
tdevp = platform_get_drvdata(pdev);
tvafe = &tdevp->tvafe;
/* close afe port first */
if (tdevp->flags & TVAFE_FLAG_DEV_OPENED) {
tvafe_pr_info("suspend module, close afe port first\n");
/* tdevp->flags &= (~TVAFE_FLAG_DEV_OPENED); */
/*del_timer_sync(&tdevp->timer);*/
/**set cvd2 reset to high**/
tvafe_cvd2_hold_rst();
/**disable av out**/
tvafe_enable_avout(tvafe->parm.port, false);
}
/*disable and reset tvafe clock*/
tdevp->flags |= TVAFE_POWERDOWN_IN_IDLE;
tvafe_clk_status = false;
tvafe_enable_module(false);
adc_set_pll_reset();
tvafe_pr_info("suspend module\n");
return 0;
}
static int tvafe_drv_resume(struct platform_device *pdev)
{
struct tvafe_dev_s *tdevp;
tdevp = platform_get_drvdata(pdev);
/*disable and reset tvafe clock*/
adc_set_pll_reset();
tvafe_enable_module(true);
tdevp->flags &= (~TVAFE_POWERDOWN_IN_IDLE);
tvafe_clk_status = false;
tvafe_pr_info("resume module\n");
return 0;
}
#endif
static void tvafe_drv_shutdown(struct platform_device *pdev)
{
if (tvafe_cpu_type() == CPU_TYPE_TL1) {
W_APB_BIT(TVFE_VAFE_CTRL0, 0, 19, 1);
W_APB_BIT(TVFE_VAFE_CTRL1, 0, 8, 1);
}
adc_pll_down();
tvafe_pr_info("tvafe_drv_shutdown ok.\n");
}
static struct platform_driver tvafe_driver = {
.probe = tvafe_drv_probe,
.remove = tvafe_drv_remove,
#ifdef CONFIG_PM
.suspend = tvafe_drv_suspend,
.resume = tvafe_drv_resume,
#endif
.shutdown = tvafe_drv_shutdown,
.driver = {
.name = TVAFE_DRIVER_NAME,
.of_match_table = meson_tvafe_dt_match,
}
};
static int __init tvafe_drv_init(void)
{
int ret = 0;
ret = alloc_chrdev_region(&tvafe_devno, 0, 1, TVAFE_NAME);
if (ret < 0) {
tvafe_pr_err("%s: failed to allocate major number\n", __func__);
goto fail_alloc_cdev_region;
}
tvafe_pr_info("%s: major %d\n", __func__, MAJOR(tvafe_devno));
tvafe_clsp = class_create(THIS_MODULE, TVAFE_NAME);
if (IS_ERR(tvafe_clsp)) {
ret = PTR_ERR(tvafe_clsp);
tvafe_pr_err("%s: failed to create class\n", __func__);
goto fail_class_create;
}
ret = platform_driver_register(&tvafe_driver);
if (ret != 0) {
tvafe_pr_err("%s: failed to register driver\n", __func__);
goto fail_pdrv_register;
}
tvafe_pr_info("tvafe_drv_init.\n");
return 0;
fail_pdrv_register:
class_destroy(tvafe_clsp);
fail_class_create:
unregister_chrdev_region(tvafe_devno, 1);
fail_alloc_cdev_region:
return ret;
}
static void __exit tvafe_drv_exit(void)
{
class_destroy(tvafe_clsp);
unregister_chrdev_region(tvafe_devno, 1);
platform_driver_unregister(&tvafe_driver);
tvafe_pr_info("tvafe_drv_exit.\n");
}
static int tvafe_mem_device_init(struct reserved_mem *rmem,
struct device *dev)
{
s32 ret = 0;
struct resource *res = NULL;
if (!rmem) {
tvafe_pr_info("Can't get reverse mem!\n");
ret = -EFAULT;
return ret;
}
res = &tvafe_memobj;
res->start = rmem->base;
res->end = rmem->base + rmem->size - 1;
if (rmem->size >= 0x500000)
tvafe_use_reserved_mem = 1;
tvafe_pr_info("init tvafe memsource 0x%lx->0x%lx tvafe_use_reserved_mem:%d\n",
(unsigned long)res->start, (unsigned long)res->end,
tvafe_use_reserved_mem);
return 0;
}
static const struct reserved_mem_ops rmem_tvafe_ops = {
.device_init = tvafe_mem_device_init,
};
static int __init tvafe_mem_setup(struct reserved_mem *rmem)
{
rmem->ops = &rmem_tvafe_ops;
tvafe_pr_info("share mem setup\n");
return 0;
}
module_init(tvafe_drv_init);
module_exit(tvafe_drv_exit);
RESERVEDMEM_OF_DECLARE(tvafe, "amlogic, tvafe_memory",
tvafe_mem_setup);
MODULE_VERSION(TVAFE_VER);
/*only for develop debug*/
#ifdef TVAFE_DEBUG
module_param(cutwindow_val_v, int, 0664);
MODULE_PARM_DESC(cutwindow_val_v, "cutwindow_val_v");
module_param(cutwindow_val_v_level0, int, 0664);
MODULE_PARM_DESC(cutwindow_val_v_level0, "cutwindow_val_v_level0");
module_param(cutwindow_val_v_level1, int, 0664);
MODULE_PARM_DESC(cutwindow_val_v_level1, "cutwindow_val_v_level1");
module_param(cutwindow_val_v_level2, int, 0664);
MODULE_PARM_DESC(cutwindow_val_v_level2, "cutwindow_val_v_level2");
module_param(cutwindow_val_v_level3, int, 0664);
MODULE_PARM_DESC(cutwindow_val_v_level3, "cutwindow_val_v_level3");
module_param(cutwindow_val_v_level4, int, 0664);
MODULE_PARM_DESC(cutwindow_val_v_level4, "cutwindow_val_v_level4");
module_param(cutwindow_val_h_level1, int, 0664);
MODULE_PARM_DESC(cutwindow_val_h_level1, "cutwindow_val_h_level1");
module_param(cutwindow_val_h_level2, int, 0664);
MODULE_PARM_DESC(cutwindow_val_h_level2, "cutwindow_val_h_level2");
module_param(cutwindow_val_h_level3, int, 0664);
MODULE_PARM_DESC(cutwindow_val_h_level3, "cutwindow_val_h_level3");
module_param(cutwindow_val_h_level4, int, 0664);
MODULE_PARM_DESC(cutwindow_val_h_level4, "cutwindow_val_h_level4");
#endif
MODULE_DESCRIPTION("AMLOGIC TVAFE driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Xu Lin <lin.xu@amlogic.com>");