1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Infineon TUA9001 silicon tuner driver |
4 | * |
5 | * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> |
6 | */ |
7 | |
8 | #include "tua9001_priv.h" |
9 | |
10 | static int tua9001_init(struct dvb_frontend *fe) |
11 | { |
12 | struct tua9001_dev *dev = fe->tuner_priv; |
13 | struct i2c_client *client = dev->client; |
14 | int ret, i; |
15 | static const struct tua9001_reg_val data[] = { |
16 | {0x1e, 0x6512}, |
17 | {0x25, 0xb888}, |
18 | {0x39, 0x5460}, |
19 | {0x3b, 0x00c0}, |
20 | {0x3a, 0xf000}, |
21 | {0x08, 0x0000}, |
22 | {0x32, 0x0030}, |
23 | {0x41, 0x703a}, |
24 | {0x40, 0x1c78}, |
25 | {0x2c, 0x1c00}, |
26 | {0x36, 0xc013}, |
27 | {0x37, 0x6f18}, |
28 | {0x27, 0x0008}, |
29 | {0x2a, 0x0001}, |
30 | {0x34, 0x0a40}, |
31 | }; |
32 | |
33 | dev_dbg(&client->dev, "\n" ); |
34 | |
35 | if (fe->callback) { |
36 | ret = fe->callback(client->adapter, |
37 | DVB_FRONTEND_COMPONENT_TUNER, |
38 | TUA9001_CMD_RESETN, 0); |
39 | if (ret) |
40 | goto err; |
41 | } |
42 | |
43 | for (i = 0; i < ARRAY_SIZE(data); i++) { |
44 | ret = regmap_write(map: dev->regmap, reg: data[i].reg, val: data[i].val); |
45 | if (ret) |
46 | goto err; |
47 | } |
48 | return 0; |
49 | err: |
50 | dev_dbg(&client->dev, "failed=%d\n" , ret); |
51 | return ret; |
52 | } |
53 | |
54 | static int tua9001_sleep(struct dvb_frontend *fe) |
55 | { |
56 | struct tua9001_dev *dev = fe->tuner_priv; |
57 | struct i2c_client *client = dev->client; |
58 | int ret; |
59 | |
60 | dev_dbg(&client->dev, "\n" ); |
61 | |
62 | if (fe->callback) { |
63 | ret = fe->callback(client->adapter, |
64 | DVB_FRONTEND_COMPONENT_TUNER, |
65 | TUA9001_CMD_RESETN, 1); |
66 | if (ret) |
67 | goto err; |
68 | } |
69 | return 0; |
70 | err: |
71 | dev_dbg(&client->dev, "failed=%d\n" , ret); |
72 | return ret; |
73 | } |
74 | |
75 | static int tua9001_set_params(struct dvb_frontend *fe) |
76 | { |
77 | struct tua9001_dev *dev = fe->tuner_priv; |
78 | struct i2c_client *client = dev->client; |
79 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
80 | int ret, i; |
81 | u16 val; |
82 | struct tua9001_reg_val data[2]; |
83 | |
84 | dev_dbg(&client->dev, |
85 | "delivery_system=%u frequency=%u bandwidth_hz=%u\n" , |
86 | c->delivery_system, c->frequency, c->bandwidth_hz); |
87 | |
88 | switch (c->delivery_system) { |
89 | case SYS_DVBT: |
90 | switch (c->bandwidth_hz) { |
91 | case 8000000: |
92 | val = 0x0000; |
93 | break; |
94 | case 7000000: |
95 | val = 0x1000; |
96 | break; |
97 | case 6000000: |
98 | val = 0x2000; |
99 | break; |
100 | case 5000000: |
101 | val = 0x3000; |
102 | break; |
103 | default: |
104 | ret = -EINVAL; |
105 | goto err; |
106 | } |
107 | break; |
108 | default: |
109 | ret = -EINVAL; |
110 | goto err; |
111 | } |
112 | |
113 | data[0].reg = 0x04; |
114 | data[0].val = val; |
115 | data[1].reg = 0x1f; |
116 | data[1].val = div_u64(dividend: (u64) (c->frequency - 150000000) * 48, divisor: 1000000); |
117 | |
118 | if (fe->callback) { |
119 | ret = fe->callback(client->adapter, |
120 | DVB_FRONTEND_COMPONENT_TUNER, |
121 | TUA9001_CMD_RXEN, 0); |
122 | if (ret) |
123 | goto err; |
124 | } |
125 | |
126 | for (i = 0; i < ARRAY_SIZE(data); i++) { |
127 | ret = regmap_write(map: dev->regmap, reg: data[i].reg, val: data[i].val); |
128 | if (ret) |
129 | goto err; |
130 | } |
131 | |
132 | if (fe->callback) { |
133 | ret = fe->callback(client->adapter, |
134 | DVB_FRONTEND_COMPONENT_TUNER, |
135 | TUA9001_CMD_RXEN, 1); |
136 | if (ret) |
137 | goto err; |
138 | } |
139 | return 0; |
140 | err: |
141 | dev_dbg(&client->dev, "failed=%d\n" , ret); |
142 | return ret; |
143 | } |
144 | |
145 | static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) |
146 | { |
147 | struct tua9001_dev *dev = fe->tuner_priv; |
148 | struct i2c_client *client = dev->client; |
149 | |
150 | dev_dbg(&client->dev, "\n" ); |
151 | |
152 | *frequency = 0; /* Zero-IF */ |
153 | return 0; |
154 | } |
155 | |
156 | static const struct dvb_tuner_ops tua9001_tuner_ops = { |
157 | .info = { |
158 | .name = "Infineon TUA9001" , |
159 | .frequency_min_hz = 170 * MHz, |
160 | .frequency_max_hz = 862 * MHz, |
161 | }, |
162 | |
163 | .init = tua9001_init, |
164 | .sleep = tua9001_sleep, |
165 | .set_params = tua9001_set_params, |
166 | |
167 | .get_if_frequency = tua9001_get_if_frequency, |
168 | }; |
169 | |
170 | static int tua9001_probe(struct i2c_client *client) |
171 | { |
172 | struct tua9001_dev *dev; |
173 | struct tua9001_platform_data *pdata = client->dev.platform_data; |
174 | struct dvb_frontend *fe = pdata->dvb_frontend; |
175 | int ret; |
176 | static const struct regmap_config regmap_config = { |
177 | .reg_bits = 8, |
178 | .val_bits = 16, |
179 | }; |
180 | |
181 | dev = kzalloc(size: sizeof(*dev), GFP_KERNEL); |
182 | if (!dev) { |
183 | ret = -ENOMEM; |
184 | goto err; |
185 | } |
186 | |
187 | dev->fe = pdata->dvb_frontend; |
188 | dev->client = client; |
189 | dev->regmap = devm_regmap_init_i2c(client, ®map_config); |
190 | if (IS_ERR(ptr: dev->regmap)) { |
191 | ret = PTR_ERR(ptr: dev->regmap); |
192 | goto err_kfree; |
193 | } |
194 | |
195 | if (fe->callback) { |
196 | ret = fe->callback(client->adapter, |
197 | DVB_FRONTEND_COMPONENT_TUNER, |
198 | TUA9001_CMD_CEN, 1); |
199 | if (ret) |
200 | goto err_kfree; |
201 | |
202 | ret = fe->callback(client->adapter, |
203 | DVB_FRONTEND_COMPONENT_TUNER, |
204 | TUA9001_CMD_RXEN, 0); |
205 | if (ret) |
206 | goto err_kfree; |
207 | |
208 | ret = fe->callback(client->adapter, |
209 | DVB_FRONTEND_COMPONENT_TUNER, |
210 | TUA9001_CMD_RESETN, 1); |
211 | if (ret) |
212 | goto err_kfree; |
213 | } |
214 | |
215 | fe->tuner_priv = dev; |
216 | memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops, |
217 | sizeof(struct dvb_tuner_ops)); |
218 | i2c_set_clientdata(client, data: dev); |
219 | |
220 | dev_info(&client->dev, "Infineon TUA9001 successfully attached\n" ); |
221 | return 0; |
222 | err_kfree: |
223 | kfree(objp: dev); |
224 | err: |
225 | dev_dbg(&client->dev, "failed=%d\n" , ret); |
226 | return ret; |
227 | } |
228 | |
229 | static void tua9001_remove(struct i2c_client *client) |
230 | { |
231 | struct tua9001_dev *dev = i2c_get_clientdata(client); |
232 | struct dvb_frontend *fe = dev->fe; |
233 | int ret; |
234 | |
235 | dev_dbg(&client->dev, "\n" ); |
236 | |
237 | if (fe->callback) { |
238 | ret = fe->callback(client->adapter, |
239 | DVB_FRONTEND_COMPONENT_TUNER, |
240 | TUA9001_CMD_CEN, 0); |
241 | if (ret) |
242 | dev_err(&client->dev, "Tuner disable failed (%pe)\n" , ERR_PTR(ret)); |
243 | } |
244 | kfree(objp: dev); |
245 | } |
246 | |
247 | static const struct i2c_device_id tua9001_id_table[] = { |
248 | {"tua9001" , 0}, |
249 | {} |
250 | }; |
251 | MODULE_DEVICE_TABLE(i2c, tua9001_id_table); |
252 | |
253 | static struct i2c_driver tua9001_driver = { |
254 | .driver = { |
255 | .name = "tua9001" , |
256 | .suppress_bind_attrs = true, |
257 | }, |
258 | .probe = tua9001_probe, |
259 | .remove = tua9001_remove, |
260 | .id_table = tua9001_id_table, |
261 | }; |
262 | |
263 | module_i2c_driver(tua9001_driver); |
264 | |
265 | MODULE_DESCRIPTION("Infineon TUA9001 silicon tuner driver" ); |
266 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>" ); |
267 | MODULE_LICENSE("GPL" ); |
268 | |