18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Driver for Maxim MAX2165 silicon tuner
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 2009 David T. L. Wong <davidtlwong@gmail.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
108c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/dvb/frontend.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "max2165.h"
198c2ecf20Sopenharmony_ci#include "max2165_priv.h"
208c2ecf20Sopenharmony_ci#include "tuner-i2c.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define dprintk(args...) \
238c2ecf20Sopenharmony_ci	do { \
248c2ecf20Sopenharmony_ci		if (debug) \
258c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "max2165: " args); \
268c2ecf20Sopenharmony_ci	} while (0)
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int debug;
298c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int max2165_write_reg(struct max2165_priv *priv, u8 reg, u8 data)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	int ret;
358c2ecf20Sopenharmony_ci	u8 buf[] = { reg, data };
368c2ecf20Sopenharmony_ci	struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	msg.addr = priv->config->i2c_address;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (debug >= 2)
418c2ecf20Sopenharmony_ci		dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	ret = i2c_transfer(priv->i2c, &msg, 1);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (ret != 1)
468c2ecf20Sopenharmony_ci		dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
478c2ecf20Sopenharmony_ci			__func__, reg, data, ret);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	return (ret != 1) ? -EIO : 0;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic int max2165_read_reg(struct max2165_priv *priv, u8 reg, u8 *p_data)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	int ret;
558c2ecf20Sopenharmony_ci	u8 dev_addr = priv->config->i2c_address;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	u8 b0[] = { reg };
588c2ecf20Sopenharmony_ci	u8 b1[] = { 0 };
598c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
608c2ecf20Sopenharmony_ci		{ .addr = dev_addr, .flags = 0, .buf = b0, .len = 1 },
618c2ecf20Sopenharmony_ci		{ .addr = dev_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 },
628c2ecf20Sopenharmony_ci	};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ret = i2c_transfer(priv->i2c, msg, 2);
658c2ecf20Sopenharmony_ci	if (ret != 2) {
668c2ecf20Sopenharmony_ci		dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret);
678c2ecf20Sopenharmony_ci		return -EIO;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	*p_data = b1[0];
718c2ecf20Sopenharmony_ci	if (debug >= 2)
728c2ecf20Sopenharmony_ci		dprintk("%s: reg=0x%02X, data=0x%02X\n",
738c2ecf20Sopenharmony_ci			__func__, reg, b1[0]);
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int max2165_mask_write_reg(struct max2165_priv *priv, u8 reg,
788c2ecf20Sopenharmony_ci	u8 mask, u8 data)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	int ret;
818c2ecf20Sopenharmony_ci	u8 v;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	data &= mask;
848c2ecf20Sopenharmony_ci	ret = max2165_read_reg(priv, reg, &v);
858c2ecf20Sopenharmony_ci	if (ret != 0)
868c2ecf20Sopenharmony_ci		return ret;
878c2ecf20Sopenharmony_ci	v &= ~mask;
888c2ecf20Sopenharmony_ci	v |= data;
898c2ecf20Sopenharmony_ci	ret = max2165_write_reg(priv, reg, v);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return ret;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int max2165_read_rom_table(struct max2165_priv *priv)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	u8 dat[3];
978c2ecf20Sopenharmony_ci	int i;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
1008c2ecf20Sopenharmony_ci		max2165_write_reg(priv, REG_ROM_TABLE_ADDR, i + 1);
1018c2ecf20Sopenharmony_ci		max2165_read_reg(priv, REG_ROM_TABLE_DATA, &dat[i]);
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	priv->tf_ntch_low_cfg = dat[0] >> 4;
1058c2ecf20Sopenharmony_ci	priv->tf_ntch_hi_cfg = dat[0] & 0x0F;
1068c2ecf20Sopenharmony_ci	priv->tf_balun_low_ref = dat[1] & 0x0F;
1078c2ecf20Sopenharmony_ci	priv->tf_balun_hi_ref = dat[1] >> 4;
1088c2ecf20Sopenharmony_ci	priv->bb_filter_7mhz_cfg = dat[2] & 0x0F;
1098c2ecf20Sopenharmony_ci	priv->bb_filter_8mhz_cfg = dat[2] >> 4;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	dprintk("tf_ntch_low_cfg = 0x%X\n", priv->tf_ntch_low_cfg);
1128c2ecf20Sopenharmony_ci	dprintk("tf_ntch_hi_cfg = 0x%X\n", priv->tf_ntch_hi_cfg);
1138c2ecf20Sopenharmony_ci	dprintk("tf_balun_low_ref = 0x%X\n", priv->tf_balun_low_ref);
1148c2ecf20Sopenharmony_ci	dprintk("tf_balun_hi_ref = 0x%X\n", priv->tf_balun_hi_ref);
1158c2ecf20Sopenharmony_ci	dprintk("bb_filter_7mhz_cfg = 0x%X\n", priv->bb_filter_7mhz_cfg);
1168c2ecf20Sopenharmony_ci	dprintk("bb_filter_8mhz_cfg = 0x%X\n", priv->bb_filter_8mhz_cfg);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return 0;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int max2165_set_osc(struct max2165_priv *priv, u8 osc /*MHz*/)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	u8 v;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	v = (osc / 2);
1268c2ecf20Sopenharmony_ci	if (v == 2)
1278c2ecf20Sopenharmony_ci		v = 0x7;
1288c2ecf20Sopenharmony_ci	else
1298c2ecf20Sopenharmony_ci		v -= 8;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	max2165_mask_write_reg(priv, REG_PLL_CFG, 0x07, v);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	u8 val;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (bw == 8000000)
1418c2ecf20Sopenharmony_ci		val = priv->bb_filter_8mhz_cfg;
1428c2ecf20Sopenharmony_ci	else
1438c2ecf20Sopenharmony_ci		val = priv->bb_filter_7mhz_cfg;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	max2165_mask_write_reg(priv, REG_BASEBAND_CTRL, 0xF0, val << 4);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	u32 remainder;
1538c2ecf20Sopenharmony_ci	u32 q, f = 0;
1548c2ecf20Sopenharmony_ci	int i;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (0 == divisor)
1578c2ecf20Sopenharmony_ci		return -EINVAL;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	q = dividend / divisor;
1608c2ecf20Sopenharmony_ci	remainder = dividend - q * divisor;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	for (i = 0; i < 31; i++) {
1638c2ecf20Sopenharmony_ci		remainder <<= 1;
1648c2ecf20Sopenharmony_ci		if (remainder >= divisor) {
1658c2ecf20Sopenharmony_ci			f += 1;
1668c2ecf20Sopenharmony_ci			remainder -= divisor;
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci		f <<= 1;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	*quotient = q;
1728c2ecf20Sopenharmony_ci	*fraction = f;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int max2165_set_rf(struct max2165_priv *priv, u32 freq)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	u8 tf;
1808c2ecf20Sopenharmony_ci	u8 tf_ntch;
1818c2ecf20Sopenharmony_ci	u32 t;
1828c2ecf20Sopenharmony_ci	u32 quotient, fraction;
1838c2ecf20Sopenharmony_ci	int ret;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* Set PLL divider according to RF frequency */
1868c2ecf20Sopenharmony_ci	ret = fixpt_div32(freq / 1000, priv->config->osc_clk * 1000,
1878c2ecf20Sopenharmony_ci			 &quotient, &fraction);
1888c2ecf20Sopenharmony_ci	if (ret != 0)
1898c2ecf20Sopenharmony_ci		return ret;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* 20-bit fraction */
1928c2ecf20Sopenharmony_ci	fraction >>= 12;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_NDIV_INT, quotient);
1958c2ecf20Sopenharmony_ci	max2165_mask_write_reg(priv, REG_NDIV_FRAC2, 0x0F, fraction >> 16);
1968c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_NDIV_FRAC1, fraction >> 8);
1978c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_NDIV_FRAC0, fraction);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/* Norch Filter */
2008c2ecf20Sopenharmony_ci	tf_ntch = (freq < 725000000) ?
2018c2ecf20Sopenharmony_ci		priv->tf_ntch_low_cfg : priv->tf_ntch_hi_cfg;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* Tracking filter balun */
2048c2ecf20Sopenharmony_ci	t = priv->tf_balun_low_ref;
2058c2ecf20Sopenharmony_ci	t += (priv->tf_balun_hi_ref - priv->tf_balun_low_ref)
2068c2ecf20Sopenharmony_ci		* (freq / 1000 - 470000) / (780000 - 470000);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	tf = t;
2098c2ecf20Sopenharmony_ci	dprintk("tf = %X\n", tf);
2108c2ecf20Sopenharmony_ci	tf |= tf_ntch << 4;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_TRACK_FILTER, tf);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return 0;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic void max2165_debug_status(struct max2165_priv *priv)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	u8 status, autotune;
2208c2ecf20Sopenharmony_ci	u8 auto_vco_success, auto_vco_active;
2218c2ecf20Sopenharmony_ci	u8 pll_locked;
2228c2ecf20Sopenharmony_ci	u8 dc_offset_low, dc_offset_hi;
2238c2ecf20Sopenharmony_ci	u8 signal_lv_over_threshold;
2248c2ecf20Sopenharmony_ci	u8 vco, vco_sub_band, adc;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	max2165_read_reg(priv, REG_STATUS, &status);
2278c2ecf20Sopenharmony_ci	max2165_read_reg(priv, REG_AUTOTUNE, &autotune);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	auto_vco_success = (status >> 6) & 0x01;
2308c2ecf20Sopenharmony_ci	auto_vco_active = (status >> 5) & 0x01;
2318c2ecf20Sopenharmony_ci	pll_locked = (status >> 4) & 0x01;
2328c2ecf20Sopenharmony_ci	dc_offset_low = (status >> 3) & 0x01;
2338c2ecf20Sopenharmony_ci	dc_offset_hi = (status >> 2) & 0x01;
2348c2ecf20Sopenharmony_ci	signal_lv_over_threshold = status & 0x01;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	vco = autotune >> 6;
2378c2ecf20Sopenharmony_ci	vco_sub_band = (autotune >> 3) & 0x7;
2388c2ecf20Sopenharmony_ci	adc = autotune & 0x7;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	dprintk("auto VCO active: %d, auto VCO success: %d\n",
2418c2ecf20Sopenharmony_ci		auto_vco_active, auto_vco_success);
2428c2ecf20Sopenharmony_ci	dprintk("PLL locked: %d\n", pll_locked);
2438c2ecf20Sopenharmony_ci	dprintk("DC offset low: %d, DC offset high: %d\n",
2448c2ecf20Sopenharmony_ci		dc_offset_low, dc_offset_hi);
2458c2ecf20Sopenharmony_ci	dprintk("Signal lvl over threshold: %d\n", signal_lv_over_threshold);
2468c2ecf20Sopenharmony_ci	dprintk("VCO: %d, VCO Sub-band: %d, ADC: %d\n", vco, vco_sub_band, adc);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int max2165_set_params(struct dvb_frontend *fe)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct max2165_priv *priv = fe->tuner_priv;
2528c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
2538c2ecf20Sopenharmony_ci	int ret;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	switch (c->bandwidth_hz) {
2568c2ecf20Sopenharmony_ci	case 7000000:
2578c2ecf20Sopenharmony_ci	case 8000000:
2588c2ecf20Sopenharmony_ci		priv->frequency = c->frequency;
2598c2ecf20Sopenharmony_ci		break;
2608c2ecf20Sopenharmony_ci	default:
2618c2ecf20Sopenharmony_ci		printk(KERN_INFO "MAX2165: bandwidth %d Hz not supported.\n",
2628c2ecf20Sopenharmony_ci		       c->bandwidth_hz);
2638c2ecf20Sopenharmony_ci		return -EINVAL;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	dprintk("%s() frequency=%d\n", __func__, c->frequency);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
2698c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1);
2708c2ecf20Sopenharmony_ci	max2165_set_bandwidth(priv, c->bandwidth_hz);
2718c2ecf20Sopenharmony_ci	ret = max2165_set_rf(priv, priv->frequency);
2728c2ecf20Sopenharmony_ci	mdelay(50);
2738c2ecf20Sopenharmony_ci	max2165_debug_status(priv);
2748c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
2758c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (ret != 0)
2788c2ecf20Sopenharmony_ci		return -EREMOTEIO;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return 0;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int max2165_get_frequency(struct dvb_frontend *fe, u32 *freq)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct max2165_priv *priv = fe->tuner_priv;
2868c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
2878c2ecf20Sopenharmony_ci	*freq = priv->frequency;
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int max2165_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct max2165_priv *priv = fe->tuner_priv;
2948c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	*bw = priv->bandwidth;
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int max2165_get_status(struct dvb_frontend *fe, u32 *status)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct max2165_priv *priv = fe->tuner_priv;
3038c2ecf20Sopenharmony_ci	u16 lock_status = 0;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
3088c2ecf20Sopenharmony_ci			fe->ops.i2c_gate_ctrl(fe, 1);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	max2165_debug_status(priv);
3118c2ecf20Sopenharmony_ci	*status = lock_status;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
3148c2ecf20Sopenharmony_ci			fe->ops.i2c_gate_ctrl(fe, 0);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return 0;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic int max2165_sleep(struct dvb_frontend *fe)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
3228c2ecf20Sopenharmony_ci	return 0;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic int max2165_init(struct dvb_frontend *fe)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct max2165_priv *priv = fe->tuner_priv;
3288c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
3318c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/* Setup initial values */
3348c2ecf20Sopenharmony_ci	/* Fractional Mode on */
3358c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_NDIV_FRAC2, 0x18);
3368c2ecf20Sopenharmony_ci	/* LNA on */
3378c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_LNA, 0x01);
3388c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_PLL_CFG, 0x7A);
3398c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_TEST, 0x08);
3408c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_SHUTDOWN, 0x40);
3418c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_VCO_CTRL, 0x84);
3428c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_BASEBAND_CTRL, 0xC3);
3438c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_DC_OFFSET_CTRL, 0x75);
3448c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_DC_OFFSET_DAC, 0x00);
3458c2ecf20Sopenharmony_ci	max2165_write_reg(priv, REG_ROM_TABLE_ADDR, 0x00);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	max2165_set_osc(priv, priv->config->osc_clk);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	max2165_read_rom_table(priv);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	max2165_set_bandwidth(priv, 8000000);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
3548c2ecf20Sopenharmony_ci			fe->ops.i2c_gate_ctrl(fe, 0);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return 0;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic void max2165_release(struct dvb_frontend *fe)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct max2165_priv *priv = fe->tuner_priv;
3628c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	kfree(priv);
3658c2ecf20Sopenharmony_ci	fe->tuner_priv = NULL;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops max2165_tuner_ops = {
3698c2ecf20Sopenharmony_ci	.info = {
3708c2ecf20Sopenharmony_ci		.name              = "Maxim MAX2165",
3718c2ecf20Sopenharmony_ci		.frequency_min_hz  = 470 * MHz,
3728c2ecf20Sopenharmony_ci		.frequency_max_hz  = 862 * MHz,
3738c2ecf20Sopenharmony_ci		.frequency_step_hz =  50 * kHz,
3748c2ecf20Sopenharmony_ci	},
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	.release	   = max2165_release,
3778c2ecf20Sopenharmony_ci	.init		   = max2165_init,
3788c2ecf20Sopenharmony_ci	.sleep		   = max2165_sleep,
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	.set_params	   = max2165_set_params,
3818c2ecf20Sopenharmony_ci	.set_analog_params = NULL,
3828c2ecf20Sopenharmony_ci	.get_frequency	   = max2165_get_frequency,
3838c2ecf20Sopenharmony_ci	.get_bandwidth	   = max2165_get_bandwidth,
3848c2ecf20Sopenharmony_ci	.get_status	   = max2165_get_status
3858c2ecf20Sopenharmony_ci};
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistruct dvb_frontend *max2165_attach(struct dvb_frontend *fe,
3888c2ecf20Sopenharmony_ci				   struct i2c_adapter *i2c,
3898c2ecf20Sopenharmony_ci				   struct max2165_config *cfg)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct max2165_priv *priv = NULL;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	dprintk("%s(%d-%04x)\n", __func__,
3948c2ecf20Sopenharmony_ci		i2c ? i2c_adapter_id(i2c) : -1,
3958c2ecf20Sopenharmony_ci		cfg ? cfg->i2c_address : -1);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(struct max2165_priv), GFP_KERNEL);
3988c2ecf20Sopenharmony_ci	if (priv == NULL)
3998c2ecf20Sopenharmony_ci		return NULL;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	memcpy(&fe->ops.tuner_ops, &max2165_tuner_ops,
4028c2ecf20Sopenharmony_ci		sizeof(struct dvb_tuner_ops));
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	priv->config = cfg;
4058c2ecf20Sopenharmony_ci	priv->i2c = i2c;
4068c2ecf20Sopenharmony_ci	fe->tuner_priv = priv;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	max2165_init(fe);
4098c2ecf20Sopenharmony_ci	max2165_debug_status(priv);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return fe;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(max2165_attach);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ciMODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
4168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver");
4178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
418