diff --git a/drivers/amlogic/atv_demod/atv_demod_debug.h b/drivers/amlogic/atv_demod/atv_demod_debug.h index 936c87e570b8..d1c6faad7d39 100644 --- a/drivers/amlogic/atv_demod/atv_demod_debug.h +++ b/drivers/amlogic/atv_demod/atv_demod_debug.h @@ -29,7 +29,7 @@ extern int atvdemod_debug_en; #undef pr_dbg #define pr_dbg(fmt, ...) \ do {\ - if (atvdemod_debug_en)\ + if (atvdemod_debug_en & 01)\ printk(fmt, ##__VA_ARGS__);\ } while (0) @@ -40,4 +40,24 @@ extern int atvdemod_debug_en; printk(fmt, ##__VA_ARGS__);\ } while (0) +#undef pr_afc +#define pr_afc(fmt, ...) \ + do {\ + if (atvdemod_debug_en & 0x02)\ + printk(fmt, ##__VA_ARGS__);\ + } while (0) + +#undef pr_warn +#define pr_warn(fmt, ...) \ + do {\ + if (1)\ + printk(fmt, ##__VA_ARGS__);\ + } while (0) + +#undef pr_audio +#define pr_audio(fmt, ...) \ + do {\ + if (atvdemod_debug_en & 0x04)\ + printk(fmt, ##__VA_ARGS__);\ + } while (0) #endif /* __ATV_DEMOD_DEBUG_H__ */ diff --git a/drivers/amlogic/atv_demod/atv_demod_driver.c b/drivers/amlogic/atv_demod/atv_demod_driver.c index 39ea8299f646..48468537ebcf 100644 --- a/drivers/amlogic/atv_demod/atv_demod_driver.c +++ b/drivers/amlogic/atv_demod/atv_demod_driver.c @@ -77,7 +77,7 @@ static ssize_t aml_atvdemod_store(struct class *class, if (ret) pr_info("atv init error.\n"); } else if (!strncmp(parm[0], "audout_mode", 11)) { - if (get_atvdemod_state() == ATVDEMOD_STATE_WORK) { + if (atv_demod_get_state() == ATVDEMOD_STATE_WORK) { if (is_meson_txlx_cpu()) { atvauddemod_set_outputmode(); pr_info("atvauddemod_set_outputmode done ....\n"); @@ -88,7 +88,7 @@ static ssize_t aml_atvdemod_store(struct class *class, } else if (!strncmp(parm[0], "signal_audmode", 14)) { int stereo_flag, sap_flag; - if (get_atvdemod_state() == ATVDEMOD_STATE_WORK) { + if (atv_demod_get_state() == ATVDEMOD_STATE_WORK) { if (is_meson_txlx_cpu()) { update_btsc_mode(1, &stereo_flag, &sap_flag); pr_info("get signal_audmode done ....\n"); @@ -427,6 +427,18 @@ int aml_attach_demod_tuner(struct aml_atvdemod_device *dev) return 0; } +static int aml_detach_demod_tuner(struct aml_atvdemod_device *dev) +{ + struct v4l2_frontend *v4l2_fe = &dev->v4l2_fe; + + v4l2_frontend_detach(v4l2_fe); + + dev->analog_attached = false; + dev->tuner_attached = false; + + return 0; +} + static int aml_atvdemod_probe(struct platform_device *pdev) { int ret = 0; @@ -531,6 +543,7 @@ static int aml_atvdemod_remove(struct platform_device *pdev) return -1; v4l2_unresister_frontend(&dev->v4l2_fe); + aml_detach_demod_tuner(dev); amlatvdemod_devp = NULL; diff --git a/drivers/amlogic/atv_demod/atv_demod_ops.c b/drivers/amlogic/atv_demod/atv_demod_ops.c index 78d3447c2a52..75a856ec98b7 100644 --- a/drivers/amlogic/atv_demod/atv_demod_ops.c +++ b/drivers/amlogic/atv_demod/atv_demod_ops.c @@ -28,7 +28,7 @@ #include "atv_demod_ops.h" #include "atv_demod_v4l2.h" -#define DEVICE_NAME "aml_atvdemod_fe" +#define DEVICE_NAME "aml_atvdemod" #define ATVDEMOD_STATE_IDEL 0 #define ATVDEMOD_STATE_WORK 1 @@ -46,6 +46,295 @@ static int btsc_sap_mode = 1; /*0: off 1:monitor 2:auto */ module_param(btsc_sap_mode, int, 0644); MODULE_DESCRIPTION("btsc sap mode\n"); +static int afc_offset; +module_param(afc_offset, int, 0644); +MODULE_PARM_DESC(afc_offset, "\n afc_offset\n"); +static unsigned int afc_limit = 2100;/*+-2.1Mhz*/ +module_param(afc_limit, uint, 0644); +MODULE_PARM_DESC(afc_limit, "\n afc_limit\n"); + +static int no_sig_cnt; +struct timer_list aml_timer; +#define AML_INTERVAL (HZ/100) /* 10ms, #define HZ 100 */ +static unsigned int timer_init_state; +static unsigned int aft_thread_enable; +static unsigned int aft_thread_delaycnt; +static unsigned int aml_timer_en = 1; +module_param(aml_timer_en, uint, 0644); +MODULE_PARM_DESC(aml_timer_en, "\n aml_timer_en\n"); + +static unsigned int timer_delay = 1; +module_param(timer_delay, uint, 0644); +MODULE_PARM_DESC(timer_delay, "\n timer_delay\n"); + +static unsigned int timer_delay2 = 10; +module_param(timer_delay2, uint, 0644); +MODULE_PARM_DESC(timer_delay2, "\n timer_delay2\n"); + +static unsigned int timer_delay3 = 10;/*100ms*/ +module_param(timer_delay3, uint, 0644); +MODULE_PARM_DESC(timer_delay3, "\n timer_delay3\n"); + +static unsigned int afc_wave_cnt = 4; +module_param(afc_wave_cnt, uint, 0644); +MODULE_PARM_DESC(afc_wave_cnt, "\n afc_wave_cnt\n"); + + +#define AFC_LOCK_STATUS_NULL 0 +#define AFC_LOCK_STATUS_PRE_UNLOCK 1 +#define AFC_LOCK_STATUS_PRE_LOCK 2 +#define AFC_LOCK_STATUS_PRE_OVER_RANGE 3 +#define AFC_LOCK_STATUS_POST_PROCESS 4 +#define AFC_LOCK_STATUS_POST_LOCK 5 +#define AFC_LOCK_STATUS_POST_UNLOCK 6 +#define AFC_LOCK_STATUS_POST_OVER_RANGE 7 +#define AFC_LOCK_STATUS_NUM 8 + +#define AFC_PRE_STEP_NUM 9 +static int afc_range[11] = {0, -500, 500, -1000, 1000, + -1500, 1500, -2000, 2000, -2500, 2500}; +static unsigned int afc_pre_step; +static unsigned int afc_pre_lock_cnt; +static unsigned int afc_pre_unlock_cnt; +static unsigned int afc_lock_status = AFC_LOCK_STATUS_NULL; + +static void aml_fe_do_work_pre(int lock) +{ + struct dvb_frontend *fe = &amlatvdemod_devp->v4l2_fe.fe; + struct atv_demod_priv *priv = fe->analog_demod_priv; + struct analog_parameters *param = &priv->atvdemod_param.param; + + afc_pre_unlock_cnt++; + if (!lock) { + afc_pre_lock_cnt = 0; + if (afc_lock_status != AFC_LOCK_STATUS_PRE_UNLOCK && + afc_offset) { + param->frequency -= afc_offset * 1000; + afc_offset = 0; + afc_pre_step = 0; + afc_lock_status = AFC_LOCK_STATUS_PRE_UNLOCK; + } + + if (afc_pre_unlock_cnt <= afc_wave_cnt) {/*40ms*/ + afc_lock_status = AFC_LOCK_STATUS_PRE_UNLOCK; + return; + } + + if (afc_offset == afc_range[afc_pre_step]) { + param->frequency -= afc_range[afc_pre_step] * 1000; + afc_offset -= afc_range[afc_pre_step]; + } + + afc_pre_step++; + if (afc_pre_step < AFC_PRE_STEP_NUM) { + param->frequency += afc_range[afc_pre_step] * 1000; + afc_offset += afc_range[afc_pre_step]; + afc_lock_status = AFC_LOCK_STATUS_PRE_UNLOCK; + } else { + param->frequency -= afc_offset * 1000; + afc_offset = 0; + afc_pre_step = 0; + afc_lock_status = AFC_LOCK_STATUS_PRE_OVER_RANGE; + } + + if (fe->ops.tuner_ops.set_analog_params) + fe->ops.tuner_ops.set_analog_params(fe, param); + + afc_pre_unlock_cnt = 0; + pr_afc("%s,unlock afc_offset:%d KHz, set freq:%d\n", + __func__, afc_offset, param->frequency); + } else { + afc_pre_lock_cnt++; + pr_afc("%s,afc_pre_lock_cnt:%d\n", + __func__, afc_pre_lock_cnt); + if (afc_pre_lock_cnt >= afc_wave_cnt * 2) {/*100ms*/ + afc_pre_lock_cnt = 0; + afc_pre_unlock_cnt = 0; + afc_lock_status = AFC_LOCK_STATUS_PRE_LOCK; + } + } +} + +static void aml_fe_do_work(struct work_struct *work) +{ + struct dvb_frontend *fe = &amlatvdemod_devp->v4l2_fe.fe; + struct atv_demod_priv *priv = fe->analog_demod_priv; + struct analog_parameters *param = &priv->atvdemod_param.param; + int afc = 100; + int lock = 0; + int tmp = 0; + int field_lock = 0; + static int audio_overmodul; + static int wave_cnt; + + if (timer_init_state == 0) + return; + + retrieve_vpll_carrier_lock(&tmp);/* 0 means lock, 1 means unlock */ + lock = !tmp; + + /*pre afc:speed up afc*/ + if ((afc_lock_status != AFC_LOCK_STATUS_POST_PROCESS) && + (afc_lock_status != AFC_LOCK_STATUS_POST_LOCK) && + (afc_lock_status != AFC_LOCK_STATUS_PRE_LOCK)) { + aml_fe_do_work_pre(lock); + return; + } + + afc_pre_step = 0; + + if (lock) { + if (0 == ((audio_overmodul++) % 10)) + aml_audio_overmodulation(1); + } + + retrieve_frequency_offset(&afc); + + if (++wave_cnt <= afc_wave_cnt) {/*40ms*/ + afc_lock_status = AFC_LOCK_STATUS_POST_PROCESS; + pr_afc("%s,wave_cnt:%d is wave,lock:%d,afc:%d ignore\n", + __func__, wave_cnt, lock, afc); + return; + } + + /*retrieve_frequency_offset(&afc);*/ + retrieve_field_lock(&tmp); + field_lock = tmp; + + pr_afc("%s,afc:%d lock:%d field_lock:%d freq:%d\n", + __func__, afc, lock, field_lock, param->frequency); + + if (lock && (abs(afc) < AFC_BEST_LOCK && + abs(afc_offset) <= afc_limit) && field_lock) { + afc_lock_status = AFC_LOCK_STATUS_POST_LOCK; + wave_cnt = 0; + pr_afc("%s,afc lock, set wave_cnt 0\n", __func__); + return; + } + + if (!lock) { + afc_lock_status = AFC_LOCK_STATUS_POST_UNLOCK; + afc_pre_lock_cnt = 0; + param->frequency -= afc_offset * 1000; + if (fe->ops.tuner_ops.set_analog_params) + fe->ops.tuner_ops.set_analog_params(fe, param); + pr_afc("%s,afc:%d , set freq:%d\n", + __func__, afc, param->frequency); + wave_cnt = 0; + afc_offset = 0; + pr_afc("%s, [post lock --> unlock]\n", __func__); + return; + } + + if (abs(afc_offset) > afc_limit) { + no_sig_cnt++; + if (no_sig_cnt == 20) { + param->frequency -= afc_offset * 1000; + pr_afc("%s,afc no_sig trig, set freq:%d\n", + __func__, param->frequency); + if (fe->ops.tuner_ops.set_analog_params) + fe->ops.tuner_ops.set_analog_params(fe, param); + wave_cnt = 0; + afc_offset = 0; + afc_lock_status = AFC_LOCK_STATUS_POST_OVER_RANGE; + } + return; + } + + no_sig_cnt = 0; + if (abs(afc) >= AFC_BEST_LOCK) { + param->frequency += afc * 1000; + afc_offset += afc; + if (fe->ops.tuner_ops.set_analog_params) + fe->ops.tuner_ops.set_analog_params(fe, param); + + pr_afc("%s,afc:%d , set freq:%d\n", + __func__, afc, param->frequency); + } + + wave_cnt = 0; + afc_lock_status = AFC_LOCK_STATUS_POST_PROCESS; +} + +void aml_timer_handler(unsigned long arg) +{ + struct dvb_frontend *fe = (struct dvb_frontend *)arg; + struct atv_demod_priv *priv = fe->analog_demod_priv; + unsigned int delay_ms = 0; + + if ((fe == NULL) || (priv == NULL) || (timer_init_state == 0)) + return; + + if (afc_lock_status == AFC_LOCK_STATUS_POST_OVER_RANGE || + afc_lock_status == AFC_LOCK_STATUS_PRE_OVER_RANGE || + afc_lock_status == AFC_LOCK_STATUS_POST_LOCK) + delay_ms = timer_delay2;/*100ms*/ + else + delay_ms = timer_delay;/*10ms*/ + + aml_timer.function = aml_timer_handler; + aml_timer.data = arg; + aml_timer.expires = jiffies + AML_INTERVAL * delay_ms; + add_timer(&aml_timer); + + if (!aft_thread_enable) { + /*pr_info("%s, stop aft thread\n", __func__);*/ + return; + } + + if (aft_thread_delaycnt > 0) { + aft_thread_delaycnt--; + return; + } + + if ((aml_timer_en == 0) || (fe->ops.info.type != FE_ANALOG)) + return; + + schedule_work(&priv->demod_wq); +} + +static void afc_timer_disable(struct dvb_frontend *fe) +{ + struct atv_demod_priv *priv = fe->analog_demod_priv; + + if ((aml_timer_en == 1) && (timer_init_state == 1)) { + del_timer_sync(&aml_timer); + cancel_work_sync(&priv->demod_wq); + timer_init_state = 0; + } +} + +static void afc_timer_enable(struct dvb_frontend *fe) +{ + if (fe && (aml_timer_en == 1) && (timer_init_state == 0)) { + init_timer(&aml_timer); + aml_timer.function = aml_timer_handler; + aml_timer.data = (ulong) fe; + /* after 5s enable demod auto detect */ + aml_timer.expires = jiffies + AML_INTERVAL * timer_delay3; + afc_offset = 0; + no_sig_cnt = 0; + afc_pre_step = 0; + afc_lock_status = AFC_LOCK_STATUS_NULL; + add_timer(&aml_timer); + timer_init_state = 1; + } +} + +static void set_aft_thread_enable(int enable, unsigned int delay) +{ + if (enable == aft_thread_enable) + return; + + aft_thread_enable = enable; + aft_thread_delaycnt = delay; + + if (aft_thread_enable) + afc_timer_enable(&amlatvdemod_devp->v4l2_fe.fe); + else + afc_timer_disable(&amlatvdemod_devp->v4l2_fe.fe); +} + /* * add interface for audio driver to get atv audio state. * state: @@ -57,22 +346,23 @@ void aml_fe_get_atvaudio_state(int *state) int power = 0; int vpll_lock = 0; int line_lock = 0; + int atv_state = atv_demod_get_state(); - if (atvdemod_state == ATVDEMOD_STATE_WORK) { + if (atv_state == ATVDEMOD_STATE_WORK) { 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); } else - pr_err("%s, atv is not work, atvdemod_state: %d.\n", - __func__, atvdemod_state); + pr_err("%s, atv is not work, atv_state: %d.\n", + __func__, atv_state); if (power >= 150) *state = 1; else *state = 0; - pr_dbg("aml_fe_get_atvaudio_state: %d, power = %d.\n", + pr_audio("aml_fe_get_atvaudio_state: %d, power = %d.\n", *state, power); } @@ -146,29 +436,28 @@ int is_atvdemod_work(void) { int ret = 0; - if (atvdemod_state == ATVDEMOD_STATE_WORK) + if (atv_demod_get_state() == ATVDEMOD_STATE_WORK) ret = 1; return ret; } -int is_atvdemod_scan_mode(void) +static int atv_demod_get_scan_mode(void) { return atvdemod_scan_mode; } -//EXPORT_SYMBOL(is_atvdemod_scan_mode); -void set_atvdemod_scan_mode(int val) +static void atv_demod_set_scan_mode(int val) { atvdemod_scan_mode = val; } -int get_atvdemod_state(void) +int atv_demod_get_state(void) { return atvdemod_state; } -void set_atvdemod_state(int state) +static void atv_demod_set_state(int state) { atvdemod_state = state; } @@ -178,7 +467,7 @@ int atv_demod_enter_mode(void) int err_code = 0; #if 0 - if (atvdemod_state == ATVDEMOD_STATE_WORK) + if (atv_demod_get_state() == ATVDEMOD_STATE_WORK) return 0; #endif if (amlatvdemod_devp->pin_name != NULL) @@ -200,14 +489,15 @@ int atv_demod_enter_mode(void) return err_code; } - /* set_aft_thread_enable(1, 0); */ + set_aft_thread_enable(1, 0); /* * memset(&(amlatvdemod_devp->parm), 0, * sizeof(amlatvdemod_devp->parm)); */ amlatvdemod_devp->std = 0; + amlatvdemod_devp->audmode = 0; - atvdemod_state = ATVDEMOD_STATE_WORK; + atv_demod_set_state(ATVDEMOD_STATE_WORK); pr_info("%s: OK.\n", __func__); @@ -216,10 +506,10 @@ int atv_demod_enter_mode(void) int atv_demod_leave_mode(void) { - if (atvdemod_state == ATVDEMOD_STATE_IDEL) + if (atv_demod_get_state() == ATVDEMOD_STATE_IDEL) return 0; - /* set_aft_thread_enable(0, 0); */ + set_aft_thread_enable(0, 0); atvdemod_uninit(); if (amlatvdemod_devp->pin != NULL) { devm_pinctrl_put(amlatvdemod_devp->pin); @@ -235,7 +525,8 @@ int atv_demod_leave_mode(void) * sizeof(amlatvdemod_devp->parm)); */ amlatvdemod_devp->std = 0; - atvdemod_state = ATVDEMOD_STATE_IDEL; + amlatvdemod_devp->audmode = 0; + atv_demod_set_state(ATVDEMOD_STATE_IDEL); pr_info("%s: OK.\n", __func__); @@ -258,7 +549,7 @@ static void atv_demod_set_params(struct dvb_frontend *fe, atvdemod_param->param.std = params->std; /* afc tune disable,must cancel wq before set tuner freq*/ - /* afc_timer_disable(); */ + afc_timer_disable(fe); if (fe->ops.tuner_ops.set_analog_params) ret = fe->ops.tuner_ops.set_analog_params(fe, params); @@ -303,7 +594,7 @@ static void atv_demod_set_params(struct dvb_frontend *fe, return; } - if (!is_atvdemod_scan_mode()) + if (!atv_demod_get_scan_mode()) atvauddemod_init(); pr_info("[%s] set std color %s, audio type %s.\n", @@ -314,6 +605,11 @@ static void atv_demod_set_params(struct dvb_frontend *fe, __func__, amlatvdemod_devp->if_freq, amlatvdemod_devp->if_inv); } + + /* afc tune enable */ + if ((fe->ops.info.type == FE_ANALOG) + && (atv_demod_get_scan_mode() == 0)) + afc_timer_enable(fe); } static int atv_demod_has_signal(struct dvb_frontend *fe, u16 *signal) @@ -339,9 +635,9 @@ static int atv_demod_has_signal(struct dvb_frontend *fe, u16 *signal) static void atv_demod_standby(struct dvb_frontend *fe) { - if (get_atvdemod_state() != ATVDEMOD_STATE_IDEL) { + if (atv_demod_get_state() != ATVDEMOD_STATE_IDEL) { atv_demod_leave_mode(); - set_atvdemod_state(ATVDEMOD_STATE_SLEEP); + atv_demod_set_state(ATVDEMOD_STATE_SLEEP); } pr_info("%s: OK.\n", __func__); @@ -361,6 +657,7 @@ static int atv_demod_get_afc(struct dvb_frontend *fe, s32 *afc) static void atv_demod_release(struct dvb_frontend *fe) { + int instance = 0; struct atv_demod_priv *priv = fe->analog_demod_priv; atv_demod_leave_mode(); @@ -368,12 +665,13 @@ static void atv_demod_release(struct dvb_frontend *fe) mutex_lock(&atv_demod_list_mutex); if (priv) - hybrid_tuner_release_state(priv); + instance = hybrid_tuner_release_state(priv); + + if (instance == 0) + fe->analog_demod_priv = NULL; mutex_unlock(&atv_demod_list_mutex); - fe->analog_demod_priv = NULL; - pr_info("%s: OK.\n", __func__); } @@ -390,7 +688,7 @@ static int atv_demod_set_config(struct dvb_frontend *fe, void *priv_cfg) switch (*state) { case AML_ATVDEMOD_INIT: - if (get_atvdemod_state() != ATVDEMOD_STATE_WORK) { + if (atv_demod_get_state() != ATVDEMOD_STATE_WORK) { atv_demod_enter_mode(); if (fe->ops.tuner_ops.set_config) fe->ops.tuner_ops.set_config(fe, NULL); @@ -398,7 +696,7 @@ static int atv_demod_set_config(struct dvb_frontend *fe, void *priv_cfg) break; case AML_ATVDEMOD_UNINIT: - if (get_atvdemod_state() != ATVDEMOD_STATE_IDEL) { + if (atv_demod_get_state() != ATVDEMOD_STATE_IDEL) { atv_demod_leave_mode(); if (fe->ops.tuner_ops.release) fe->ops.tuner_ops.release(fe); @@ -406,12 +704,22 @@ static int atv_demod_set_config(struct dvb_frontend *fe, void *priv_cfg) break; case AML_ATVDEMOD_RESUME: - if (get_atvdemod_state() == ATVDEMOD_STATE_SLEEP) { + if (atv_demod_get_state() == ATVDEMOD_STATE_SLEEP) { atv_demod_enter_mode(); if (fe->ops.tuner_ops.resume) fe->ops.tuner_ops.resume(fe); } break; + + case AML_ATVDEMOD_SCAN_MODE: + atv_demod_set_scan_mode(1); + afc_timer_disable(fe); + break; + + case AML_ATVDEMOD_UNSCAN_MODE: + atv_demod_set_scan_mode(0); + afc_timer_enable(fe); + break; } mutex_unlock(&atv_demod_list_mutex); @@ -421,7 +729,7 @@ static int atv_demod_set_config(struct dvb_frontend *fe, void *priv_cfg) static struct analog_demod_ops atvdemod_ops = { .info = { - .name = "atv_demod", + .name = DEVICE_NAME, }, .set_params = atv_demod_set_params, .has_signal = atv_demod_has_signal, @@ -452,6 +760,7 @@ struct dvb_frontend *aml_atvdemod_attach(struct dvb_frontend *fe, mutex_unlock(&atv_demod_list_mutex); return NULL; case 1: + INIT_WORK(&priv->demod_wq, aml_fe_do_work); fe->analog_demod_priv = priv; priv->standby = true; pr_info("aml_atvdemod found\n"); diff --git a/drivers/amlogic/atv_demod/atv_demod_ops.h b/drivers/amlogic/atv_demod/atv_demod_ops.h index 7623bfcbd4d0..43272b6c54f6 100644 --- a/drivers/amlogic/atv_demod/atv_demod_ops.h +++ b/drivers/amlogic/atv_demod/atv_demod_ops.h @@ -14,9 +14,16 @@ #ifndef __ATV_DEMOD_OPS_H__ #define __ATV_DEMOD_OPS_H__ -#define AML_ATVDEMOD_RESUME 0x2 -#define AML_ATVDEMOD_INIT 0x1 -#define AML_ATVDEMOD_UNINIT 0x0 +#define AML_ATVDEMOD_UNINIT 0x0 +#define AML_ATVDEMOD_INIT 0x1 +#define AML_ATVDEMOD_RESUME 0x2 +#define AML_ATVDEMOD_SCAN_MODE 0x3 +#define AML_ATVDEMOD_UNSCAN_MODE 0x4 + +#define AFC_BEST_LOCK 50 +#define ATV_AFC_500KHZ 500000 +#define ATV_AFC_1_0MHZ 1000000 +#define ATV_AFC_2_0MHZ 2000000 #include "drivers/media/dvb-core/dvb_frontend.h" #include "drivers/media/tuners/tuner-i2c.h" @@ -30,6 +37,7 @@ struct atv_demod_priv { bool standby; struct aml_atvdemod_parameters atvdemod_param; + struct work_struct demod_wq; }; extern int atv_demod_enter_mode(void); diff --git a/drivers/amlogic/atv_demod/atv_demod_v4l2.c b/drivers/amlogic/atv_demod/atv_demod_v4l2.c index 324167656a8f..e7a386be0bd6 100644 --- a/drivers/amlogic/atv_demod/atv_demod_v4l2.c +++ b/drivers/amlogic/atv_demod/atv_demod_v4l2.c @@ -43,12 +43,6 @@ static DEFINE_MUTEX(v4l2_fe_mutex); /* static int v4l2_shutdown_timeout;*/ - -#define AFC_BEST_LOCK 50 -#define ATV_AFC_500KHZ 500000 -#define ATV_AFC_1_0MHZ 1000000 -#define ATV_AFC_2_0MHZ 2000000 - static int tuner_status_cnt = 8; /* 4-->16 test on sky mxl661 */ module_param(tuner_status_cnt, int, 0644); MODULE_DESCRIPTION("after write a freq, max cnt value of read tuner status\n"); @@ -444,6 +438,7 @@ static enum v4l2_search v4l2_frontend_search(struct v4l2_frontend *v4l2_fe) bool try_secam = false; int ret = -1; unsigned int tuner_id = v4l2_fe->tuner_id; + int priv_cfg = 0; #ifdef DEBUG_TIME_CUS unsigned int time_start, time_end, time_delta; @@ -454,7 +449,8 @@ static enum v4l2_search v4l2_frontend_search(struct v4l2_frontend *v4l2_fe) 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_params || + !fe->ops.analog_ops.set_config)) { pr_err("[%s] error: NULL function or pointer.\n", __func__); return V4L2_SEARCH_INVALID; } @@ -500,9 +496,8 @@ static enum v4l2_search v4l2_frontend_search(struct v4l2_frontend *v4l2_fe) __func__, v4l2_std_to_str(p->audmode)); } - /*afc tune disable*/ - //TODO: afc_timer_disable(); - //analog_search_flag = 1; + 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; @@ -658,9 +653,8 @@ static enum v4l2_search v4l2_frontend_search(struct v4l2_frontend *v4l2_fe) #endif /*sync param */ //aml_fe_analog_sync_frontend(fe); - /*afc tune enable*/ - //analog_search_flag = 0; - //afc_timer_enable(fe); + priv_cfg = AML_ATVDEMOD_UNSCAN_MODE; + fe->ops.analog_ops.set_config(fe, &priv_cfg); return V4L2_SEARCH_SUCCESS; } } @@ -711,9 +705,10 @@ static enum v4l2_search v4l2_frontend_search(struct v4l2_frontend *v4l2_fe) pr_dbg("[%s] [%d] over of range [min=%d, max=%d], search failed.\n", __func__, p->frequency, minafcfreq, maxafcfreq); p->frequency = set_freq; - /*afc tune enable*/ - //analog_search_flag = 0; - //afc_timer_enable(fe); + + priv_cfg = AML_ATVDEMOD_UNSCAN_MODE; + fe->ops.analog_ops.set_config(fe, &priv_cfg); + return DVBFE_ALGO_SEARCH_FAILED; } @@ -1149,6 +1144,157 @@ static unsigned int v4l2_frontend_poll(struct file *filp, return 0; } +static void v4l2_property_dump(struct v4l2_frontend *v4l2_fe, + bool is_set, struct v4l2_property *tvp) +{ + + /*int i = 0;*/ + + if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) { + pr_warn("%s: %s tvp.cmd = 0x%08x undefined\n", + __func__, + is_set ? "SET" : "GET", + tvp->cmd); + return; + } +#if 0 + pr_dbg("%s: %s tvp.cmd = 0x%08x (%s)\n", __func__, + is_set ? "SET" : "GET", + tvp->cmd, + v4l2_cmds[tvp->cmd].name); + + if (v4l2_cmds[tvp->cmd].buffer) { + pr_dbg("%s: tvp.u.buffer.len = 0x%02x\n", + __func__, tvp->u.buffer.len); + + for (i = 0; i < tvp->u.buffer.len; i++) + pr_dbg("%s: tvp.u.buffer.data[0x%02x] = 0x%02x\n", + __func__, i, tvp->u.buffer.data[i]); + } else { + pr_dbg("%s: tvp.u.data = 0x%08x\n", __func__, + tvp->u.data); + } +#endif +} + +static int v4l2_property_process_set(struct v4l2_frontend *v4l2_fe, + struct v4l2_property *tvp, struct file *file) +{ + int r = 0; + + v4l2_property_dump(v4l2_fe, true, tvp); + + switch (tvp->cmd) { + case V4L2_TUNE: + break; + case V4L2_NICAM: + break; + default: + return -EINVAL; + } + + return r; +} + +static int v4l2_property_process_get(struct v4l2_frontend *v4l2_fe, + struct v4l2_property *tvp, struct file *file) +{ + switch (tvp->cmd) { + case V4L2_NICAM: + tvp->u.data = 0; + break; + + default: + pr_dbg("%s: V4L2 property %d doesn't exist\n", + __func__, tvp->cmd); + return -EINVAL; + } + + v4l2_property_dump(v4l2_fe, false, tvp); + + return 0; +} + +static int v4l2_frontend_ioctl_properties(struct file *filp, + unsigned int cmd, void *parg) +{ + struct v4l2_frontend *v4l2_fe = video_get_drvdata(video_devdata(filp)); + int err = 0; + + struct v4l2_properties *tvps = parg; + struct v4l2_property *tvp = NULL; + int i = 0; + + pr_dbg("%s.\n", __func__); + + if (cmd == V4L2_SET_PROPERTY) { + pr_dbg("%s: properties.num = %d\n", __func__, tvps->num); + pr_dbg("%s: properties.props = %p\n", __func__, tvps->props); + + /* Put an arbitrary limit on the number of messages that can + * be sent at once + */ + if ((tvps->num == 0) || (tvps->num > V4L2_IOCTL_MAX_MSGS)) + return -EINVAL; + + tvp = memdup_user(tvps->props, tvps->num * sizeof(*tvp)); + if (IS_ERR(tvp)) + return PTR_ERR(tvp); + + for (i = 0; i < tvps->num; i++) { + err = v4l2_property_process_set(v4l2_fe, tvp + i, filp); + if (err < 0) + goto out; + (tvp + i)->result = err; + } + } else if (cmd == FE_GET_PROPERTY) { + pr_dbg("%s: properties.num = %d\n", __func__, tvps->num); + pr_dbg("%s: properties.props = %p\n", __func__, tvps->props); + + /* Put an arbitrary limit on the number of messages that can + * be sent at once + */ + if ((tvps->num == 0) || (tvps->num > V4L2_IOCTL_MAX_MSGS)) + return -EINVAL; + + tvp = memdup_user(tvps->props, tvps->num * sizeof(*tvp)); + if (IS_ERR(tvp)) + return PTR_ERR(tvp); + + /* + * Let's use our own copy of property cache, in order to + * avoid mangling with DTV zigzag logic, as drivers might + * return crap, if they don't check if the data is available + * before updating the properties cache. + */ +#if 0 + if (fepriv->state != V4L2FE_STATE_IDLE) { + err = v4l2_get_frontend(v4l2_fe, &getp, NULL); + if (err < 0) + goto out; + } +#endif + for (i = 0; i < tvps->num; i++) { + err = v4l2_property_process_get(v4l2_fe, tvp + i, filp); + if (err < 0) + goto out; + (tvp + i)->result = err; + } + + if (copy_to_user((void __user *)tvps->props, tvp, + tvps->num * sizeof(struct v4l2_property))) { + err = -EFAULT; + goto out; + } + + } else + err = -EOPNOTSUPP; + +out: + kfree(tvp); + return err; +} + static long v4l2_frontend_ioctl(struct file *filp, void *fh, bool valid_prio, unsigned int cmd, void *arg) { @@ -1199,6 +1345,11 @@ static long v4l2_frontend_ioctl(struct file *filp, void *fh, bool valid_prio, (enum v4l2_status *) arg); break; + case V4L2_SET_PROPERTY: + case V4L2_GET_PROPERTY: + ret = v4l2_frontend_ioctl_properties(filp, cmd, arg); + break; + default: break; } @@ -1451,6 +1602,16 @@ int v4l2_unresister_frontend(struct v4l2_frontend *v4l2_fe) return 0; } +void v4l2_frontend_detach(struct v4l2_frontend *v4l2_fe) +{ + if (v4l2_fe->fe.ops.tuner_ops.release) + v4l2_fe->fe.ops.tuner_ops.release(&v4l2_fe->fe); + if (v4l2_fe->fe.ops.analog_ops.release) + v4l2_fe->fe.ops.analog_ops.release(&v4l2_fe->fe); + if (v4l2_fe->fe.ops.release) + v4l2_fe->fe.ops.release(&v4l2_fe->fe); +} + int v4l2_frontend_suspend(struct v4l2_frontend *v4l2_fe) { int ret = 0; diff --git a/drivers/amlogic/atv_demod/atv_demod_v4l2.h b/drivers/amlogic/atv_demod/atv_demod_v4l2.h index 06b98981d42c..396bba5dd6a7 100644 --- a/drivers/amlogic/atv_demod/atv_demod_v4l2.h +++ b/drivers/amlogic/atv_demod/atv_demod_v4l2.h @@ -34,13 +34,19 @@ __r; \ }) +#define v4l2_detach(FUNCTION) symbol_put_addr(FUNCTION) + #else #define v4l2_attach(FUNCTION, ARGS...) ({ \ FUNCTION(ARGS); \ }) + +#define v4l2_detach(FUNCTION) {} + #endif /* CONFIG_MEDIA_ATTACH */ + #define V4L2_FE_NO_EXIT 0 #define V4L2_FE_NORMAL_EXIT 1 #define V4L2_FE_DEVICE_REMOVED 2 @@ -64,15 +70,22 @@ #define V4L2FE_STATE_LOSTLOCK (V4L2FE_STATE_ZIGZAG_FAST |\ V4L2FE_STATE_ZIGZAG_SLOW) +#define V4L2_IOCTL_MAX_MSGS 64 + #define V4L2_SET_FRONTEND _IOW('V', 105, struct v4l2_analog_parameters) #define V4L2_GET_FRONTEND _IOR('V', 106, struct v4l2_analog_parameters) #define V4L2_GET_EVENT _IOR('V', 107, struct v4l2_frontend_event) #define V4L2_SET_MODE _IOW('V', 108, int) #define V4L2_READ_STATUS _IOR('V', 109, enum v4l2_status) +#define V4L2_SET_PROPERTY _IOW('V', 111, struct v4l2_properties) +#define V4L2_GET_PROPERTY _IOR('V', 112, struct v4l2_properties) +#define ANALOG_FLAG_ENABLE_AFC 0X00000001 +#define ANALOG_FLAG_MANUL_SCAN 0x00000011 -#define ANALOG_FLAG_ENABLE_AFC 0X00000001 -#define ANALOG_FLAG_MANUL_SCAN 0x00000011 +#define V4L2_UNDEFINED 0 +#define V4L2_TUNE 1 +#define V4L2_NICAM 2 struct v4l2_analog_parameters { @@ -96,6 +109,26 @@ enum v4l2_status { }; /* application is recommended to reset */ /* DiSEqC, tone and parameters */ +struct v4l2_property { + __u32 cmd; + __u32 reserved[3]; + union { + __u32 data; + struct { + __u8 data[32]; + __u32 len; + __u32 reserved1[3]; + void *reserved2; + } buffer; + } u; + int result; +} __attribute__ ((__packed__)); + +struct v4l2_properties { + __u32 num; + struct v4l2_property *props; +}; + enum v4l2_search { V4L2_SEARCH_SUCCESS = (1 << 0), V4L2_SEARCH_ASLEEP = (1 << 1), @@ -183,6 +216,7 @@ struct v4l2_atvdemod_device { int v4l2_resister_frontend(struct v4l2_frontend *v4l2_fe); int v4l2_unresister_frontend(struct v4l2_frontend *v4l2_fe); +void v4l2_frontend_detach(struct v4l2_frontend *v4l2_fe); int v4l2_frontend_suspend(struct v4l2_frontend *v4l2_fe); int v4l2_frontend_resume(struct v4l2_frontend *v4l2_fe); diff --git a/drivers/amlogic/atv_demod/atvauddemod_func.h b/drivers/amlogic/atv_demod/atvauddemod_func.h index 4febf74b1bfa..4407241c68e9 100644 --- a/drivers/amlogic/atv_demod/atvauddemod_func.h +++ b/drivers/amlogic/atv_demod/atvauddemod_func.h @@ -9,9 +9,8 @@ extern int atvaudiodem_reg_read(unsigned int reg, unsigned int *val); extern int atvaudiodem_reg_write(unsigned int reg, unsigned int val); extern uint32_t adec_rd_reg(uint32_t addr); extern void adec_wr_reg(uint32_t reg, uint32_t val); +extern int atv_demod_get_state(void); extern int is_atvdemod_work(void); -int get_atvdemod_state(void); -void set_atvdemod_state(int state); extern int aml_atvdemod_get_btsc_sap_mode(void); extern void audio_mode_det(int mode);