blob: 964bc42c224eabe28bf823cf31dd1debff5aa2ca [file] [log] [blame]
/*
* Silicon labs atvdemod Device Driver
*
* Author: dezhi.kong <dezhi.kong@amlogic.com>
*
*
* Copyright (C) 2014 Amlogic Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* Standard Liniux Headers */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/io.h>
/* Amlogic Headers */
/* Local Headers */
#include "atvdemod_func.h"
#include "../aml_fe.h"
#include <uapi/linux/dvb/frontend.h>
#include <linux/amlogic/tvin/tvin.h>
#define ATVDEMOD_DEVICE_NAME "amlatvdemod"
#define ATVDEMOD_DRIVER_NAME "amlatvdemod"
#define ATVDEMOD_MODULE_NAME "amlatvdemod"
#define ATVDEMOD_CLASS_NAME "amlatvdemod"
struct amlatvdemod_device_s *amlatvdemod_devp;
#define AMLATVDEMOD_VER "Ref.2015/09/01a"
static int afc_wave_cnt;
static int last_frq, last_std;
unsigned int reg_23cf = 0x88188832; /*IIR filter*/
module_param(reg_23cf, uint, 0664);
MODULE_PARM_DESC(reg_23cf, "\n reg_23cf\n");
unsigned int atvdemod_scan_mode; /*IIR filter*/
module_param(atvdemod_scan_mode, uint, 0664);
MODULE_PARM_DESC(atvdemod_scan_mode, "\n atvdemod_scan_mode\n");
/* used for resume */
#define ATVDEMOD_STATE_IDEL 0
#define ATVDEMOD_STATE_WORK 1
#define ATVDEMOD_STATE_SLEEP 2
static int atvdemod_state = ATVDEMOD_STATE_IDEL;
int is_atvdemod_scan_mode(void)
{
return atvdemod_scan_mode;
}
EXPORT_SYMBOL(is_atvdemod_scan_mode);
static int aml_atvdemod_enter_mode(struct aml_fe *fe, int mode);
/*static void sound_store(const char *buff, v4l2_std_id *std);*/
static ssize_t aml_atvdemod_store(struct class *cls,
struct class_attribute *attr, const char *buf,
size_t count)
{
int n = 0;
unsigned int ret = 0;
char *buf_orig, *ps, *token;
char *parm[4];
unsigned int data_snr[128];
unsigned int data_snr_avg;
int data_afc, block_addr, block_reg, block_val = 0;
int i, val = 0;
unsigned long tmp = 0;
struct aml_fe *atvdemod_fe = NULL;
buf_orig = kstrdup(buf, GFP_KERNEL);
ps = buf_orig;
block_addr = 0;
block_reg = 0;
while (1) {
token = strsep(&ps, "\n");
if (token == NULL)
break;
if (*token == '\0')
continue;
parm[n++] = token;
}
if (!strncmp(parm[0], "init", strlen("init"))) {
ret = aml_atvdemod_enter_mode(atvdemod_fe, 0);
if (ret)
pr_info("[tuner..] atv_restart error.\n");
} else if (!strcmp(parm[0], "tune")) {
/* val = simple_strtol(parm[1], NULL, 10); */
} else if (!strcmp(parm[0], "set")) {
if (!strncmp(parm[1], "avout_gain", strlen("avout_gain"))) {
if (kstrtoul(buf+strlen("avout_offset")+1, 10,
&tmp) == 0)
val = tmp;
atv_dmd_wr_byte(0x0c, 0x01, val&0xff);
} else if (!strncmp(parm[1], "avout_offset",
strlen("avout_offset"))) {
if (kstrtoul(buf+strlen("avout_offset")+1, 10,
&tmp) == 0)
val = tmp;
atv_dmd_wr_byte(0x0c, 0x04, val&0xff);
} else if (!strncmp(parm[1], "atv_gain", strlen("atv_gain"))) {
if (kstrtoul(buf+strlen("atv_gain")+1, 10, &tmp) == 0)
val = tmp;
atv_dmd_wr_byte(0x19, 0x01, val&0xff);
} else if (!strncmp(parm[1], "atv_offset",
strlen("atv_offset"))) {
if (kstrtoul(buf+strlen("atv_offset")+1, 10,
&tmp) == 0)
val = tmp;
atv_dmd_wr_byte(0x19, 0x04, val&0xff);
}
} else if (!strcmp(parm[0], "get")) {
if (!strncmp(parm[1], "avout_gain", strlen("avout_gain"))) {
val = atv_dmd_rd_byte(0x0c, 0x01);
pr_dbg("avout_gain:0x%x\n", val);
} else if (!strncmp(parm[1], "avout_offset",
strlen("avout_offset"))) {
val = atv_dmd_rd_byte(0x0c, 0x04);
pr_dbg("avout_offset:0x%x\n", val);
} else if (!strncmp(parm[1], "atv_gain", strlen("atv_gain"))) {
val = atv_dmd_rd_byte(0x19, 0x01);
pr_dbg("atv_gain:0x%x\n", val);
} else if (!strncmp(parm[1], "atv_offset",
strlen("atv_offset"))) {
val = atv_dmd_rd_byte(0x19, 0x04);
pr_dbg("atv_offset:0x%x\n", val);
}
} else if (!strncmp(parm[0], "snr_hist", strlen("snr_hist"))) {
data_snr_avg = 0;
for (i = 0; i < 128; i++) {
data_snr[i] =
(atv_dmd_rd_long(APB_BLOCK_ADDR_VDAGC, 0x50) >> 8);
usleep_range(50*1000, 50*1000+100);
data_snr_avg += data_snr[i];
}
data_snr_avg = data_snr_avg / 128;
pr_dbg("**********snr_hist_128avg:0x%x(%d)*********\n",
data_snr_avg, data_snr_avg);
} else if (!strncmp(parm[0], "afc_info", strlen("afc_info"))) {
data_afc = retrieve_vpll_carrier_afc();
pr_dbg("[amlatvdemod..]afc %d Khz.\n", data_afc);
} else if (!strncmp(parm[0], "ver_info", strlen("ver_info"))) {
pr_dbg("[amlatvdemod..]aml_atvdemod_ver %s.\n",
AMLATVDEMOD_VER);
} else if (!strncmp(parm[0], "audio_autodet",
strlen("audio_autodet"))) {
aml_audiomode_autodet(NULL);
} else if (!strncmp(parm[0], "overmodule_det",
strlen("overmodule_det"))) {
/* unsigned long over_threshold, */
/* int det_mode = auto_det_mode; */
aml_atvdemod_overmodule_det();
} else if (!strncmp(parm[0], "audio_gain_set",
strlen("audio_gain_set"))) {
if (kstrtoul(buf+strlen("audio_gain_set")+1, 16, &tmp) == 0)
val = tmp;
aml_audio_valume_gain_set(val);
pr_dbg("audio_gain_set : %d\n", val);
} else if (!strncmp(parm[0], "audio_gain_get",
strlen("audio_gain_get"))) {
val = aml_audio_valume_gain_get();
pr_dbg("audio_gain_get : %d\n", val);
} else if (!strncmp(parm[0], "fix_pwm_adj", strlen("fix_pwm_adj"))) {
if (kstrtoul(parm[1], 10, &tmp) == 0) {
val = tmp;
aml_fix_PWM_adjust(val);
}
} else if (!strncmp(parm[0], "rs", strlen("rs"))) {
if (kstrtoul(parm[1], 16, &tmp) == 0)
block_addr = tmp;
if (kstrtoul(parm[2], 16, &tmp) == 0)
block_reg = tmp;
if (block_addr < APB_BLOCK_ADDR_TOP)
block_val = atv_dmd_rd_long(block_addr, block_reg);
pr_info("rs block_addr:0x%x,block_reg:0x%x,block_val:0x%x\n",
block_addr, block_reg, block_val);
} else if (!strncmp(parm[0], "ws", strlen("ws"))) {
if (kstrtoul(parm[1], 16, &tmp) == 0)
block_addr = tmp;
if (kstrtoul(parm[2], 16, &tmp) == 0)
block_reg = tmp;
if (kstrtoul(parm[3], 16, &tmp) == 0)
block_val = tmp;
if (block_addr < APB_BLOCK_ADDR_TOP)
atv_dmd_wr_long(block_addr, block_reg, block_val);
pr_info("ws block_addr:0x%x,block_reg:0x%x,block_val:0x%x\n",
block_addr, block_reg, block_val);
block_val = atv_dmd_rd_long(block_addr, block_reg);
pr_info("readback_val:0x%x\n", block_val);
} else if (!strncmp(parm[0], "pin_mux", strlen("pin_mux"))) {
amlatvdemod_devp->pin =
devm_pinctrl_get_select(amlatvdemod_devp->dev,
amlatvdemod_devp->pin_name);
pr_dbg("atvdemod agc pinmux name:%s\n",
amlatvdemod_devp->pin_name);
} else if (!strncmp(parm[0], "snr_cur", strlen("snr_cur"))) {
data_snr_avg = aml_atvdemod_get_snr_ex();
pr_dbg("**********snr_cur:%d*********\n", data_snr_avg);
} else
pr_dbg("invalid command\n");
kfree(buf_orig);
return count;
}
static ssize_t aml_atvdemod_show(struct class *cls,
struct class_attribute *attr, char *buff)
{
pr_dbg("\n usage:\n");
pr_dbg("[get soft version] echo ver_info > /sys/class/amlatvdemod/atvdemod_debug\n");
pr_dbg("[get afc value] echo afc_info > /sys/class/amlatvdemod/atvdemod_debug\n");
pr_dbg("[reinit atvdemod] echo init > /sys/class/amlatvdemod/atvdemod_debug\n");
pr_dbg("[get av-out-gain/av-out-offset/atv-gain/atv-offset]:\n"
"echo get av_gain/av_offset/atv_gain/atv_offset > /sys/class/amlatvdemod/atvdemod_debug\n");
pr_dbg("[set av-out-gain/av-out-offset/atv-gain/atv-offset]:\n"
"echo set av_gain/av_offset/atv_gain/atv_offset val(0~255) > /sys/class/amlatvdemod/atvdemod_debug\n");
return 0;
}
static CLASS_ATTR(atvdemod_debug, 0644, aml_atvdemod_show, aml_atvdemod_store);
void aml_atvdemod_set_frequency(unsigned int freq)
{
}
/*static void aml_atvdemod_set_std(void);*/
/*try audmode B,CH,I,DK,return the sound level*/
/*static unsigned char set_video_audio_mode(unsigned char color,
*unsigned char audmode);
*/
/*static void aml_atvdemod_get_status(struct dvb_frontend *fe,
*void *stat);
*/
/*static void aaaml_atvdemod_get_pll_status(struct dvb_frontend *fe,
*void *stat);
*/
static int aml_atvdemod_fe_init(struct aml_fe_dev *dev)
{
int error_code = 0;
if (!dev) {
pr_dbg("[amlatvdemod..]%s: null pointer error.\n", __func__);
return -1;
}
return error_code;
}
static int aml_atvdemod_enter_mode(struct aml_fe *fe, int mode)
{
int err_code;
if (amlatvdemod_devp->pin_name != NULL)
amlatvdemod_devp->pin =
devm_pinctrl_get_select(amlatvdemod_devp->dev,
amlatvdemod_devp->pin_name);
/* printk("\n%s: set atvdemod pll...\n",__func__); */
adc_set_pll_cntl(1, 0x1);
atvdemod_clk_init();
err_code = atvdemod_init();
if (err_code) {
pr_dbg("[amlatvdemod..]%s init atvdemod error.\n", __func__);
return err_code;
}
set_aft_thread_enable(1);
atvdemod_state = ATVDEMOD_STATE_WORK;
return 0;
}
static int aml_atvdemod_leave_mode(struct aml_fe *fe, int mode)
{
set_aft_thread_enable(0);
atvdemod_uninit();
if (amlatvdemod_devp->pin != NULL) {
devm_pinctrl_put(amlatvdemod_devp->pin);
amlatvdemod_devp->pin = NULL;
}
/* reset adc pll flag */
/* printk("\n%s: init atvdemod flag...\n",__func__); */
adc_set_pll_cntl(0, 0x1);
atvdemod_state = ATVDEMOD_STATE_IDEL;
return 0;
}
static int aml_atvdemod_suspend(struct aml_fe_dev *dev)
{
pr_info("%s\n", __func__);
if (atvdemod_state != ATVDEMOD_STATE_IDEL) {
aml_atvdemod_leave_mode(NULL, 0);
atvdemod_state = ATVDEMOD_STATE_SLEEP;
}
return 0;
}
static int aml_atvdemod_resume(struct aml_fe_dev *dev)
{
pr_info("%s\n", __func__);
if (atvdemod_state == ATVDEMOD_STATE_SLEEP)
aml_atvdemod_enter_mode(NULL, 0);
return 0;
}
/*
*static int aml_atvdemod_get_afc(struct dvb_frontend *fe,int *afc)
*{
* return 0;
*}
*/
/*ret:5~100;the val is bigger,the signal is better*/
int aml_atvdemod_get_snr(struct dvb_frontend *fe)
{
#if 1
return get_atvdemod_snr_val();
#else
unsigned int snr_val;
int ret;
snr_val = atv_dmd_rd_long(APB_BLOCK_ADDR_VDAGC, 0x50) >> 8;
/* snr_val:900000~0xffffff,ret:5~15 */
if (snr_val > 900000)
ret = 15 - (snr_val - 900000)*10/(0xffffff - 900000);
/* snr_val:158000~900000,ret:15~30 */
else if (snr_val > 158000)
ret = 30 - (snr_val - 158000)*15/(900000 - 158000);
/* snr_val:31600~158000,ret:30~50 */
else if (snr_val > 31600)
ret = 50 - (snr_val - 31600)*20/(158000 - 31600);
/* snr_val:316~31600,ret:50~80 */
else if (snr_val > 316)
ret = 80 - (snr_val - 316)*30/(31600 - 316);
/* snr_val:0~316,ret:80~100 */
else
ret = 100 - (316 - snr_val)*20/316;
return ret;
#endif
}
EXPORT_SYMBOL(aml_atvdemod_get_snr);
int aml_atvdemod_get_snr_ex(void)
{
#if 1
return get_atvdemod_snr_val();
#else
unsigned int snr_val;
int ret;
snr_val = atv_dmd_rd_long(APB_BLOCK_ADDR_VDAGC, 0x50) >> 8;
/* snr_val:900000~0xffffff,ret:5~15 */
if (snr_val > 900000)
ret = 15 - (snr_val - 900000)*10/(0xffffff - 900000);
/* snr_val:158000~900000,ret:15~30 */
else if (snr_val > 158000)
ret = 30 - (snr_val - 158000)*15/(900000 - 158000);
/* snr_val:31600~158000,ret:30~50 */
else if (snr_val > 31600)
ret = 50 - (snr_val - 31600)*20/(158000 - 31600);
/* snr_val:316~31600,ret:50~80 */
else if (snr_val > 316)
ret = 80 - (snr_val - 316)*30/(31600 - 316);
/* snr_val:0~316,ret:80~100 */
else
ret = 100 - (316 - snr_val)*20/316;
return ret;
#endif
}
EXPORT_SYMBOL(aml_atvdemod_get_snr_ex);
/*tuner lock status & demod lock status should be same in silicon tuner*/
static int aml_atvdemod_get_status(struct dvb_frontend *fe, void *stat)
{
int video_lock;
fe_status_t *status = (fe_status_t *) stat;
retrieve_video_lock(&video_lock);
if ((video_lock & 0x1) == 0) {
/* *status = FE_HAS_LOCK;*/
*status = FE_TIMEDOUT;
pr_info("video lock:locked\n");
} else {
pr_info("video lock:unlocked\n");
*status = FE_TIMEDOUT;
/* *status = FE_HAS_LOCK;*/
}
return 0;
}
/*tuner lock status & demod lock status should be same in silicon tuner*/
/* force return lock, for atvdemo status not sure */
static void aml_atvdemod_get_pll_status(struct dvb_frontend *fe, void *stat)
{
int vpll_lock;
fe_status_t *status = (fe_status_t *) stat;
retrieve_vpll_carrier_lock(&vpll_lock);
if ((vpll_lock&0x1) == 0) {
*status = FE_HAS_LOCK;
pr_info("visual carrier lock:locked\n");
} else {
pr_info("visual carrier lock:unlocked\n");
*status = FE_TIMEDOUT;
/* *status = FE_HAS_LOCK;*/
}
}
static int aml_atvdemod_get_atv_status(struct dvb_frontend *fe,
struct atv_status_s *atv_status)
{
int vpll_lock = 0, line_lock = 0;
int try_std = 1;
int loop_cnt = 5;
int cnt = 10;
int try_std_cnt = 0;
static int last_report_freq;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
while (fe && atv_status && loop_cnt--) {
atv_status->afc = retrieve_vpll_carrier_afc();
retrieve_vpll_carrier_lock(&vpll_lock);
line_lock = atv_dmd_rd_byte(APB_BLOCK_ADDR_VDAGC, 0x4f)&0x10;
if ((vpll_lock&0x1) == 0 || line_lock == 0) {
atv_status->atv_lock = 1;
try_std_cnt = 2;
while (try_std_cnt--) {
atv_status->afc = retrieve_vpll_carrier_afc();
if (atv_status->afc > 1500
&& atvdemod_scan_mode) {
if ((c->analog.std & 0xff000000)
!= V4L2_COLOR_STD_PAL) {
c->analog.std =
V4L2_COLOR_STD_PAL
| V4L2_STD_PAL_BG;
c->frequency += 1;
fe->ops.set_frontend(fe);
msleep(20);
} else {
c->analog.std =
V4L2_COLOR_STD_NTSC
| V4L2_STD_NTSC_M;
c->frequency += 1;
fe->ops.set_frontend(fe);
usleep_range(20*1000,
20*1000+100);
}
atv_status->afc =
retrieve_vpll_carrier_afc();
cnt = 4;
while (cnt--) {
if (atv_status->afc < 1500)
break;
atv_status->afc =
retrieve_vpll_carrier_afc();
usleep_range(5*1000, 5*1000+100);
}
if (atv_status->afc < 1500)
break;
}
}
if (atv_status->afc > 4000 && !atvdemod_scan_mode)
atv_status->atv_lock = 0;
if (last_report_freq != c->frequency)
last_report_freq = c->frequency;
if (atvdemod_scan_mode)
pr_err("%s,lock freq:%d, afc:%d\n", __func__,
c->frequency, atv_status->afc);
break;
} else if (try_std%3 == 0 && atvdemod_scan_mode) {
if ((c->analog.std & 0xff000000)
!= V4L2_COLOR_STD_PAL) {
c->analog.std =
V4L2_COLOR_STD_PAL | V4L2_STD_PAL_DK;
}
if (abs(c->frequency - last_report_freq) > 1000000) {
c->frequency -= 500000;
pr_err("@@@ %s freq:%d unlock,try back 0.5M\n",
__func__, c->frequency);
} else
c->frequency += 1;
fe->ops.set_frontend(fe);
usleep_range(10*1000, 10*1000+100);
}
if (atvdemod_scan_mode)
pr_err("@@@ %s freq:%d unlock, read lock again\n",
__func__, c->frequency);
if (atvdemod_scan_mode == 0)
usleep_range(10*1000, 10*1000+100);
else
usleep_range(1000, 1200);
atv_status->atv_lock = 0;
try_std++;
}
if (atvdemod_scan_mode == 0) {
if (abs(atv_status->afc) < 20)
afc_wave_cnt = 0;
if (500*1000 > abs(last_frq - c->frequency)
&& 20 < abs(atv_status->afc)
&& 200 > abs(atv_status->afc)) {
afc_wave_cnt++;
pr_err("%s play mode,afc_wave_cnt:%d\n",
__func__, afc_wave_cnt);
if (afc_wave_cnt < 20) {
atv_status->afc = 0;
pr_err("%s, afc is wave,ignore\n", __func__);
}
}
}
return 0;
}
void aml_atvdemod_set_params(struct dvb_frontend *fe,
struct analog_parameters *p)
{
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
if (fe->ops.info.type == FE_ANALOG) {
if ((p->std != amlatvdemod_devp->parm.std) ||
(p->tuner_id == AM_TUNER_R840) ||
(p->tuner_id == AM_TUNER_SI2151) ||
(p->tuner_id == AM_TUNER_MXL661)) {
amlatvdemod_devp->parm.std = p->std;
amlatvdemod_devp->parm.if_freq = p->if_freq;
amlatvdemod_devp->parm.if_inv = p->if_inv;
amlatvdemod_devp->parm.tuner_id = p->tuner_id;
/* open AGC if needed */
if (amlatvdemod_devp->pin != NULL)
devm_pinctrl_put(amlatvdemod_devp->pin);
if (amlatvdemod_devp->pin_name)
amlatvdemod_devp->pin =
devm_pinctrl_get_select(amlatvdemod_devp->dev,
amlatvdemod_devp->pin_name);
atv_dmd_set_std();
last_frq = c->frequency;
last_std = c->analog.std;
pr_info("[amlatvdemod..]%s set std color %s, audio type %s.\n",
__func__,
v4l2_std_to_str(0xff000000&amlatvdemod_devp->parm.std),
v4l2_std_to_str(0xffffff&amlatvdemod_devp->parm.std));
pr_info("[amlatvdemod..]%s set if_freq 0x%x, if_inv 0x%x.\n",
__func__, amlatvdemod_devp->parm.if_freq,
amlatvdemod_devp->parm.if_inv);
}
}
}
static int aml_atvdemod_get_afc(struct dvb_frontend *fe, s32 *afc)
{
*afc = retrieve_vpll_carrier_afc();
pr_info("[amlatvdemod..]%s afc %d.\n", __func__, *afc);
return 0;
}
static int aml_atvdemod_get_ops(struct aml_fe_dev *dev, int mode, void *ops)
{
struct analog_demod_ops *aml_analog_ops =
(struct analog_demod_ops *)ops;
if (!ops) {
pr_dbg("[amlatvdemod..]%s null pointer error.\n", __func__);
return -1;
}
aml_analog_ops->get_afc = aml_atvdemod_get_afc;
aml_analog_ops->get_snr = aml_atvdemod_get_snr;
aml_analog_ops->get_status = aml_atvdemod_get_status;
aml_analog_ops->set_params = aml_atvdemod_set_params;
aml_analog_ops->get_pll_status = aml_atvdemod_get_pll_status;
aml_analog_ops->get_atv_status = aml_atvdemod_get_atv_status;
return 0;
}
static struct aml_fe_drv aml_atvdemod_drv = {
.name = "aml_atv_demod",
.capability = AM_FE_ANALOG,
.id = AM_ATV_DEMOD_AML,
.get_ops = aml_atvdemod_get_ops,
.init = aml_atvdemod_fe_init,
.enter_mode = aml_atvdemod_enter_mode,
.leave_mode = aml_atvdemod_leave_mode,
.suspend = aml_atvdemod_suspend,
.resume = aml_atvdemod_resume,
};
struct class *aml_atvdemod_clsp;
static void aml_atvdemod_dt_parse(struct platform_device *pdev)
{
struct device_node *node;
unsigned int val;
int ret;
node = pdev->dev.of_node;
/* get integer value */
if (node) {
ret = of_property_read_u32(node, "reg_23cf", &val);
if (ret)
pr_dbg("Can't find reg_23cf.\n");
else
reg_23cf = val;
ret = of_property_read_u32(node, "audio_gain_val", &val);
if (ret)
pr_dbg("Can't find audio_gain_val.\n");
else
set_audio_gain_val(val);
ret = of_property_read_u32(node, "video_gain_val", &val);
if (ret)
pr_dbg("Can't find video_gain_val.\n");
else
set_video_gain_val(val);
/* agc pin mux */
ret = of_property_read_string(node, "pinctrl-names",
&amlatvdemod_devp->pin_name);
if (!ret) {
/* amlatvdemod_devp->pin = */
/* devm_pinctrl_get_select(&pdev->dev, */
/* amlatvdemod_devp->pin_name); */
pr_dbg("atvdemod agc pinmux name:%s\n",
amlatvdemod_devp->pin_name);
}
}
}
static struct resource amlatvdemod_memobj;
void __iomem *amlatvdemod_reg_base;
void __iomem *amlatvdemod_hiu_reg_base;
void __iomem *amlatvdemod_periphs_reg_base;
int amlatvdemod_reg_read(unsigned int reg, unsigned int *val)
{
*val = readl(amlatvdemod_reg_base + reg);
return 0;
}
int amlatvdemod_reg_write(unsigned int reg, unsigned int val)
{
writel(val, (amlatvdemod_reg_base + reg));
return 0;
}
int amlatvdemod_hiu_reg_read(unsigned int reg, unsigned int *val)
{
*val = readl(amlatvdemod_hiu_reg_base + ((reg - 0x1000)<<2));
return 0;
}
int amlatvdemod_hiu_reg_write(unsigned int reg, unsigned int val)
{
writel(val, (amlatvdemod_hiu_reg_base + ((reg - 0x1000)<<2)));
return 0;
}
int amlatvdemod_periphs_reg_read(unsigned int reg, unsigned int *val)
{
*val = readl(amlatvdemod_periphs_reg_base + ((reg - 0x1000)<<2));
return 0;
}
int amlatvdemod_periphs_reg_write(unsigned int reg, unsigned int val)
{
writel(val, (amlatvdemod_periphs_reg_base + ((reg - 0x1000)<<2)));
return 0;
}
static int aml_atvdemod_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res;
int size_io_reg;
res = &amlatvdemod_memobj;
amlatvdemod_devp = kmalloc(sizeof(struct amlatvdemod_device_s),
GFP_KERNEL);
if (!amlatvdemod_devp)
goto fail_alloc_region;
memset(amlatvdemod_devp, 0, sizeof(struct amlatvdemod_device_s));
amlatvdemod_devp->clsp = class_create(THIS_MODULE,
ATVDEMOD_DEVICE_NAME);
if (!amlatvdemod_devp->clsp)
goto fail_create_class;
ret = class_create_file(amlatvdemod_devp->clsp,
&class_attr_atvdemod_debug);
if (ret)
goto fail_class_create_file;
amlatvdemod_devp->dev = &pdev->dev;
/*reg mem*/
pr_info("%s:amlatvdemod 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);
pr_info("amlatvdemod_probe reg=%p,size=%x\n",
(void *)res->start, size_io_reg);
amlatvdemod_reg_base =
devm_ioremap_nocache(&pdev->dev, res->start, size_io_reg);
if (!amlatvdemod_reg_base) {
dev_err(&pdev->dev, "amlatvdemod ioremap failed\n");
return -ENOMEM;
}
pr_info("%s: amlatvdemod maped reg_base =%p, size=%x\n",
__func__, amlatvdemod_reg_base, size_io_reg);
/*remap hiu mem*/
amlatvdemod_hiu_reg_base = ioremap(0xc883c000, 0x2000);
/*remap periphs mem*/
amlatvdemod_periphs_reg_base = ioremap(0xc8834000, 0x2000);
/*initialize the tuner common struct and register*/
aml_register_fe_drv(AM_DEV_ATV_DEMOD, &aml_atvdemod_drv);
aml_atvdemod_dt_parse(pdev);
pr_dbg("[amlatvdemod.] : probe ok.\n");
return 0;
fail_class_create_file:
pr_dbg("[amlatvdemod.] : atvdemod class file create error.\n");
class_destroy(amlatvdemod_devp->clsp);
fail_create_class:
pr_dbg("[amlatvdemod.] : atvdemod class create error.\n");
kfree(amlatvdemod_devp);
fail_alloc_region:
pr_dbg("[amlatvdemod.] : atvdemod alloc error.\n");
pr_dbg("[amlatvdemod.] : atvdemod_init fail.\n");
return ret;
}
static int __exit aml_atvdemod_remove(struct platform_device *pdev)
{
if (amlatvdemod_devp == NULL)
return -1;
class_destroy(amlatvdemod_devp->clsp);
aml_unregister_fe_drv(AM_DEV_ATV_DEMOD, &aml_atvdemod_drv);
kfree(amlatvdemod_devp);
pr_dbg("[amlatvdemod.] : amvecm_remove.\n");
return 0;
}
static const struct of_device_id aml_atvdemod_dt_match[] = {
{
.compatible = "amlogic, aml_atv_demod",
},
{},
};
static struct platform_driver aml_atvdemod_driver = {
.driver = {
.name = "aml_atv_demod",
.owner = THIS_MODULE,
.of_match_table = aml_atvdemod_dt_match,
},
.probe = aml_atvdemod_probe,
.remove = __exit_p(aml_atvdemod_remove),
};
static int __init aml_atvdemod_init(void)
{
if (platform_driver_register(&aml_atvdemod_driver)) {
pr_err("failed to register amlatvdemod driver module\n");
return -ENODEV;
}
pr_dbg("[amlatvdemod..]%s.\n", __func__);
return 0;
}
static void __exit aml_atvdemod_exit(void)
{
platform_driver_unregister(&aml_atvdemod_driver);
pr_dbg("[amlatvdemod..]%s: driver removed ok.\n", __func__);
}
MODULE_AUTHOR("dezhi.kong <dezhi.kong@amlogic.com>");
MODULE_DESCRIPTION("aml atv demod device driver");
MODULE_LICENSE("GPL");
fs_initcall(aml_atvdemod_init);
module_exit(aml_atvdemod_exit);