18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for Zarlink zl10036 DVB-S silicon tuner
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Tino Reichardt
68c2ecf20Sopenharmony_ci * Copyright (C) 2007-2009 Matthias Schwarzott <zzam@gentoo.de>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci **
98c2ecf20Sopenharmony_ci * The data sheet for this tuner can be found at:
108c2ecf20Sopenharmony_ci *    http://www.mcmilk.de/projects/dvb-card/datasheets/ZL10036.pdf
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This one is working: (at my Avermedia DVB-S Pro)
138c2ecf20Sopenharmony_ci * - zl10036 (40pin, FTA)
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * A driver for zl10038 should be very similar.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/dvb/frontend.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/types.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "zl10036.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int zl10036_debug;
268c2ecf20Sopenharmony_ci#define dprintk(level, args...) \
278c2ecf20Sopenharmony_ci	do { if (zl10036_debug & level) printk(KERN_DEBUG "zl10036: " args); \
288c2ecf20Sopenharmony_ci	} while (0)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define deb_info(args...)  dprintk(0x01, args)
318c2ecf20Sopenharmony_ci#define deb_i2c(args...)  dprintk(0x02, args)
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistruct zl10036_state {
348c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c;
358c2ecf20Sopenharmony_ci	const struct zl10036_config *config;
368c2ecf20Sopenharmony_ci	u32 frequency;
378c2ecf20Sopenharmony_ci	u8 br, bf;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* This driver assumes the tuner is driven by a 10.111MHz Cristal */
428c2ecf20Sopenharmony_ci#define _XTAL 10111
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* Some of the possible dividers:
458c2ecf20Sopenharmony_ci *   64, (write 0x05 to reg), freq step size   158kHz
468c2ecf20Sopenharmony_ci *   10, (write 0x0a to reg), freq step size 1.011kHz (used here)
478c2ecf20Sopenharmony_ci *    5, (write 0x09 to reg), freq step size 2.022kHz
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define _RDIV 10
518c2ecf20Sopenharmony_ci#define _RDIV_REG 0x0a
528c2ecf20Sopenharmony_ci#define _FR   (_XTAL/_RDIV)
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define STATUS_POR 0x80 /* Power on Reset */
558c2ecf20Sopenharmony_ci#define STATUS_FL  0x40 /* Frequency & Phase Lock */
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* read/write for zl10036 and zl10038 */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int zl10036_read_status_reg(struct zl10036_state *state)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	u8 status;
628c2ecf20Sopenharmony_ci	struct i2c_msg msg[1] = {
638c2ecf20Sopenharmony_ci		{ .addr = state->config->tuner_address, .flags = I2C_M_RD,
648c2ecf20Sopenharmony_ci		  .buf = &status, .len = sizeof(status) },
658c2ecf20Sopenharmony_ci	};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (i2c_transfer(state->i2c, msg, 1) != 1) {
688c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: i2c read failed at addr=%02x\n",
698c2ecf20Sopenharmony_ci			__func__, state->config->tuner_address);
708c2ecf20Sopenharmony_ci		return -EIO;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	deb_i2c("R(status): %02x  [FL=%d]\n", status,
748c2ecf20Sopenharmony_ci		(status & STATUS_FL) ? 1 : 0);
758c2ecf20Sopenharmony_ci	if (status & STATUS_POR)
768c2ecf20Sopenharmony_ci		deb_info("%s: Power-On-Reset bit enabled - need to initialize the tuner\n",
778c2ecf20Sopenharmony_ci			 __func__);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return status;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int zl10036_write(struct zl10036_state *state, u8 buf[], u8 count)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct i2c_msg msg[1] = {
858c2ecf20Sopenharmony_ci		{ .addr = state->config->tuner_address, .flags = 0,
868c2ecf20Sopenharmony_ci		  .buf = buf, .len = count },
878c2ecf20Sopenharmony_ci	};
888c2ecf20Sopenharmony_ci	u8 reg = 0;
898c2ecf20Sopenharmony_ci	int ret;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (zl10036_debug & 0x02) {
928c2ecf20Sopenharmony_ci		/* every 8bit-value satisifes this!
938c2ecf20Sopenharmony_ci		 * so only check for debug log */
948c2ecf20Sopenharmony_ci		if ((buf[0] & 0x80) == 0x00)
958c2ecf20Sopenharmony_ci			reg = 2;
968c2ecf20Sopenharmony_ci		else if ((buf[0] & 0xc0) == 0x80)
978c2ecf20Sopenharmony_ci			reg = 4;
988c2ecf20Sopenharmony_ci		else if ((buf[0] & 0xf0) == 0xc0)
998c2ecf20Sopenharmony_ci			reg = 6;
1008c2ecf20Sopenharmony_ci		else if ((buf[0] & 0xf0) == 0xd0)
1018c2ecf20Sopenharmony_ci			reg = 8;
1028c2ecf20Sopenharmony_ci		else if ((buf[0] & 0xf0) == 0xe0)
1038c2ecf20Sopenharmony_ci			reg = 10;
1048c2ecf20Sopenharmony_ci		else if ((buf[0] & 0xf0) == 0xf0)
1058c2ecf20Sopenharmony_ci			reg = 12;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		deb_i2c("W(%d):", reg);
1088c2ecf20Sopenharmony_ci		{
1098c2ecf20Sopenharmony_ci			int i;
1108c2ecf20Sopenharmony_ci			for (i = 0; i < count; i++)
1118c2ecf20Sopenharmony_ci				printk(KERN_CONT " %02x", buf[i]);
1128c2ecf20Sopenharmony_ci			printk(KERN_CONT "\n");
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->i2c, msg, 1);
1178c2ecf20Sopenharmony_ci	if (ret != 1) {
1188c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: i2c error, ret=%d\n", __func__, ret);
1198c2ecf20Sopenharmony_ci		return -EIO;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void zl10036_release(struct dvb_frontend *fe)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct zl10036_state *state = fe->tuner_priv;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	fe->tuner_priv = NULL;
1308c2ecf20Sopenharmony_ci	kfree(state);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int zl10036_sleep(struct dvb_frontend *fe)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct zl10036_state *state = fe->tuner_priv;
1368c2ecf20Sopenharmony_ci	u8 buf[] = { 0xf0, 0x80 }; /* regs 12/13 */
1378c2ecf20Sopenharmony_ci	int ret;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	deb_info("%s\n", __func__);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
1428c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	ret = zl10036_write(state, buf, sizeof(buf));
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
1478c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return ret;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/*
1538c2ecf20Sopenharmony_ci * register map of the ZL10036/ZL10038
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci * reg[default] content
1568c2ecf20Sopenharmony_ci *  2[0x00]:   0 | N14 | N13 | N12 | N11 | N10 |  N9 |  N8
1578c2ecf20Sopenharmony_ci *  3[0x00]:  N7 |  N6 |  N5 |  N4 |  N3 |  N2 |  N1 |  N0
1588c2ecf20Sopenharmony_ci *  4[0x80]:   1 |   0 | RFG | BA1 | BA0 | BG1 | BG0 | LEN
1598c2ecf20Sopenharmony_ci *  5[0x00]:  P0 |  C1 |  C0 |  R4 |  R3 |  R2 |  R1 |  R0
1608c2ecf20Sopenharmony_ci *  6[0xc0]:   1 |   1 |   0 |   0 | RSD |   0 |   0 |   0
1618c2ecf20Sopenharmony_ci *  7[0x20]:  P1 | BF6 | BF5 | BF4 | BF3 | BF2 | BF1 |   0
1628c2ecf20Sopenharmony_ci *  8[0xdb]:   1 |   1 |   0 |   1 |   0 |  CC |   1 |   1
1638c2ecf20Sopenharmony_ci *  9[0x30]: VSD |  V2 |  V1 |  V0 |  S3 |  S2 |  S1 |  S0
1648c2ecf20Sopenharmony_ci * 10[0xe1]:   1 |   1 |   1 |   0 |   0 | LS2 | LS1 | LS0
1658c2ecf20Sopenharmony_ci * 11[0xf5]:  WS | WH2 | WH1 | WH0 | WL2 | WL1 | WL0 | WRE
1668c2ecf20Sopenharmony_ci * 12[0xf0]:   1 |   1 |   1 |   1 |   0 |   0 |   0 |   0
1678c2ecf20Sopenharmony_ci * 13[0x28]:  PD | BR4 | BR3 | BR2 | BR1 | BR0 | CLR |  TL
1688c2ecf20Sopenharmony_ci */
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int zl10036_set_frequency(struct zl10036_state *state, u32 frequency)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	u8 buf[2];
1738c2ecf20Sopenharmony_ci	u32 div, foffset;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	div = (frequency + _FR/2) / _FR;
1768c2ecf20Sopenharmony_ci	state->frequency = div * _FR;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	foffset = frequency - state->frequency;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	buf[0] = (div >> 8) & 0x7f;
1818c2ecf20Sopenharmony_ci	buf[1] = (div >> 0) & 0xff;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	deb_info("%s: ftodo=%u fpriv=%u ferr=%d div=%u\n", __func__,
1848c2ecf20Sopenharmony_ci		frequency, state->frequency, foffset, div);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return zl10036_write(state, buf, sizeof(buf));
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int zl10036_set_bandwidth(struct zl10036_state *state, u32 fbw)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	/* fbw is measured in kHz */
1928c2ecf20Sopenharmony_ci	u8 br, bf;
1938c2ecf20Sopenharmony_ci	int ret;
1948c2ecf20Sopenharmony_ci	u8 buf_bf[] = {
1958c2ecf20Sopenharmony_ci		0xc0, 0x00, /*   6/7: rsd=0 bf=0 */
1968c2ecf20Sopenharmony_ci	};
1978c2ecf20Sopenharmony_ci	u8 buf_br[] = {
1988c2ecf20Sopenharmony_ci		0xf0, 0x00, /* 12/13: br=0xa clr=0 tl=0*/
1998c2ecf20Sopenharmony_ci	};
2008c2ecf20Sopenharmony_ci	u8 zl10036_rsd_off[] = { 0xc8 }; /* set RSD=1 */
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* ensure correct values */
2038c2ecf20Sopenharmony_ci	if (fbw > 35000)
2048c2ecf20Sopenharmony_ci		fbw = 35000;
2058c2ecf20Sopenharmony_ci	if (fbw <  8000)
2068c2ecf20Sopenharmony_ci		fbw =  8000;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci#define _BR_MAXIMUM (_XTAL/575) /* _XTAL / 575kHz = 17 */
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* <= 28,82 MHz */
2118c2ecf20Sopenharmony_ci	if (fbw <= 28820) {
2128c2ecf20Sopenharmony_ci		br = _BR_MAXIMUM;
2138c2ecf20Sopenharmony_ci	} else {
2148c2ecf20Sopenharmony_ci		/*
2158c2ecf20Sopenharmony_ci		 *  f(bw)=34,6MHz f(xtal)=10.111MHz
2168c2ecf20Sopenharmony_ci		 *  br = (10111/34600) * 63 * 1/K = 14;
2178c2ecf20Sopenharmony_ci		 */
2188c2ecf20Sopenharmony_ci		br = ((_XTAL * 21 * 1000) / (fbw * 419));
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* ensure correct values */
2228c2ecf20Sopenharmony_ci	if (br < 4)
2238c2ecf20Sopenharmony_ci		br = 4;
2248c2ecf20Sopenharmony_ci	if (br > _BR_MAXIMUM)
2258c2ecf20Sopenharmony_ci		br = _BR_MAXIMUM;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/*
2288c2ecf20Sopenharmony_ci	 * k = 1.257
2298c2ecf20Sopenharmony_ci	 * bf = fbw/_XTAL * br * k - 1 */
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	bf = (fbw * br * 1257) / (_XTAL * 1000) - 1;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* ensure correct values */
2348c2ecf20Sopenharmony_ci	if (bf > 62)
2358c2ecf20Sopenharmony_ci		bf = 62;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	buf_bf[1] = (bf << 1) & 0x7e;
2388c2ecf20Sopenharmony_ci	buf_br[1] = (br << 2) & 0x7c;
2398c2ecf20Sopenharmony_ci	deb_info("%s: BW=%d br=%u bf=%u\n", __func__, fbw, br, bf);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (br != state->br) {
2428c2ecf20Sopenharmony_ci		ret = zl10036_write(state, buf_br, sizeof(buf_br));
2438c2ecf20Sopenharmony_ci		if (ret < 0)
2448c2ecf20Sopenharmony_ci			return ret;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (bf != state->bf) {
2488c2ecf20Sopenharmony_ci		ret = zl10036_write(state, buf_bf, sizeof(buf_bf));
2498c2ecf20Sopenharmony_ci		if (ret < 0)
2508c2ecf20Sopenharmony_ci			return ret;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		/* time = br/(32* fxtal) */
2538c2ecf20Sopenharmony_ci		/* minimal sleep time to be calculated
2548c2ecf20Sopenharmony_ci		 * maximum br is 63 -> max time = 2 /10 MHz = 2e-7 */
2558c2ecf20Sopenharmony_ci		msleep(1);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		ret = zl10036_write(state, zl10036_rsd_off,
2588c2ecf20Sopenharmony_ci			sizeof(zl10036_rsd_off));
2598c2ecf20Sopenharmony_ci		if (ret < 0)
2608c2ecf20Sopenharmony_ci			return ret;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	state->br = br;
2648c2ecf20Sopenharmony_ci	state->bf = bf;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return 0;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int zl10036_set_gain_params(struct zl10036_state *state,
2708c2ecf20Sopenharmony_ci	int c)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	u8 buf[2];
2738c2ecf20Sopenharmony_ci	u8 rfg, ba, bg;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* default values */
2768c2ecf20Sopenharmony_ci	rfg = 0; /* enable when using an lna */
2778c2ecf20Sopenharmony_ci	ba = 1;
2788c2ecf20Sopenharmony_ci	bg = 1;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* reg 4 */
2818c2ecf20Sopenharmony_ci	buf[0] = 0x80 | ((rfg << 5) & 0x20)
2828c2ecf20Sopenharmony_ci		| ((ba  << 3) & 0x18) | ((bg  << 1) & 0x06);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (!state->config->rf_loop_enable)
2858c2ecf20Sopenharmony_ci		buf[0] |= 0x01;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* P0=0 */
2888c2ecf20Sopenharmony_ci	buf[1] = _RDIV_REG | ((c << 5) & 0x60);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	deb_info("%s: c=%u rfg=%u ba=%u bg=%u\n", __func__, c, rfg, ba, bg);
2918c2ecf20Sopenharmony_ci	return zl10036_write(state, buf, sizeof(buf));
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int zl10036_set_params(struct dvb_frontend *fe)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
2978c2ecf20Sopenharmony_ci	struct zl10036_state *state = fe->tuner_priv;
2988c2ecf20Sopenharmony_ci	int ret = 0;
2998c2ecf20Sopenharmony_ci	u32 frequency = p->frequency;
3008c2ecf20Sopenharmony_ci	u32 fbw;
3018c2ecf20Sopenharmony_ci	int i;
3028c2ecf20Sopenharmony_ci	u8 c;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* ensure correct values
3058c2ecf20Sopenharmony_ci	 * maybe redundant as core already checks this */
3068c2ecf20Sopenharmony_ci	if ((frequency < fe->ops.info.frequency_min_hz / kHz)
3078c2ecf20Sopenharmony_ci	||  (frequency > fe->ops.info.frequency_max_hz / kHz))
3088c2ecf20Sopenharmony_ci		return -EINVAL;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/*
3118c2ecf20Sopenharmony_ci	 * alpha = 1.35 for dvb-s
3128c2ecf20Sopenharmony_ci	 * fBW = (alpha*symbolrate)/(2*0.8)
3138c2ecf20Sopenharmony_ci	 * 1.35 / (2*0.8) = 27 / 32
3148c2ecf20Sopenharmony_ci	 */
3158c2ecf20Sopenharmony_ci	fbw = (27 * p->symbol_rate) / 32;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* scale to kHz */
3188c2ecf20Sopenharmony_ci	fbw /= 1000;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* Add safe margin of 3MHz */
3218c2ecf20Sopenharmony_ci	fbw += 3000;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* setting the charge pump - guessed values */
3248c2ecf20Sopenharmony_ci	if (frequency < 950000)
3258c2ecf20Sopenharmony_ci		return -EINVAL;
3268c2ecf20Sopenharmony_ci	else if (frequency < 1250000)
3278c2ecf20Sopenharmony_ci		c = 0;
3288c2ecf20Sopenharmony_ci	else if (frequency < 1750000)
3298c2ecf20Sopenharmony_ci		c = 1;
3308c2ecf20Sopenharmony_ci	else if (frequency < 2175000)
3318c2ecf20Sopenharmony_ci		c = 2;
3328c2ecf20Sopenharmony_ci	else
3338c2ecf20Sopenharmony_ci		return -EINVAL;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
3368c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	ret = zl10036_set_gain_params(state, c);
3398c2ecf20Sopenharmony_ci	if (ret < 0)
3408c2ecf20Sopenharmony_ci		goto error;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	ret = zl10036_set_frequency(state, p->frequency);
3438c2ecf20Sopenharmony_ci	if (ret < 0)
3448c2ecf20Sopenharmony_ci		goto error;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	ret = zl10036_set_bandwidth(state, fbw);
3478c2ecf20Sopenharmony_ci	if (ret < 0)
3488c2ecf20Sopenharmony_ci		goto error;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	/* wait for tuner lock - no idea if this is really needed */
3518c2ecf20Sopenharmony_ci	for (i = 0; i < 20; i++) {
3528c2ecf20Sopenharmony_ci		ret = zl10036_read_status_reg(state);
3538c2ecf20Sopenharmony_ci		if (ret < 0)
3548c2ecf20Sopenharmony_ci			goto error;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		/* check Frequency & Phase Lock Bit */
3578c2ecf20Sopenharmony_ci		if (ret & STATUS_FL)
3588c2ecf20Sopenharmony_ci			break;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci		msleep(10);
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cierror:
3648c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
3658c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return ret;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic int zl10036_get_frequency(struct dvb_frontend *fe, u32 *frequency)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct zl10036_state *state = fe->tuner_priv;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	*frequency = state->frequency;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	return 0;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int zl10036_init_regs(struct zl10036_state *state)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	int ret;
3828c2ecf20Sopenharmony_ci	int i;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* could also be one block from reg 2 to 13 and additional 10/11 */
3858c2ecf20Sopenharmony_ci	u8 zl10036_init_tab[][2] = {
3868c2ecf20Sopenharmony_ci		{ 0x04, 0x00 },		/*   2/3: div=0x400 - arbitrary value */
3878c2ecf20Sopenharmony_ci		{ 0x8b, _RDIV_REG },	/*   4/5: rfg=0 ba=1 bg=1 len=? */
3888c2ecf20Sopenharmony_ci					/*        p0=0 c=0 r=_RDIV_REG */
3898c2ecf20Sopenharmony_ci		{ 0xc0, 0x20 },		/*   6/7: rsd=0 bf=0x10 */
3908c2ecf20Sopenharmony_ci		{ 0xd3, 0x40 },		/*   8/9: from datasheet */
3918c2ecf20Sopenharmony_ci		{ 0xe3, 0x5b },		/* 10/11: lock window level */
3928c2ecf20Sopenharmony_ci		{ 0xf0, 0x28 },		/* 12/13: br=0xa clr=0 tl=0*/
3938c2ecf20Sopenharmony_ci		{ 0xe3, 0xf9 },		/* 10/11: unlock window level */
3948c2ecf20Sopenharmony_ci	};
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* invalid values to trigger writing */
3978c2ecf20Sopenharmony_ci	state->br = 0xff;
3988c2ecf20Sopenharmony_ci	state->bf = 0xff;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (!state->config->rf_loop_enable)
4018c2ecf20Sopenharmony_ci		zl10036_init_tab[1][0] |= 0x01;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	deb_info("%s\n", __func__);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(zl10036_init_tab); i++) {
4068c2ecf20Sopenharmony_ci		ret = zl10036_write(state, zl10036_init_tab[i], 2);
4078c2ecf20Sopenharmony_ci		if (ret < 0)
4088c2ecf20Sopenharmony_ci			return ret;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return 0;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic int zl10036_init(struct dvb_frontend *fe)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct zl10036_state *state = fe->tuner_priv;
4178c2ecf20Sopenharmony_ci	int ret = 0;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
4208c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	ret = zl10036_read_status_reg(state);
4238c2ecf20Sopenharmony_ci	if (ret < 0)
4248c2ecf20Sopenharmony_ci		return ret;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/* Only init if Power-on-Reset bit is set? */
4278c2ecf20Sopenharmony_ci	ret = zl10036_init_regs(state);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
4308c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return ret;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops zl10036_tuner_ops = {
4368c2ecf20Sopenharmony_ci	.info = {
4378c2ecf20Sopenharmony_ci		.name = "Zarlink ZL10036",
4388c2ecf20Sopenharmony_ci		.frequency_min_hz =  950 * MHz,
4398c2ecf20Sopenharmony_ci		.frequency_max_hz = 2175 * MHz
4408c2ecf20Sopenharmony_ci	},
4418c2ecf20Sopenharmony_ci	.init = zl10036_init,
4428c2ecf20Sopenharmony_ci	.release = zl10036_release,
4438c2ecf20Sopenharmony_ci	.sleep = zl10036_sleep,
4448c2ecf20Sopenharmony_ci	.set_params = zl10036_set_params,
4458c2ecf20Sopenharmony_ci	.get_frequency = zl10036_get_frequency,
4468c2ecf20Sopenharmony_ci};
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistruct dvb_frontend *zl10036_attach(struct dvb_frontend *fe,
4498c2ecf20Sopenharmony_ci				    const struct zl10036_config *config,
4508c2ecf20Sopenharmony_ci				    struct i2c_adapter *i2c)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct zl10036_state *state;
4538c2ecf20Sopenharmony_ci	int ret;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (!config) {
4568c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: no config specified", __func__);
4578c2ecf20Sopenharmony_ci		return NULL;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct zl10036_state), GFP_KERNEL);
4618c2ecf20Sopenharmony_ci	if (!state)
4628c2ecf20Sopenharmony_ci		return NULL;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	state->config = config;
4658c2ecf20Sopenharmony_ci	state->i2c = i2c;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
4688c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	ret = zl10036_read_status_reg(state);
4718c2ecf20Sopenharmony_ci	if (ret < 0) {
4728c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: No zl10036 found\n", __func__);
4738c2ecf20Sopenharmony_ci		goto error;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	ret = zl10036_init_regs(state);
4778c2ecf20Sopenharmony_ci	if (ret < 0) {
4788c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: tuner initialization failed\n",
4798c2ecf20Sopenharmony_ci			__func__);
4808c2ecf20Sopenharmony_ci		goto error;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (fe->ops.i2c_gate_ctrl)
4848c2ecf20Sopenharmony_ci		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	fe->tuner_priv = state;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	memcpy(&fe->ops.tuner_ops, &zl10036_tuner_ops,
4898c2ecf20Sopenharmony_ci		sizeof(struct dvb_tuner_ops));
4908c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: tuner initialization (%s addr=0x%02x) ok\n",
4918c2ecf20Sopenharmony_ci		__func__, fe->ops.tuner_ops.info.name, config->tuner_address);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	return fe;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cierror:
4968c2ecf20Sopenharmony_ci	kfree(state);
4978c2ecf20Sopenharmony_ci	return NULL;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(zl10036_attach);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cimodule_param_named(debug, zl10036_debug, int, 0644);
5028c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
5038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DVB ZL10036 driver");
5048c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tino Reichardt");
5058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matthias Schwarzott");
5068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
507