18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 2009 Jochen Friedrich <jochen@scram.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/dvb/frontend.h>
118c2ecf20Sopenharmony_ci#include <linux/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "mc44s803.h"
178c2ecf20Sopenharmony_ci#include "mc44s803_priv.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define mc_printk(level, format, arg...)	\
208c2ecf20Sopenharmony_ci	printk(level "mc44s803: " format , ## arg)
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* Writes a single register */
238c2ecf20Sopenharmony_cistatic int mc44s803_writereg(struct mc44s803_priv *priv, u32 val)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	u8 buf[3];
268c2ecf20Sopenharmony_ci	struct i2c_msg msg = {
278c2ecf20Sopenharmony_ci		.addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3
288c2ecf20Sopenharmony_ci	};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	buf[0] = (val & 0xff0000) >> 16;
318c2ecf20Sopenharmony_ci	buf[1] = (val & 0xff00) >> 8;
328c2ecf20Sopenharmony_ci	buf[2] = (val & 0xff);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
358c2ecf20Sopenharmony_ci		mc_printk(KERN_WARNING, "I2C write failed\n");
368c2ecf20Sopenharmony_ci		return -EREMOTEIO;
378c2ecf20Sopenharmony_ci	}
388c2ecf20Sopenharmony_ci	return 0;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* Reads a single register */
428c2ecf20Sopenharmony_cistatic int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	u32 wval;
458c2ecf20Sopenharmony_ci	u8 buf[3];
468c2ecf20Sopenharmony_ci	int ret;
478c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
488c2ecf20Sopenharmony_ci		{ .addr = priv->cfg->i2c_address, .flags = I2C_M_RD,
498c2ecf20Sopenharmony_ci		  .buf = buf, .len = 3 },
508c2ecf20Sopenharmony_ci	};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) |
538c2ecf20Sopenharmony_ci	       MC44S803_REG_SM(reg, MC44S803_D);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	ret = mc44s803_writereg(priv, wval);
568c2ecf20Sopenharmony_ci	if (ret)
578c2ecf20Sopenharmony_ci		return ret;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (i2c_transfer(priv->i2c, msg, 1) != 1) {
608c2ecf20Sopenharmony_ci		mc_printk(KERN_WARNING, "I2C read failed\n");
618c2ecf20Sopenharmony_ci		return -EREMOTEIO;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	*val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return 0;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void mc44s803_release(struct dvb_frontend *fe)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct mc44s803_priv *priv = fe->tuner_priv;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	fe->tuner_priv = NULL;
748c2ecf20Sopenharmony_ci	kfree(priv);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int mc44s803_init(struct dvb_frontend *fe)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct mc44s803_priv *priv = fe->tuner_priv;
808c2ecf20Sopenharmony_ci	u32 val;
818c2ecf20Sopenharmony_ci	int err;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
848c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* Reset chip */
878c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) |
888c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_RS);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
918c2ecf20Sopenharmony_ci	if (err)
928c2ecf20Sopenharmony_ci		goto exit;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
978c2ecf20Sopenharmony_ci	if (err)
988c2ecf20Sopenharmony_ci		goto exit;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/* Power Up and Start Osc */
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
1038c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(0xC0, MC44S803_REFOSC) |
1048c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_OSCSEL);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
1078c2ecf20Sopenharmony_ci	if (err)
1088c2ecf20Sopenharmony_ci		goto exit;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) |
1118c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(0x200, MC44S803_POWER);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
1148c2ecf20Sopenharmony_ci	if (err)
1158c2ecf20Sopenharmony_ci		goto exit;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	msleep(10);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
1208c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(0x40, MC44S803_REFOSC) |
1218c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_OSCSEL);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
1248c2ecf20Sopenharmony_ci	if (err)
1258c2ecf20Sopenharmony_ci		goto exit;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	msleep(20);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* Setup Mixer */
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) |
1328c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_TRI_STATE) |
1338c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
1368c2ecf20Sopenharmony_ci	if (err)
1378c2ecf20Sopenharmony_ci		goto exit;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/* Setup Cirquit Adjust */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
1428c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_G1) |
1438c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_G3) |
1448c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
1458c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_G6) |
1468c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
1478c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(0x3, MC44S803_LP) |
1488c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_CLRF) |
1498c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_CLIF);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
1528c2ecf20Sopenharmony_ci	if (err)
1538c2ecf20Sopenharmony_ci		goto exit;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
1568c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_G1) |
1578c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_G3) |
1588c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
1598c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_G6) |
1608c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
1618c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(0x3, MC44S803_LP);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
1648c2ecf20Sopenharmony_ci	if (err)
1658c2ecf20Sopenharmony_ci		goto exit;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/* Setup Digtune */
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
1708c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(3, MC44S803_XOD);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
1738c2ecf20Sopenharmony_ci	if (err)
1748c2ecf20Sopenharmony_ci		goto exit;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/* Setup AGC */
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) |
1798c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_AT1) |
1808c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_AT2) |
1818c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) |
1828c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) |
1838c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_LNA0);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
1868c2ecf20Sopenharmony_ci	if (err)
1878c2ecf20Sopenharmony_ci		goto exit;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
1908c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0);
1918c2ecf20Sopenharmony_ci	return 0;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ciexit:
1948c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
1958c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	mc_printk(KERN_WARNING, "I/O Error\n");
1988c2ecf20Sopenharmony_ci	return err;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic int mc44s803_set_params(struct dvb_frontend *fe)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct mc44s803_priv *priv = fe->tuner_priv;
2048c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
2058c2ecf20Sopenharmony_ci	u32 r1, r2, n1, n2, lo1, lo2, freq, val;
2068c2ecf20Sopenharmony_ci	int err;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	priv->frequency = c->frequency;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	r1 = MC44S803_OSC / 1000000;
2118c2ecf20Sopenharmony_ci	r2 = MC44S803_OSC /  100000;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	n1 = (c->frequency + MC44S803_IF1 + 500000) / 1000000;
2148c2ecf20Sopenharmony_ci	freq = MC44S803_OSC / r1 * n1;
2158c2ecf20Sopenharmony_ci	lo1 = ((60 * n1) + (r1 / 2)) / r1;
2168c2ecf20Sopenharmony_ci	freq = freq - c->frequency;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	n2 = (freq - MC44S803_IF2 + 50000) / 100000;
2198c2ecf20Sopenharmony_ci	lo2 = ((60 * n2) + (r2 / 2)) / r2;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
2228c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) |
2258c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(r1-1, MC44S803_R1) |
2268c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(r2-1, MC44S803_R2) |
2278c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_REFBUF_EN);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
2308c2ecf20Sopenharmony_ci	if (err)
2318c2ecf20Sopenharmony_ci		goto exit;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) |
2348c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(n1-2, MC44S803_LO1);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
2378c2ecf20Sopenharmony_ci	if (err)
2388c2ecf20Sopenharmony_ci		goto exit;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) |
2418c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(n2-2, MC44S803_LO2);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
2448c2ecf20Sopenharmony_ci	if (err)
2458c2ecf20Sopenharmony_ci		goto exit;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
2488c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_DA) |
2498c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(lo1, MC44S803_LO_REF) |
2508c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_AT);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
2538c2ecf20Sopenharmony_ci	if (err)
2548c2ecf20Sopenharmony_ci		goto exit;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
2578c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(2, MC44S803_DA) |
2588c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(lo2, MC44S803_LO_REF) |
2598c2ecf20Sopenharmony_ci	      MC44S803_REG_SM(1, MC44S803_AT);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	err = mc44s803_writereg(priv, val);
2628c2ecf20Sopenharmony_ci	if (err)
2638c2ecf20Sopenharmony_ci		goto exit;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
2668c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return 0;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ciexit:
2718c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
2728c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	mc_printk(KERN_WARNING, "I/O Error\n");
2758c2ecf20Sopenharmony_ci	return err;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	struct mc44s803_priv *priv = fe->tuner_priv;
2818c2ecf20Sopenharmony_ci	*frequency = priv->frequency;
2828c2ecf20Sopenharmony_ci	return 0;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int mc44s803_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	*frequency = MC44S803_IF2; /* 36.125 MHz */
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops mc44s803_tuner_ops = {
2928c2ecf20Sopenharmony_ci	.info = {
2938c2ecf20Sopenharmony_ci		.name              = "Freescale MC44S803",
2948c2ecf20Sopenharmony_ci		.frequency_min_hz  =   48 * MHz,
2958c2ecf20Sopenharmony_ci		.frequency_max_hz  = 1000 * MHz,
2968c2ecf20Sopenharmony_ci		.frequency_step_hz =  100 * kHz,
2978c2ecf20Sopenharmony_ci	},
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	.release       = mc44s803_release,
3008c2ecf20Sopenharmony_ci	.init          = mc44s803_init,
3018c2ecf20Sopenharmony_ci	.set_params    = mc44s803_set_params,
3028c2ecf20Sopenharmony_ci	.get_frequency = mc44s803_get_frequency,
3038c2ecf20Sopenharmony_ci	.get_if_frequency = mc44s803_get_if_frequency,
3048c2ecf20Sopenharmony_ci};
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/* This functions tries to identify a MC44S803 tuner by reading the ID
3078c2ecf20Sopenharmony_ci   register. This is hasty. */
3088c2ecf20Sopenharmony_cistruct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
3098c2ecf20Sopenharmony_ci	 struct i2c_adapter *i2c, struct mc44s803_config *cfg)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct mc44s803_priv *priv;
3128c2ecf20Sopenharmony_ci	u32 reg;
3138c2ecf20Sopenharmony_ci	u8 id;
3148c2ecf20Sopenharmony_ci	int ret;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	reg = 0;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(struct mc44s803_priv), GFP_KERNEL);
3198c2ecf20Sopenharmony_ci	if (priv == NULL)
3208c2ecf20Sopenharmony_ci		return NULL;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	priv->cfg = cfg;
3238c2ecf20Sopenharmony_ci	priv->i2c = i2c;
3248c2ecf20Sopenharmony_ci	priv->fe  = fe;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
3278c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	ret = mc44s803_readreg(priv, MC44S803_REG_ID, &reg);
3308c2ecf20Sopenharmony_ci	if (ret)
3318c2ecf20Sopenharmony_ci		goto error;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	id = MC44S803_REG_MS(reg, MC44S803_ID);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (id != 0x14) {
3368c2ecf20Sopenharmony_ci		mc_printk(KERN_ERR, "unsupported ID (%x should be 0x14)\n",
3378c2ecf20Sopenharmony_ci			  id);
3388c2ecf20Sopenharmony_ci		goto error;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	mc_printk(KERN_INFO, "successfully identified (ID = %x)\n", id);
3428c2ecf20Sopenharmony_ci	memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops,
3438c2ecf20Sopenharmony_ci	       sizeof(struct dvb_tuner_ops));
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	fe->tuner_priv = priv;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
3488c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return fe;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cierror:
3538c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
3548c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	kfree(priv);
3578c2ecf20Sopenharmony_ci	return NULL;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mc44s803_attach);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jochen Friedrich");
3628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver");
3638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
364