blob: 64ed944cd3c98be3f79f54f92d641444d7b0f753 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/i2c.h>
#include <uapi/linux/dvb/frontend.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/aml_atvdemod.h>
#include <linux/amlogic/media/vout/vdac_dev.h>
#include <linux/amlogic/media/frame_provider/tvin/tvin.h>
#include <tuner-i2c.h>
#include <dvb_frontend.h>
#include "atvdemod_func.h"
#include "atvauddemod_func.h"
#include "atv_demod_debug.h"
#include "atv_demod_driver.h"
#include "atv_demod_ops.h"
#include "atv_demod_v4l2.h"
#include "atv_demod_afc.h"
#include "atv_demod_monitor.h"
#include "atv_demod_isr.h"
#include "atv_demod_ext.h"
#define DEVICE_NAME "aml_atvdemod"
static DEFINE_MUTEX(atv_demod_list_mutex);
static LIST_HEAD(hybrid_tuner_instance_list);
unsigned int reg_23cf = 0x88188832; /*IIR filter*/
unsigned int btsc_sap_mode = 1; /*0: off 1:monitor 2:auto */
/*
* add interface for audio driver to get atv audio state.
* state:
* 0 - no data.
* 1 - has data.
*/
void aml_fe_get_atvaudio_state(int *state)
{
#if 0 /* delay notification stable */
static unsigned int count;
static bool mute = true;
#endif
int av_status = 0;
unsigned int power = 0;
int vpll_lock = 0;
int line_lock = 0;
struct atv_demod_priv *priv = amlatvdemod_devp != NULL
? amlatvdemod_devp->v4l2_fe.fe.analog_demod_priv : NULL;
if (priv == NULL) {
pr_audio("priv == NULL\n");
*state = 0;
return;
}
#ifdef CONFIG_AMLOGIC_MEDIA_VDIN
av_status = tvin_get_av_status();
#endif
/* scan mode need mute */
if (priv->state == ATVDEMOD_STATE_WORK
&& !priv->scanning
&& !priv->standby
&& av_status) {
retrieve_vpll_carrier_lock(&vpll_lock);
retrieve_vpll_carrier_line_lock(&line_lock);
if ((vpll_lock == 0) && (line_lock == 0)) {
/* retrieve_vpll_carrier_audio_power(&power, 1); */
*state = 1;
} else {
*state = 0;
pr_audio("vpll_lock: 0x%x, line_lock: 0x%x\n",
vpll_lock, line_lock);
}
} else {
*state = 0;
pr_audio("ATV state[%d], scan[%d], standby[%d], av[%d].\n",
priv->state, priv->scanning,
priv->standby, av_status);
}
/* If the atv signal is locked, it means there is audio data,
* so no need to check the power value.
*/
#if 0
if (power >= 150)
*state = 1;
else
*state = 0;
#endif
#if 0 /* delay notification stable */
if (*state) {
if (mute) {
count++;
if (count > 100) {
count = 0;
mute = false;
} else {
*state = 0;
}
} else {
count = 0;
}
} else {
count = 0;
mute = true;
}
#endif
pr_audio("%s: %d, power = %d\n", __func__, *state, power);
}
EXPORT_SYMBOL_GPL(aml_fe_get_atvaudio_state);
int aml_atvdemod_get_btsc_sap_mode(void)
{
return btsc_sap_mode;
}
static bool atvdemod_check_exited(struct v4l2_frontend *v4l2_fe)
{
struct dvb_frontend *fe = &v4l2_fe->fe;
struct atv_demod_priv *priv = fe->analog_demod_priv;
if (priv->state != ATVDEMOD_STATE_WORK ||
v4l2_fe->async_tune_needexit(v4l2_fe)) {
pr_err("%s: need to exit.\n", __func__);
return true;
}
return false;
}
int atv_demod_enter_mode(struct dvb_frontend *fe)
{
int err_code = 0;
struct atv_demod_priv *priv = fe->analog_demod_priv;
atvdemod_power_switch(true);
if (amlatvdemod_devp->pin_name != NULL) {
amlatvdemod_devp->agc_pin =
devm_pinctrl_get_select(amlatvdemod_devp->dev,
amlatvdemod_devp->pin_name);
if (IS_ERR(amlatvdemod_devp->agc_pin)) {
amlatvdemod_devp->agc_pin = NULL;
pr_err("%s: get agc pins fail\n", __func__);
}
}
#ifdef CONFIG_AMLOGIC_MEDIA_ADC
err_code = adc_set_pll_cntl(1, ADC_ATV_DEMOD, NULL);
if (err_code) {
pr_dbg("%s: adc set pll error %d.\n", __func__, err_code);
if (!IS_ERR_OR_NULL(amlatvdemod_devp->agc_pin)) {
devm_pinctrl_put(amlatvdemod_devp->agc_pin);
amlatvdemod_devp->agc_pin = NULL;
}
atvdemod_power_switch(false);
return -1;
}
adc_set_filter_ctrl(true, FILTER_ATV_DEMOD, NULL);
#else
if (!IS_ERR_OR_NULL(amlatvdemod_devp->agc_pin)) {
devm_pinctrl_put(amlatvdemod_devp->agc_pin);
amlatvdemod_devp->agc_pin = NULL;
}
atvdemod_power_switch(false);
pr_dbg("%s: error, adc pll is not enabled.\n", __func__);
return -1;
#endif
#ifdef CONFIG_AMLOGIC_VDAC
vdac_enable(1, VDAC_MODULE_AVOUT_ATV);
#else
if (!IS_ERR_OR_NULL(amlatvdemod_devp->agc_pin)) {
devm_pinctrl_put(amlatvdemod_devp->agc_pin);
amlatvdemod_devp->agc_pin = NULL;
}
atvdemod_power_switch(false);
#ifdef CONFIG_AMLOGIC_MEDIA_ADC
adc_set_pll_cntl(0, ADC_ATV_DEMOD, NULL);
adc_set_filter_ctrl(false, FILTER_ATV_DEMOD, NULL);
#endif
pr_dbg("%s: error, vdac is not enabled.\n", __func__);
return -1;
#endif
adc_set_filter_ctrl(true, FILTER_ATV_DEMOD, NULL);
usleep_range(2000, 2100);
atvdemod_clk_init();
/* err_code = atvdemod_init(); */
if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXLX)) {
aud_demod_clk_gate(1);
/* atvauddemod_init(); */
}
if (priv->isr.enable)
priv->isr.enable(&priv->isr);
amlatvdemod_devp->std = 0;
amlatvdemod_devp->audmode = 0;
amlatvdemod_devp->sound_mode = 0xFF;
pr_info("%s: OK.\n", __func__);
return err_code;
}
int atv_demod_leave_mode(struct dvb_frontend *fe)
{
struct atv_demod_priv *priv = fe->analog_demod_priv;
priv->state = ATVDEMOD_STATE_IDEL;
priv->standby = true;
usleep_range(30 * 1000, 30 * 1000 + 100);
if (priv->isr.disable)
priv->isr.disable(&priv->isr);
if (priv->afc.disable)
priv->afc.disable(&priv->afc);
if (priv->monitor.disable)
priv->monitor.disable(&priv->monitor);
atvdemod_uninit();
if (!IS_ERR_OR_NULL(amlatvdemod_devp->agc_pin)) {
devm_pinctrl_put(amlatvdemod_devp->agc_pin);
amlatvdemod_devp->agc_pin = NULL;
}
#ifdef CONFIG_AMLOGIC_VDAC
vdac_enable(0, VDAC_MODULE_AVOUT_ATV);
#endif
#ifdef CONFIG_AMLOGIC_MEDIA_ADC
adc_set_pll_cntl(0, ADC_ATV_DEMOD, NULL);
adc_set_filter_ctrl(false, FILTER_ATV_DEMOD, NULL);
#endif
if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXLX))
aud_demod_clk_gate(0);
amlatvdemod_devp->std = 0;
amlatvdemod_devp->audmode = 0;
amlatvdemod_devp->sound_mode = 0xFF;
atvdemod_power_switch(false);
pr_info("%s: OK.\n", __func__);
return 0;
}
static void atv_demod_set_params(struct dvb_frontend *fe,
struct analog_parameters *params)
{
int ret = -1;
u32 if_info[2] = { 0 };
struct atv_demod_priv *priv = fe->analog_demod_priv;
struct atv_demod_parameters *p = &priv->atvdemod_param;
struct analog_parameters ptuner = { 0 };
priv->standby = true;
/* tuner config, no need cvbs format, just audio mode. */
ptuner.frequency = params->frequency;
ptuner.mode = params->mode;
ptuner.audmode = params->audmode;
ptuner.std = (params->std & 0xFF000000) | ptuner.audmode;
/* afc tune disable,must cancel wq before set tuner freq*/
if (priv->afc.pause)
priv->afc.pause(&priv->afc);
if (priv->monitor.pause)
priv->monitor.pause(&priv->monitor);
if (fe->ops.tuner_ops.set_analog_params)
ret = fe->ops.tuner_ops.set_analog_params(fe, &ptuner);
if (fe->ops.tuner_ops.get_if_frequency)
ret = fe->ops.tuner_ops.get_if_frequency(fe, if_info);
p->param.frequency = params->frequency;
p->param.mode = params->mode;
p->param.audmode = params->audmode;
p->param.std = params->std;
p->last_frequency = params->frequency;
p->if_inv = if_info[0];
p->if_freq = if_info[1];
atvdemod_init(priv);
if (!priv->scanning)
atvauddemod_init();
/* afc tune enable */
/* analog_search_flag == 0 or afc_range != 0 means searching */
if ((fe->ops.info.type == FE_ANALOG)
&& (priv->scanning == false)
&& (p->param.mode == 0)) {
if (priv->afc.enable && non_std_en == 0)
priv->afc.enable(&priv->afc);
if (priv->monitor.enable && non_std_en == 0)
priv->monitor.enable(&priv->monitor);
/* for searching mute audio */
priv->standby = false;
pr_dbg("%s: frequency %d.\n", __func__, p->param.frequency);
}
}
static int atv_demod_has_signal(struct dvb_frontend *fe, u16 *signal)
{
int vpll_lock = 0;
int line_lock = 0;
retrieve_vpll_carrier_lock(&vpll_lock);
/* add line lock status for atv scan */
retrieve_vpll_carrier_line_lock(&line_lock);
if (vpll_lock == 0 && line_lock == 0) {
*signal = V4L2_HAS_LOCK;
pr_info("%s locked [vpll_lock: 0x%x, line_lock:0x%x]\n",
__func__, vpll_lock, line_lock);
} else {
*signal = V4L2_TIMEDOUT;
pr_dbg("%s unlocked [vpll_lock: 0x%x, line_lock:0x%x]\n",
__func__, vpll_lock, line_lock);
}
return 0;
}
static void atv_demod_standby(struct dvb_frontend *fe)
{
struct atv_demod_priv *priv = fe->analog_demod_priv;
if (priv->state != ATVDEMOD_STATE_IDEL) {
atv_demod_leave_mode(fe);
priv->state = ATVDEMOD_STATE_SLEEP;
priv->standby = true;
}
pr_info("%s: OK.\n", __func__);
}
static void atv_demod_tuner_status(struct dvb_frontend *fe)
{
pr_info("%s.\n", __func__);
}
static int atv_demod_get_afc(struct dvb_frontend *fe, s32 *afc)
{
*afc = retrieve_vpll_carrier_afc();
return 0;
}
static void atv_demod_release(struct dvb_frontend *fe)
{
int instance = 0;
struct atv_demod_priv *priv = fe->analog_demod_priv;
mutex_lock(&atv_demod_list_mutex);
atv_demod_leave_mode(fe);
if (priv) {
if (amlatvdemod_devp->irq > 0)
atv_demod_isr_uninit(&priv->isr);
instance = hybrid_tuner_release_state(priv);
}
if (instance == 0)
fe->analog_demod_priv = NULL;
mutex_unlock(&atv_demod_list_mutex);
pr_info("%s: OK.\n", __func__);
}
static int atv_demod_set_config(struct dvb_frontend *fe, void *priv_cfg)
{
int ret = 0;
int *state = (int *) priv_cfg;
struct atv_demod_priv *priv = fe->analog_demod_priv;
if (!state) {
pr_err("%s: state == NULL.\n", __func__);
return -1;
}
mutex_lock(&atv_demod_list_mutex);
switch (*state) {
case AML_ATVDEMOD_INIT:
if (priv->state != ATVDEMOD_STATE_WORK) {
priv->standby = true;
if (fe->ops.tuner_ops.set_config)
fe->ops.tuner_ops.set_config(fe, NULL);
ret = atv_demod_enter_mode(fe);
if (!ret) {
priv->state = ATVDEMOD_STATE_WORK;
} else {
if (fe->ops.tuner_ops.release)
fe->ops.tuner_ops.release(fe);
}
}
break;
case AML_ATVDEMOD_UNINIT:
if (priv->state != ATVDEMOD_STATE_IDEL) {
atv_demod_leave_mode(fe);
if (fe->ops.tuner_ops.release)
fe->ops.tuner_ops.release(fe);
}
break;
case AML_ATVDEMOD_RESUME:
if (priv->state == ATVDEMOD_STATE_SLEEP) {
ret = atv_demod_enter_mode(fe);
if (!ret) {
priv->state = ATVDEMOD_STATE_WORK;
priv->standby = false;
}
}
break;
case AML_ATVDEMOD_SCAN_MODE:
priv->scanning = true;
if (priv->afc.disable)
priv->afc.disable(&priv->afc);
if (priv->monitor.disable)
priv->monitor.disable(&priv->monitor);
aml_fe_hook_call_set_mode(true);
break;
case AML_ATVDEMOD_UNSCAN_MODE:
priv->scanning = false;
/* No need to enable when exiting the scan,
* but enable when actually played.
*/
#if 0
if (priv->afc.enable)
priv->afc.enable(&priv->afc);
if (priv->monitor.enable)
priv->monitor.enable(&priv->monitor);
#endif
aml_fe_hook_call_set_mode(false);
break;
}
mutex_unlock(&atv_demod_list_mutex);
return ret;
}
static struct analog_demod_ops atvdemod_ops = {
.info = {
.name = DEVICE_NAME,
},
.set_params = atv_demod_set_params,
.has_signal = atv_demod_has_signal,
.standby = atv_demod_standby,
.tuner_status = atv_demod_tuner_status,
.get_afc = atv_demod_get_afc,
.release = atv_demod_release,
.set_config = atv_demod_set_config,
.i2c_gate_ctrl = NULL,
};
unsigned int tuner_status_cnt = 4; /* 4-->16 test on sky mxl661 */
/* 0: no check, 1: check */
bool check_rssi = true;
/* Less than -85, it means no signal */
int tuner_rssi = -80;
/* when need to support secam-l, will enable it */
bool support_secam_l;
bool slow_mode;
static v4l2_std_id atvdemod_fmt_2_v4l2_std(int fmt)
{
v4l2_std_id std = 0;
switch (fmt) {
case AML_ATV_DEMOD_VIDEO_MODE_PROP_NTSC_DK:
case AML_ATV_DEMOD_VIDEO_MODE_PROP_PAL_DK:
std = V4L2_STD_PAL_DK;
break;
case AML_ATV_DEMOD_VIDEO_MODE_PROP_NTSC_I:
case AML_ATV_DEMOD_VIDEO_MODE_PROP_PAL_I:
std = V4L2_STD_PAL_I;
break;
case AML_ATV_DEMOD_VIDEO_MODE_PROP_NTSC_BG:
case AML_ATV_DEMOD_VIDEO_MODE_PROP_PAL_BG:
std = V4L2_STD_PAL_BG;
break;
case AML_ATV_DEMOD_VIDEO_MODE_PROP_PAL_M:
std = V4L2_STD_PAL_M;
break;
case AML_ATV_DEMOD_VIDEO_MODE_PROP_NTSC:
case AML_ATV_DEMOD_VIDEO_MODE_PROP_NTSC_M:
std = V4L2_STD_NTSC_M;
break;
case AML_ATV_DEMOD_VIDEO_MODE_PROP_SECAM_L:
std = V4L2_STD_SECAM_L;
break;
case AML_ATV_DEMOD_VIDEO_MODE_PROP_SECAM_LC:
std = V4L2_STD_SECAM_LC;
break;
case AML_ATV_DEMOD_VIDEO_MODE_PROP_SECAM_DK2:
case AML_ATV_DEMOD_VIDEO_MODE_PROP_SECAM_DK3:
std = V4L2_STD_SECAM_DK;
break;
default:
pr_err("%s: Unsupport fmt: 0x%0x.\n", __func__, fmt);
}
return std;
}
static v4l2_std_id atvdemod_fe_tvin_fmt_to_v4l2_std(int fmt)
{
v4l2_std_id std = 0;
switch (fmt) {
case TVIN_SIG_FMT_CVBS_NTSC_M:
std = V4L2_COLOR_STD_NTSC | V4L2_STD_NTSC_M;
break;
case TVIN_SIG_FMT_CVBS_NTSC_443:
std = V4L2_COLOR_STD_NTSC | V4L2_STD_NTSC_443;
break;
case TVIN_SIG_FMT_CVBS_NTSC_50:
std = V4L2_COLOR_STD_NTSC | V4L2_STD_NTSC_M;
break;
case TVIN_SIG_FMT_CVBS_PAL_I:
std = V4L2_COLOR_STD_PAL | V4L2_STD_PAL_I;
break;
case TVIN_SIG_FMT_CVBS_PAL_M:
std = V4L2_COLOR_STD_PAL | V4L2_STD_PAL_M;
break;
case TVIN_SIG_FMT_CVBS_PAL_60:
std = V4L2_COLOR_STD_PAL | V4L2_STD_PAL_60;
break;
case TVIN_SIG_FMT_CVBS_PAL_CN:
std = V4L2_COLOR_STD_PAL | V4L2_STD_PAL_Nc;
break;
case TVIN_SIG_FMT_CVBS_SECAM:
std = V4L2_COLOR_STD_SECAM | V4L2_STD_SECAM_L;
break;
default:
pr_err("%s: Unsupport fmt: 0x%x\n", __func__, fmt);
break;
}
return std;
}
static void atvdemod_fe_try_analog_format(struct v4l2_frontend *v4l2_fe,
int auto_search_std, v4l2_std_id *video_fmt,
unsigned int *audio_fmt, unsigned int *soundsys)
{
struct dvb_frontend *fe = &v4l2_fe->fe;
struct v4l2_analog_parameters *p = &v4l2_fe->params;
struct analog_parameters params;
struct atv_demod_priv *priv = fe->analog_demod_priv;
unsigned int tuner_id = priv->atvdemod_param.tuner_id;
int i = 0;
int try_vfmt_cnt = 300;
int varify_cnt = 0;
int cvbs_std = 0;
v4l2_std_id std_bk = 0;
unsigned int broad_std = 0;
unsigned int audio = 0;
*video_fmt = 0;
*audio_fmt = 0;
*soundsys = 0;
if (auto_search_std & AUTO_DETECT_COLOR) {
for (i = 0; i < try_vfmt_cnt; i++) {
if (atvdemod_check_exited(v4l2_fe))
return;
/* SECAM-L/L' */
if ((p->std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))
&& (p->std & V4L2_COLOR_STD_SECAM)) {
cvbs_std = TVIN_SIG_FMT_CVBS_SECAM;
break;
}
if (aml_fe_hook_call_get_fmt(&cvbs_std) == false) {
pr_err("%s: aml_fe_hook_get_fmt == NULL.\n",
__func__);
break;
}
if (cvbs_std) {
varify_cnt++;
pr_dbg("get cvbs_std varify_cnt:%d, cnt:%d, cvbs_std:0x%x\n",
varify_cnt, i,
(unsigned int) cvbs_std);
if (((tuner_id == AM_TUNER_R840
|| tuner_id == AM_TUNER_R842)
&& varify_cnt > 0)
|| varify_cnt > 3)
break;
}
if (i == (try_vfmt_cnt / 3) ||
(i == (try_vfmt_cnt / 3) * 2)) {
/* Before enter search,
* need set the std,
* then, try others std.
*/
if (p->std & V4L2_COLOR_STD_PAL) {
p->std = V4L2_COLOR_STD_NTSC
| V4L2_STD_NTSC_M;
p->audmode = V4L2_STD_NTSC_M;
#if 0 /*for secam */
} else if (p->std & V4L2_COLOR_STD_NTSC) {
p->std = V4L2_COLOR_STD_SECAM
| V4L2_STD_SECAM;
p->audmode = V4L2_STD_SECAM;
#endif
} else if (p->std & V4L2_COLOR_STD_NTSC) {
p->std = V4L2_COLOR_STD_PAL
| V4L2_STD_PAL_DK;
p->audmode = V4L2_STD_PAL_DK;
}
p->frequency += 1;
params.frequency = p->frequency;
params.mode = p->afc_range;
params.audmode = p->audmode;
params.std = p->std;
fe->ops.analog_ops.set_params(fe, &params);
}
usleep_range(30 * 1000, 30 * 1000 + 100);
}
pr_dbg("get cvbs_std cnt:%d, cvbs_std: 0x%x\n",
i, (unsigned int) cvbs_std);
if (cvbs_std == 0) {
pr_err("%s: failed to get video fmt, assume PAL.\n",
__func__);
cvbs_std = TVIN_SIG_FMT_CVBS_PAL_I;
p->std = V4L2_COLOR_STD_PAL | V4L2_STD_PAL_DK;
p->frequency += 1;
p->audmode = V4L2_STD_PAL_DK;
params.frequency = p->frequency;
params.mode = p->afc_range;
params.audmode = p->audmode;
params.std = p->std;
fe->ops.analog_ops.set_params(fe, &params);
usleep_range(20 * 1000, 20 * 1000 + 100);
}
std_bk = atvdemod_fe_tvin_fmt_to_v4l2_std(cvbs_std);
} else {
/* Only search std by user setting,
* so no need tvafe identify signal.
*/
std_bk = p->std;
}
*video_fmt = std_bk;
if (!(auto_search_std & AUTO_DETECT_AUDIO)) {
*audio_fmt = p->audmode;
return;
}
if (std_bk & V4L2_COLOR_STD_NTSC) {
#if 1 /* For TV Signal Generator(TG39) test, NTSC need support other audio.*/
if (cvbs_std == TVIN_SIG_FMT_CVBS_NTSC_M) {
broad_std = AML_ATV_DEMOD_VIDEO_MODE_PROP_PAL_M;
audio = V4L2_STD_NTSC_M;
} else {
amlatvdemod_set_std(AML_ATV_DEMOD_VIDEO_MODE_PROP_NTSC);
broad_std = aml_audiomode_autodet(v4l2_fe);
audio = atvdemod_fmt_2_v4l2_std(broad_std);
}
#if 0 /* I don't know what's going on here */
if (audio == V4L2_STD_PAL_M)
audio = V4L2_STD_NTSC_M;
else
std_bk = V4L2_COLOR_STD_PAL;
#endif
#else /* Now, force to NTSC_M, Ours demod only support M for NTSC.*/
audio = V4L2_STD_NTSC_M;
#endif
} else if (std_bk & V4L2_COLOR_STD_SECAM) {
#if 1 /* For support SECAM-DK/BG/I/L */
amlatvdemod_set_std(AML_ATV_DEMOD_VIDEO_MODE_PROP_SECAM_L);
broad_std = aml_audiomode_autodet(v4l2_fe);
audio = atvdemod_fmt_2_v4l2_std(broad_std);
#else /* For force L */
audio = V4L2_STD_SECAM_L;
#endif
} else {
/* V4L2_COLOR_STD_PAL */
if (cvbs_std == TVIN_SIG_FMT_CVBS_PAL_M ||
cvbs_std == TVIN_SIG_FMT_CVBS_PAL_CN) {
broad_std = AML_ATV_DEMOD_VIDEO_MODE_PROP_PAL_M;
audio = V4L2_STD_PAL_M;
} else {
amlatvdemod_set_std(
AML_ATV_DEMOD_VIDEO_MODE_PROP_PAL_DK);
broad_std = aml_audiomode_autodet(v4l2_fe);
audio = atvdemod_fmt_2_v4l2_std(broad_std);
}
#if 0 /* Why do this to me? We need support PAL_M.*/
if (audio == V4L2_STD_PAL_M) {
audio = atvdemod_fmt_2_v4l2_std(broad_std_except_pal_m);
pr_info("select audmode 0x%x\n", audio);
}
#endif
}
*audio_fmt = audio;
#if 0 /* no detect when searching */
/* for audio standard detection */
if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXLX)) {
*soundsys = amlfmt_aud_standard(broad_std);
*soundsys = (*soundsys << 16) | 0x00FFFF;
} else
#endif
*soundsys = 0xFFFFFF;
pr_info("auto detect audio broad_std %d, [%s][0x%x] soundsys[0x%x]\n",
broad_std, v4l2_std_to_str(audio), audio, *soundsys);
}
static void atvdemod_fe_try_signal(struct v4l2_frontend *v4l2_fe,
int auto_search, bool *lock)
{
struct analog_parameters params;
struct dvb_frontend *fe = &v4l2_fe->fe;
struct atv_demod_priv *priv = fe->analog_demod_priv;
struct v4l2_analog_parameters *p = &v4l2_fe->params;
enum v4l2_status tuner_state = V4L2_TIMEDOUT;
enum v4l2_status ade_state = V4L2_TIMEDOUT;
int try_cnt = tuner_status_cnt;
v4l2_std_id std_bk = 0;
unsigned int audio = 0;
bool try_secaml = false;
bool try_secamlc = false;
unsigned int tuner_id = priv->atvdemod_param.tuner_id;
s16 strength = 0;
if (fe->ops.analog_ops.set_params) {
params.frequency = p->frequency;
params.mode = p->afc_range;
params.audmode = p->audmode;
params.std = p->std;
fe->ops.analog_ops.set_params(fe, &params);
}
/* backup the std and audio mode */
std_bk = p->std;
audio = p->audmode;
*lock = false;
do {
if (atvdemod_check_exited(v4l2_fe))
break;
if (tuner_id == AM_TUNER_MXL661) {
usleep_range(30 * 1000, 30 * 1000 + 100);
} else if (tuner_id == AM_TUNER_R840 ||
tuner_id == AM_TUNER_R842) {
usleep_range(40 * 1000, 40 * 1000 + 100);
fe->ops.tuner_ops.get_status(fe, (u32 *)&tuner_state);
} else if (tuner_id == AM_TUNER_ATBM2040 ||
tuner_id == AM_TUNER_ATBM253) {
usleep_range(50 * 1000, 50 * 1000 + 100);
} else {
/* AM_TUNER_SI2151 and AM_TUNER_SI2159 */
usleep_range(10 * 1000, 10 * 1000 + 100);
}
/* Add tuner rssi strength check */
if (tuner_id == AM_TUNER_ATBM2040 &&
fe->ops.tuner_ops.get_strength && check_rssi) {
fe->ops.tuner_ops.get_strength(fe, &strength);
if (strength < tuner_rssi) {
pr_err("[%s] freq: %d tuner RSSI [%d] less than [%d].\n",
__func__, p->frequency,
strength, tuner_rssi);
break;
}
}
fe->ops.analog_ops.has_signal(fe, (u16 *)&ade_state);
try_cnt--;
if (((ade_state == V4L2_HAS_LOCK ||
tuner_state == V4L2_HAS_LOCK) &&
(tuner_id != AM_TUNER_R840 &&
tuner_id != AM_TUNER_R842)) ||
((ade_state == V4L2_HAS_LOCK &&
tuner_state == V4L2_HAS_LOCK) &&
(tuner_id == AM_TUNER_R840 ||
tuner_id == AM_TUNER_R842))) {
*lock = true;
break;
}
if (try_cnt == 0) {
if (support_secam_l && auto_search) {
if (!(p->std & V4L2_STD_SECAM_L) &&
!try_secaml) {
p->std = (V4L2_COLOR_STD_SECAM
| V4L2_STD_SECAM_L);
p->audmode = V4L2_STD_SECAM_L;
try_secaml = true;
} else if (!(p->std & V4L2_STD_SECAM_LC) &&
!try_secamlc &&
p->frequency <= ATV_SECAM_LC_100MHZ) {
p->std = (V4L2_COLOR_STD_SECAM
| V4L2_STD_SECAM_LC);
p->audmode = V4L2_STD_SECAM_LC;
try_secamlc = true;
} else {
break;
}
params.frequency = p->frequency;
params.mode = p->afc_range;
params.audmode = p->audmode;
params.std = p->std;
fe->ops.analog_ops.set_params(fe, &params);
if (tuner_status_cnt > 2)
try_cnt = tuner_status_cnt / 2;
else
try_cnt = tuner_status_cnt;
continue;
}
break;
}
} while (1);
if (*lock == false && (try_secaml || try_secamlc)) {
p->std = std_bk;
p->audmode = audio;
params.frequency = p->frequency;
params.mode = p->afc_range;
params.audmode = p->audmode;
params.std = p->std;
fe->ops.analog_ops.set_params(fe, &params);
}
}
static int atvdemod_fe_afc_closer(struct v4l2_frontend *v4l2_fe, int minafcfreq,
int maxafcfreq, int isAutoSearch)
{
struct dvb_frontend *fe = &v4l2_fe->fe;
struct v4l2_analog_parameters *p = &v4l2_fe->params;
struct analog_parameters params;
struct atv_demod_priv *priv = fe->analog_demod_priv;
int afc = 100;
__u32 set_freq = 0;
int count = 25;
int lock_cnt = 0;
static int freq_success;
static int temp_freq, temp_afc;
struct timespec time_now;
static struct timespec success_time;
unsigned int tuner_id = priv->atvdemod_param.tuner_id;
pr_dbg("[%s] freq_success: %d, freq: %d, minfreq: %d, maxfreq: %d\n",
__func__, freq_success, p->frequency, minafcfreq, maxafcfreq);
/* avoid more search the same program, except < 45.00Mhz */
if (abs(p->frequency - freq_success) < 3000000
&& p->frequency > 45000000) {
ktime_get_ts(&time_now);
pr_err("[%s] tv_sec now:%ld,tv_sec success:%ld\n",
__func__, time_now.tv_sec, success_time.tv_sec);
/* beyond 10s search same frequency is ok */
if ((time_now.tv_sec - success_time.tv_sec) < 10)
return -1;
}
/*do the auto afc make sure the afc < 50k or the range from api */
if ((fe->ops.analog_ops.get_afc || fe->ops.tuner_ops.get_afc) &&
fe->ops.tuner_ops.set_analog_params) {
set_freq = p->frequency;
while (abs(afc) > AFC_BEST_LOCK) {
if (atvdemod_check_exited(v4l2_fe))
return -1;
if (tuner_id == AM_TUNER_MXL661)
usleep_range(30 * 1000, 30 * 1000 + 100);
else if (tuner_id == AM_TUNER_R840 ||
tuner_id == AM_TUNER_R842)
usleep_range(40 * 1000, 40 * 1000 + 100);
else if (tuner_id == AM_TUNER_ATBM2040 ||
tuner_id == AM_TUNER_ATBM253)
usleep_range(50 * 1000, 50 * 1000 + 100);
else
usleep_range(10 * 1000, 10 * 1000 + 100);
if (fe->ops.analog_ops.get_afc)
fe->ops.analog_ops.get_afc(fe, &afc);
else if (fe->ops.tuner_ops.get_afc)
fe->ops.tuner_ops.get_afc(fe, &afc);
pr_dbg("[%s] get afc %d khz, freq %u.\n",
__func__, afc, p->frequency);
if (afc == 0xffff) {
/*last lock, but this unlock,so try get afc*/
if (lock_cnt > 0) {
p->frequency = temp_freq +
temp_afc * 1000;
pr_err("[%s] force lock, f:%d\n",
__func__, p->frequency);
break;
}
afc = 500;
} else {
lock_cnt++;
temp_freq = p->frequency;
if (afc > 50)
temp_afc = 500;
else if (afc < -50)
temp_afc = -500;
else
temp_afc = afc;
}
if (((abs(afc) > (500 - AFC_BEST_LOCK))
&& (abs(afc) < (500 + AFC_BEST_LOCK))
&& (abs(afc) != 500))
|| ((abs(afc) == 500) && (lock_cnt > 0))) {
p->frequency += afc * 1000;
break;
}
if (afc >= (500 + AFC_BEST_LOCK))
afc = 500;
p->frequency += afc * 1000;
if (unlikely(p->frequency > maxafcfreq)) {
pr_err("[%s] [%d] is exceed maxafcfreq[%d]\n",
__func__, p->frequency, maxafcfreq);
p->frequency = set_freq;
return -1;
}
#if 0 /*if enable, it would miss program*/
if (unlikely(c->frequency < minafcfreq)) {
pr_dbg("[%s] [%d] is exceed minafcfreq[%d]\n",
__func__,
c->frequency, minafcfreq);
c->frequency = set_freq;
return -1;
}
#endif
if (likely(!(count--))) {
pr_err("[%s] exceed the afc count\n", __func__);
p->frequency = set_freq;
return -1;
}
/* delete it will miss program
* when c->frequency equal program frequency
*/
p->frequency++;
if (fe->ops.tuner_ops.set_analog_params) {
params.frequency = p->frequency;
params.mode = p->afc_range;
params.audmode = p->audmode;
params.std = p->std;
fe->ops.tuner_ops.set_analog_params(fe,
&params);
}
}
/* After correcting the frequency offset success,
* need to set up tuner.
*/
if (fe->ops.tuner_ops.set_analog_params) {
params.frequency = p->frequency;
params.mode = p->afc_range;
params.audmode = p->audmode;
params.std = p->std;
fe->ops.tuner_ops.set_analog_params(fe,
&params);
if (tuner_id == AM_TUNER_MXL661)
usleep_range(30 * 1000, 30 * 1000 + 100);
else if (tuner_id == AM_TUNER_R840 ||
tuner_id == AM_TUNER_R842)
usleep_range(40 * 1000, 40 * 1000 + 100);
else if (tuner_id == AM_TUNER_ATBM2040 ||
tuner_id == AM_TUNER_ATBM253)
usleep_range(50 * 1000, 50 * 1000 + 100);
else
usleep_range(10 * 1000, 10 * 1000 + 100);
}
freq_success = p->frequency;
ktime_get_ts(&success_time);
pr_dbg("[%s] get afc %d khz done, freq %u.\n",
__func__, afc, p->frequency);
}
return 0;
}
static int atvdemod_fe_set_property(struct v4l2_frontend *v4l2_fe,
struct v4l2_property *tvp)
{
struct dvb_frontend *fe = &v4l2_fe->fe;
struct atv_demod_priv *priv = fe->analog_demod_priv;
struct v4l2_analog_parameters *params = &v4l2_fe->params;
pr_dbg("%s: cmd = 0x%x.\n", __func__, tvp->cmd);
switch (tvp->cmd) {
case V4L2_SOUND_SYS:
/* aud_mode = tvp->data & 0xFF; */
amlatvdemod_devp->sound_mode = tvp->data & 0xFF;
if (amlatvdemod_devp->sound_mode != 0xFF) {
aud_mode = amlatvdemod_devp->sound_mode;
params->soundsys = params->soundsys | aud_mode;
}
priv->atvdemod_sound.output_mode = tvp->data & 0xFF;
break;
case V4L2_SLOW_SEARCH_MODE:
slow_mode = tvp->data;
break;
case V4L2_SIF_OVER_MODULATION:
priv->atvdemod_sound.sif_over_modulation = tvp->data;
break;
default:
pr_dbg("%s: property %d doesn't exist\n",
__func__, tvp->cmd);
return -EINVAL;
}
return 0;
}
static int atvdemod_fe_get_property(struct v4l2_frontend *v4l2_fe,
struct v4l2_property *tvp)
{
pr_dbg("%s: cmd = 0x%x.\n", __func__, tvp->cmd);
switch (tvp->cmd) {
case V4L2_SOUND_SYS:
tvp->data = ((aud_std & 0xFF) << 16)
| ((signal_audmode & 0xFF) << 8)
| (aud_mode & 0xFF);
break;
case V4L2_SLOW_SEARCH_MODE:
tvp->data = slow_mode;
break;
default:
pr_dbg("%s: property %d doesn't exist\n",
__func__, tvp->cmd);
return -EINVAL;
}
return 0;
}
static int atvdemod_fe_tune(struct v4l2_frontend *v4l2_fe,
struct v4l2_tune_status *status)
{
bool lock = false;
int priv_cfg = 0;
int try_cnt = 4;
enum v4l2_status state = V4L2_TIMEDOUT;
struct v4l2_analog_parameters *p = &v4l2_fe->params;
struct dvb_frontend *fe = &v4l2_fe->fe;
/* for tune */
if (p->flag & ANALOG_FLAG_ENABLE_AFC) {
priv_cfg = AML_ATVDEMOD_SCAN_MODE;
if (fe->ops.analog_ops.set_config)
fe->ops.analog_ops.set_config(fe, &priv_cfg);
atvdemod_fe_try_signal(v4l2_fe, 0, &lock);
} else { /* for play */
if (fe->ops.analog_ops.has_signal)
fe->ops.analog_ops.has_signal(fe, (u16 *) &state);
if (state == V4L2_HAS_LOCK)
lock = true;
else
lock = false;
}
if (lock) {
status->lock = 1;
while (try_cnt--) {
status->afc = retrieve_vpll_carrier_afc();
if (status->afc < 1500)
break;
usleep_range(5 * 1000, 5 * 1000 + 100);
}
} else {
status->lock = 0;
status->afc = 0;
}
pr_info("[%s] lock: [%d], afc: [%d], freq: [%d], flag: [%d].\n",
__func__, status->lock, status->afc,
p->frequency, p->flag);
if (p->flag & ANALOG_FLAG_ENABLE_AFC) {
priv_cfg = AML_ATVDEMOD_UNSCAN_MODE;
if (fe->ops.analog_ops.set_config)
fe->ops.analog_ops.set_config(fe, &priv_cfg);
}
return 0;
}
static int atvdemod_fe_detect(struct v4l2_frontend *v4l2_fe)
{
struct v4l2_analog_parameters *p = &v4l2_fe->params;
struct dvb_frontend *fe = &v4l2_fe->fe;
int priv_cfg = 0;
v4l2_std_id std_bk = 0;
unsigned int audio = 0;
unsigned int soundsys = 0;
int auto_detect = AUTO_DETECT_COLOR | AUTO_DETECT_AUDIO;
priv_cfg = AML_ATVDEMOD_SCAN_MODE;
if (fe->ops.analog_ops.set_config)
fe->ops.analog_ops.set_config(fe, &priv_cfg);
atvdemod_fe_try_analog_format(v4l2_fe, auto_detect,
&std_bk, &audio, &soundsys);
if (std_bk != 0) {
p->audmode = audio;
p->std = std_bk;
p->soundsys = soundsys;
std_bk = 0;
audio = 0;
}
priv_cfg = AML_ATVDEMOD_UNSCAN_MODE;
if (fe->ops.analog_ops.set_config)
fe->ops.analog_ops.set_config(fe, &priv_cfg);
return 0;
}
static enum v4l2_search atvdemod_fe_search(struct v4l2_frontend *v4l2_fe)
{
/* struct analog_parameters params; */
struct dvb_frontend *fe = &v4l2_fe->fe;
struct atv_demod_priv *priv = NULL;
struct v4l2_analog_parameters *p = &v4l2_fe->params;
/*enum v4l2_status tuner_state = V4L2_TIMEDOUT;*/
/*enum v4l2_status ade_state = V4L2_TIMEDOUT;*/
bool pll_lock = false;
/*struct atv_status_s atv_status;*/
__u32 set_freq = 0;
__u32 minafcfreq = 0, maxafcfreq = 0;
__u32 afc_step = 0;
/* int tuner_status_cnt_local = tuner_status_cnt; */
v4l2_std_id std_bk = 0;
unsigned int audio = 0;
unsigned int soundsys = 0;
/* int double_check_cnt = 1; */
int auto_search_std = 0;
int search_count = 0;
/* bool try_secam = false; */
int ret = -1;
unsigned int tuner_id = 0;
int priv_cfg = 0;
int exit_status = 0;
char *exit_str = "";
if (unlikely(!fe || !p ||
!fe->ops.tuner_ops.get_status ||
!fe->ops.analog_ops.has_signal ||
!fe->ops.analog_ops.set_params ||
!fe->ops.analog_ops.set_config ||
(aml_fe_has_hook_up() == false))) {
pr_err("[%s] error: NULL function or pointer.\n", __func__);
return V4L2_SEARCH_INVALID;
}
priv = fe->analog_demod_priv;
if (atvdemod_check_exited(v4l2_fe)) {
pr_err("[%s] ATV state is not work.\n", __func__);
return V4L2_SEARCH_INVALID;
}
if (p->afc_range == 0) {
pr_err("[%s] afc_range == 0, skip the search\n", __func__);
return V4L2_SEARCH_INVALID;
}
tuner_id = priv->atvdemod_param.tuner_id;
pr_info("[%s] afc_range: [%d], tuner: [%d], freq: [%d], flag: [%d].\n",
__func__, p->afc_range, tuner_id,
p->frequency, p->flag);
/* backup the freq by api */
set_freq = p->frequency;
/* Before enter search, need set the std first.
* If set p->analog.std == 0, will search all std (PAL/NTSC/SECAM),
* and need tvafe identify signal type.
*/
if (p->std == 0) {
if (tuner_id == AM_TUNER_ATBM2040)
p->std = V4L2_COLOR_STD_PAL | V4L2_STD_PAL_DK;
else
p->std = V4L2_COLOR_STD_NTSC | V4L2_STD_NTSC_M;
auto_search_std = AUTO_DETECT_COLOR;
pr_dbg("[%s] user std is 0, so set it to %s.\n",
__func__, v4l2_std_to_str(p->std & 0xFF000000));
}
if (p->audmode == 0) {
if (auto_search_std) {
p->audmode = p->std & 0x00FFFFFF;
} else {
if (p->std & V4L2_COLOR_STD_PAL)
p->audmode = V4L2_STD_PAL_DK;
else if (p->std & V4L2_COLOR_STD_NTSC)
p->audmode = V4L2_STD_NTSC_M;
else if (p->std & V4L2_COLOR_STD_SECAM)
p->audmode = V4L2_STD_PAL_DK;
p->std = (p->std & 0xFF000000) | p->audmode;
}
auto_search_std |= AUTO_DETECT_AUDIO;
pr_dbg("[%s] user audmode is 0, so set it to %s.\n",
__func__, v4l2_std_to_str(p->audmode));
}
priv_cfg = AML_ATVDEMOD_SCAN_MODE;
fe->ops.analog_ops.set_config(fe, &priv_cfg);
/*set the afc_range and start freq*/
minafcfreq = p->frequency - p->afc_range;
maxafcfreq = p->frequency + p->afc_range;
/*from the current freq start, and set the afc_step*/
/*if step is 2Mhz,r840 will miss program*/
if (slow_mode || p->afc_range == ATV_AFC_1_0MHZ) {
pr_dbg("[%s] slow mode to search the channel\n", __func__);
afc_step = ATV_AFC_1_0MHZ;
} else if (!slow_mode) {
if ((tuner_id == AM_TUNER_R840 || tuner_id == AM_TUNER_R842) &&
p->afc_range >= ATV_AFC_2_0MHZ) {
pr_dbg("[%s] r842/r840 use slow mode to search the channel\n",
__func__);
afc_step = ATV_AFC_1_0MHZ;
} else {
afc_step = p->afc_range;
}
} else {
pr_dbg("[%s] default use slow mode to search the channel\n",
__func__);
afc_step = ATV_AFC_1_0MHZ;
}
/**enter auto search mode**/
pr_dbg("[%s] Auto search std: 0x%08x, audmode: 0x%08x\n",
__func__, (unsigned int) p->std, p->audmode);
while (minafcfreq <= p->frequency &&
p->frequency <= maxafcfreq) {
if (atvdemod_check_exited(v4l2_fe)) {
exit_status = 1;
break;
}
pr_dbg("[%s] [%d] is processing, [min=%d, max=%d].\n",
__func__, p->frequency, minafcfreq, maxafcfreq);
pll_lock = false;
atvdemod_fe_try_signal(v4l2_fe, auto_search_std, &pll_lock);
std_bk = 0;
audio = 0;
if (pll_lock) {
pr_dbg("[%s] freq: [%d] pll lock success\n",
__func__, p->frequency);
ret = atvdemod_fe_afc_closer(v4l2_fe, minafcfreq,
maxafcfreq + ATV_AFC_500KHZ, 1);
if (ret == 0) {
atvdemod_fe_try_analog_format(v4l2_fe,
auto_search_std,
&std_bk, &audio, &soundsys);
pr_info("[%s] freq:%d, std_bk:0x%x, audmode:0x%x, search OK.\n",
__func__, p->frequency,
(unsigned int) std_bk, audio);
if (std_bk != 0) {
p->audmode = audio;
p->std = std_bk;
/*avoid std unenable */
p->frequency -= 1;
p->soundsys = soundsys;
std_bk = 0;
audio = 0;
} else {
exit_status = 1;
break;
}
/* sync param */
/* aml_fe_analog_sync_frontend(fe); */
priv_cfg = AML_ATVDEMOD_UNSCAN_MODE;
fe->ops.analog_ops.set_config(fe, &priv_cfg);
return V4L2_SEARCH_SUCCESS;
}
}
pr_dbg("[%s] freq[analog.std:0x%08x] is[%d] unlock\n",
__func__,
(uint32_t) p->std, p->frequency);
/* when manual search, just search current freq */
if (p->flag == ANALOG_FLAG_MANUL_SCAN) {
exit_status = 2;
break;
}
#ifdef DOUBLE_CHECK_44_25MHZ
if (p->frequency >= 44200000 &&
p->frequency <= 44300000 &&
double_check_cnt) {
double_check_cnt--;
pr_err("%s 44.25Mhz double check\n", __func__);
} else {
++search_count;
p->frequency += afc_step * ((search_count % 2) ?
-search_count : search_count);
}
#else
++search_count;
p->frequency += afc_step * ((search_count % 2) ?
-search_count : search_count);
#endif
}
if (!exit_status)
exit_str = "over of range, search failed";
else if (exit_status == 1)
exit_str = "search exited";
else
exit_str = "search failed";
pr_dbg("[%s] [%d] %s.\n", __func__, p->frequency, exit_str);
p->frequency = set_freq;
priv_cfg = AML_ATVDEMOD_UNSCAN_MODE;
fe->ops.analog_ops.set_config(fe, &priv_cfg);
return V4L2_SEARCH_FAILED;
}
static struct v4l2_frontend_ops atvdemod_fe_ops = {
.set_property = atvdemod_fe_set_property,
.get_property = atvdemod_fe_get_property,
.tune = atvdemod_fe_tune,
.detect = atvdemod_fe_detect,
.search = atvdemod_fe_search,
};
struct dvb_frontend *aml_atvdemod_attach(struct dvb_frontend *fe,
struct v4l2_frontend *v4l2_fe,
struct i2c_adapter *i2c_adap, u8 i2c_addr, u32 tuner_id)
{
int instance = 0;
struct atv_demod_priv *priv = NULL;
mutex_lock(&atv_demod_list_mutex);
instance = hybrid_tuner_request_state(struct atv_demod_priv, priv,
hybrid_tuner_instance_list,
i2c_adap, i2c_addr, DEVICE_NAME);
priv->atvdemod_param.tuner_id = tuner_id;
switch (instance) {
case 0:
mutex_unlock(&atv_demod_list_mutex);
return NULL;
case 1:
fe->analog_demod_priv = priv;
priv->afc.fe = fe;
atv_demod_afc_init(&priv->afc);
priv->monitor.fe = fe;
atv_demod_monitor_init(&priv->monitor);
if (amlatvdemod_devp->irq > 0) {
priv->isr.irq = amlatvdemod_devp->irq;
atv_demod_isr_init(&priv->isr);
}
priv->standby = true;
pr_info("%s: aml_atvdemod found.\n", __func__);
break;
default:
fe->analog_demod_priv = priv;
break;
}
mutex_unlock(&atv_demod_list_mutex);
fe->ops.info.type = FE_ANALOG;
memcpy(&fe->ops.analog_ops, &atvdemod_ops,
sizeof(struct analog_demod_ops));
memcpy(&v4l2_fe->ops, &atvdemod_fe_ops,
sizeof(struct v4l2_frontend_ops));
return fe;
}
EXPORT_SYMBOL_GPL(aml_atvdemod_attach);