diff options
author | Eduardo Valentin <eduardo.valentin@nokia.com> | 2009-08-08 07:46:53 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-09-12 11:19:20 -0400 |
commit | 02bee89e79b1302776e32214b8ca96a00c70c446 (patch) | |
tree | de3ee880d3cb3bf2e3e98d9af4152338bf6d3061 /drivers/media/radio | |
parent | 1fd2121c08eeef2e9a792719628a467e0fe97b96 (diff) |
V4L/DVB (12552): FM TX: si4713: Add files to handle si4713 i2c device
This patch adds files to control si4713 devices.
Internal functions to control device properties
and initialization procedures are into these files.
Also, a v4l2 subdev interface is also exported.
This way other drivers can use this as v4l2 i2c subdevice.
Signed-off-by: Eduardo Valentin <eduardo.valentin@nokia.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/si4713-i2c.c | 2067 | ||||
-rw-r--r-- | drivers/media/radio/si4713-i2c.h | 237 |
2 files changed, 2304 insertions, 0 deletions
diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c new file mode 100644 index 000000000000..8cbbe48b01bd --- /dev/null +++ b/drivers/media/radio/si4713-i2c.c | |||
@@ -0,0 +1,2067 @@ | |||
1 | /* | ||
2 | * drivers/media/radio/si4713-i2c.c | ||
3 | * | ||
4 | * Silicon Labs Si4713 FM Radio Transmitter I2C commands. | ||
5 | * | ||
6 | * Copyright (c) 2009 Nokia Corporation | ||
7 | * Contact: Eduardo Valentin <eduardo.valentin@nokia.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | #include <linux/mutex.h> | ||
25 | #include <linux/completion.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/i2c.h> | ||
29 | #include <media/v4l2-device.h> | ||
30 | #include <media/v4l2-ioctl.h> | ||
31 | #include <media/v4l2-common.h> | ||
32 | |||
33 | #include "si4713-i2c.h" | ||
34 | |||
35 | /* module parameters */ | ||
36 | static int debug; | ||
37 | module_param(debug, int, S_IRUGO | S_IWUSR); | ||
38 | MODULE_PARM_DESC(debug, "Debug level (0 - 2)"); | ||
39 | |||
40 | MODULE_LICENSE("GPL"); | ||
41 | MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>"); | ||
42 | MODULE_DESCRIPTION("I2C driver for Si4713 FM Radio Transmitter"); | ||
43 | MODULE_VERSION("0.0.1"); | ||
44 | |||
45 | #define DEFAULT_RDS_PI 0x00 | ||
46 | #define DEFAULT_RDS_PTY 0x00 | ||
47 | #define DEFAULT_RDS_PS_NAME "" | ||
48 | #define DEFAULT_RDS_RADIO_TEXT DEFAULT_RDS_PS_NAME | ||
49 | #define DEFAULT_RDS_DEVIATION 0x00C8 | ||
50 | #define DEFAULT_RDS_PS_REPEAT_COUNT 0x0003 | ||
51 | #define DEFAULT_LIMITER_RTIME 0x1392 | ||
52 | #define DEFAULT_LIMITER_DEV 0x102CA | ||
53 | #define DEFAULT_PILOT_FREQUENCY 0x4A38 | ||
54 | #define DEFAULT_PILOT_DEVIATION 0x1A5E | ||
55 | #define DEFAULT_ACOMP_ATIME 0x0000 | ||
56 | #define DEFAULT_ACOMP_RTIME 0xF4240L | ||
57 | #define DEFAULT_ACOMP_GAIN 0x0F | ||
58 | #define DEFAULT_ACOMP_THRESHOLD (-0x28) | ||
59 | #define DEFAULT_MUTE 0x01 | ||
60 | #define DEFAULT_POWER_LEVEL 88 | ||
61 | #define DEFAULT_FREQUENCY 8800 | ||
62 | #define DEFAULT_PREEMPHASIS FMPE_EU | ||
63 | #define DEFAULT_TUNE_RNL 0xFF | ||
64 | |||
65 | #define to_si4713_device(sd) container_of(sd, struct si4713_device, sd) | ||
66 | |||
67 | /* frequency domain transformation (using times 10 to avoid floats) */ | ||
68 | #define FREQDEV_UNIT 100000 | ||
69 | #define FREQV4L2_MULTI 625 | ||
70 | #define si4713_to_v4l2(f) ((f * FREQDEV_UNIT) / FREQV4L2_MULTI) | ||
71 | #define v4l2_to_si4713(f) ((f * FREQV4L2_MULTI) / FREQDEV_UNIT) | ||
72 | #define FREQ_RANGE_LOW 7600 | ||
73 | #define FREQ_RANGE_HIGH 10800 | ||
74 | |||
75 | #define MAX_ARGS 7 | ||
76 | |||
77 | #define RDS_BLOCK 8 | ||
78 | #define RDS_BLOCK_CLEAR 0x03 | ||
79 | #define RDS_BLOCK_LOAD 0x04 | ||
80 | #define RDS_RADIOTEXT_2A 0x20 | ||
81 | #define RDS_RADIOTEXT_BLK_SIZE 4 | ||
82 | #define RDS_RADIOTEXT_INDEX_MAX 0x0F | ||
83 | #define RDS_CARRIAGE_RETURN 0x0D | ||
84 | |||
85 | #define rds_ps_nblocks(len) ((len / RDS_BLOCK) + (len % RDS_BLOCK ? 1 : 0)) | ||
86 | |||
87 | #define get_status_bit(p, b, m) (((p) & (m)) >> (b)) | ||
88 | #define set_bits(p, v, b, m) (((p) & ~(m)) | ((v) << (b))) | ||
89 | |||
90 | #define ATTACK_TIME_UNIT 500 | ||
91 | |||
92 | #define POWER_OFF 0x00 | ||
93 | #define POWER_ON 0x01 | ||
94 | |||
95 | #define msb(x) ((u8)((u16) x >> 8)) | ||
96 | #define lsb(x) ((u8)((u16) x & 0x00FF)) | ||
97 | #define compose_u16(msb, lsb) (((u16)msb << 8) | lsb) | ||
98 | #define check_command_failed(status) (!(status & SI4713_CTS) || \ | ||
99 | (status & SI4713_ERR)) | ||
100 | /* mute definition */ | ||
101 | #define set_mute(p) ((p & 1) | ((p & 1) << 1)); | ||
102 | #define get_mute(p) (p & 0x01) | ||
103 | |||
104 | #ifdef DEBUG | ||
105 | #define DBG_BUFFER(device, message, buffer, size) \ | ||
106 | { \ | ||
107 | int i; \ | ||
108 | char str[(size)*5]; \ | ||
109 | for (i = 0; i < size; i++) \ | ||
110 | sprintf(str + i * 5, " 0x%02x", buffer[i]); \ | ||
111 | v4l2_dbg(2, debug, device, "%s:%s\n", message, str); \ | ||
112 | } | ||
113 | #else | ||
114 | #define DBG_BUFFER(device, message, buffer, size) | ||
115 | #endif | ||
116 | |||
117 | /* | ||
118 | * Values for limiter release time (sorted by second column) | ||
119 | * device release | ||
120 | * value time (us) | ||
121 | */ | ||
122 | static long limiter_times[] = { | ||
123 | 2000, 250, | ||
124 | 1000, 500, | ||
125 | 510, 1000, | ||
126 | 255, 2000, | ||
127 | 170, 3000, | ||
128 | 127, 4020, | ||
129 | 102, 5010, | ||
130 | 85, 6020, | ||
131 | 73, 7010, | ||
132 | 64, 7990, | ||
133 | 57, 8970, | ||
134 | 51, 10030, | ||
135 | 25, 20470, | ||
136 | 17, 30110, | ||
137 | 13, 39380, | ||
138 | 10, 51190, | ||
139 | 8, 63690, | ||
140 | 7, 73140, | ||
141 | 6, 85330, | ||
142 | 5, 102390, | ||
143 | }; | ||
144 | |||
145 | /* | ||
146 | * Values for audio compression release time (sorted by second column) | ||
147 | * device release | ||
148 | * value time (us) | ||
149 | */ | ||
150 | static unsigned long acomp_rtimes[] = { | ||
151 | 0, 100000, | ||
152 | 1, 200000, | ||
153 | 2, 350000, | ||
154 | 3, 525000, | ||
155 | 4, 1000000, | ||
156 | }; | ||
157 | |||
158 | /* | ||
159 | * Values for preemphasis (sorted by second column) | ||
160 | * device preemphasis | ||
161 | * value value (v4l2) | ||
162 | */ | ||
163 | static unsigned long preemphasis_values[] = { | ||
164 | FMPE_DISABLED, V4L2_PREEMPHASIS_DISABLED, | ||
165 | FMPE_EU, V4L2_PREEMPHASIS_50_uS, | ||
166 | FMPE_USA, V4L2_PREEMPHASIS_75_uS, | ||
167 | }; | ||
168 | |||
169 | static int usecs_to_dev(unsigned long usecs, unsigned long const array[], | ||
170 | int size) | ||
171 | { | ||
172 | int i; | ||
173 | int rval = -EINVAL; | ||
174 | |||
175 | for (i = 0; i < size / 2; i++) | ||
176 | if (array[(i * 2) + 1] >= usecs) { | ||
177 | rval = array[i * 2]; | ||
178 | break; | ||
179 | } | ||
180 | |||
181 | return rval; | ||
182 | } | ||
183 | |||
184 | static unsigned long dev_to_usecs(int value, unsigned long const array[], | ||
185 | int size) | ||
186 | { | ||
187 | int i; | ||
188 | int rval = -EINVAL; | ||
189 | |||
190 | for (i = 0; i < size / 2; i++) | ||
191 | if (array[i * 2] == value) { | ||
192 | rval = array[(i * 2) + 1]; | ||
193 | break; | ||
194 | } | ||
195 | |||
196 | return rval; | ||
197 | } | ||
198 | |||
199 | /* si4713_handler: IRQ handler, just complete work */ | ||
200 | static irqreturn_t si4713_handler(int irq, void *dev) | ||
201 | { | ||
202 | struct si4713_device *sdev = dev; | ||
203 | |||
204 | v4l2_dbg(2, debug, &sdev->sd, | ||
205 | "%s: sending signal to completion work.\n", __func__); | ||
206 | complete(&sdev->work); | ||
207 | |||
208 | return IRQ_HANDLED; | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * si4713_send_command - sends a command to si4713 and waits its response | ||
213 | * @sdev: si4713_device structure for the device we are communicating | ||
214 | * @command: command id | ||
215 | * @args: command arguments we are sending (up to 7) | ||
216 | * @argn: actual size of @args | ||
217 | * @response: buffer to place the expected response from the device (up to 15) | ||
218 | * @respn: actual size of @response | ||
219 | * @usecs: amount of time to wait before reading the response (in usecs) | ||
220 | */ | ||
221 | static int si4713_send_command(struct si4713_device *sdev, const u8 command, | ||
222 | const u8 args[], const int argn, | ||
223 | u8 response[], const int respn, const int usecs) | ||
224 | { | ||
225 | struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); | ||
226 | u8 data1[MAX_ARGS + 1]; | ||
227 | int err; | ||
228 | |||
229 | if (!client->adapter) | ||
230 | return -ENODEV; | ||
231 | |||
232 | /* First send the command and its arguments */ | ||
233 | data1[0] = command; | ||
234 | memcpy(data1 + 1, args, argn); | ||
235 | DBG_BUFFER(&sdev->sd, "Parameters", data1, argn + 1); | ||
236 | |||
237 | err = i2c_master_send(client, data1, argn + 1); | ||
238 | if (err != argn + 1) { | ||
239 | v4l2_err(&sdev->sd, "Error while sending command 0x%02x\n", | ||
240 | command); | ||
241 | return (err > 0) ? -EIO : err; | ||
242 | } | ||
243 | |||
244 | /* Wait response from interrupt */ | ||
245 | if (!wait_for_completion_timeout(&sdev->work, | ||
246 | usecs_to_jiffies(usecs) + 1)) | ||
247 | v4l2_warn(&sdev->sd, | ||
248 | "(%s) Device took too much time to answer.\n", | ||
249 | __func__); | ||
250 | |||
251 | /* Then get the response */ | ||
252 | err = i2c_master_recv(client, response, respn); | ||
253 | if (err != respn) { | ||
254 | v4l2_err(&sdev->sd, | ||
255 | "Error while reading response for command 0x%02x\n", | ||
256 | command); | ||
257 | return (err > 0) ? -EIO : err; | ||
258 | } | ||
259 | |||
260 | DBG_BUFFER(&sdev->sd, "Response", response, respn); | ||
261 | if (check_command_failed(response[0])) | ||
262 | return -EBUSY; | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * si4713_read_property - reads a si4713 property | ||
269 | * @sdev: si4713_device structure for the device we are communicating | ||
270 | * @prop: property identification number | ||
271 | * @pv: property value to be returned on success | ||
272 | */ | ||
273 | static int si4713_read_property(struct si4713_device *sdev, u16 prop, u32 *pv) | ||
274 | { | ||
275 | int err; | ||
276 | u8 val[SI4713_GET_PROP_NRESP]; | ||
277 | /* | ||
278 | * .First byte = 0 | ||
279 | * .Second byte = property's MSB | ||
280 | * .Third byte = property's LSB | ||
281 | */ | ||
282 | const u8 args[SI4713_GET_PROP_NARGS] = { | ||
283 | 0x00, | ||
284 | msb(prop), | ||
285 | lsb(prop), | ||
286 | }; | ||
287 | |||
288 | err = si4713_send_command(sdev, SI4713_CMD_GET_PROPERTY, | ||
289 | args, ARRAY_SIZE(args), val, | ||
290 | ARRAY_SIZE(val), DEFAULT_TIMEOUT); | ||
291 | |||
292 | if (err < 0) | ||
293 | return err; | ||
294 | |||
295 | *pv = compose_u16(val[2], val[3]); | ||
296 | |||
297 | v4l2_dbg(1, debug, &sdev->sd, | ||
298 | "%s: property=0x%02x value=0x%02x status=0x%02x\n", | ||
299 | __func__, prop, *pv, val[0]); | ||
300 | |||
301 | return err; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * si4713_write_property - modifies a si4713 property | ||
306 | * @sdev: si4713_device structure for the device we are communicating | ||
307 | * @prop: property identification number | ||
308 | * @val: new value for that property | ||
309 | */ | ||
310 | static int si4713_write_property(struct si4713_device *sdev, u16 prop, u16 val) | ||
311 | { | ||
312 | int rval; | ||
313 | u8 resp[SI4713_SET_PROP_NRESP]; | ||
314 | /* | ||
315 | * .First byte = 0 | ||
316 | * .Second byte = property's MSB | ||
317 | * .Third byte = property's LSB | ||
318 | * .Fourth byte = value's MSB | ||
319 | * .Fifth byte = value's LSB | ||
320 | */ | ||
321 | const u8 args[SI4713_SET_PROP_NARGS] = { | ||
322 | 0x00, | ||
323 | msb(prop), | ||
324 | lsb(prop), | ||
325 | msb(val), | ||
326 | lsb(val), | ||
327 | }; | ||
328 | |||
329 | rval = si4713_send_command(sdev, SI4713_CMD_SET_PROPERTY, | ||
330 | args, ARRAY_SIZE(args), | ||
331 | resp, ARRAY_SIZE(resp), | ||
332 | DEFAULT_TIMEOUT); | ||
333 | |||
334 | if (rval < 0) | ||
335 | return rval; | ||
336 | |||
337 | v4l2_dbg(1, debug, &sdev->sd, | ||
338 | "%s: property=0x%02x value=0x%02x status=0x%02x\n", | ||
339 | __func__, prop, val, resp[0]); | ||
340 | |||
341 | /* | ||
342 | * As there is no command response for SET_PROPERTY, | ||
343 | * wait Tcomp time to finish before proceed, in order | ||
344 | * to have property properly set. | ||
345 | */ | ||
346 | msleep(TIMEOUT_SET_PROPERTY); | ||
347 | |||
348 | return rval; | ||
349 | } | ||
350 | |||
351 | /* | ||
352 | * si4713_powerup - Powers the device up | ||
353 | * @sdev: si4713_device structure for the device we are communicating | ||
354 | */ | ||
355 | static int si4713_powerup(struct si4713_device *sdev) | ||
356 | { | ||
357 | int err; | ||
358 | u8 resp[SI4713_PWUP_NRESP]; | ||
359 | /* | ||
360 | * .First byte = Enabled interrupts and boot function | ||
361 | * .Second byte = Input operation mode | ||
362 | */ | ||
363 | const u8 args[SI4713_PWUP_NARGS] = { | ||
364 | SI4713_PWUP_CTSIEN | SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX, | ||
365 | SI4713_PWUP_OPMOD_ANALOG, | ||
366 | }; | ||
367 | |||
368 | if (sdev->power_state) | ||
369 | return 0; | ||
370 | |||
371 | sdev->platform_data->set_power(1); | ||
372 | err = si4713_send_command(sdev, SI4713_CMD_POWER_UP, | ||
373 | args, ARRAY_SIZE(args), | ||
374 | resp, ARRAY_SIZE(resp), | ||
375 | TIMEOUT_POWER_UP); | ||
376 | |||
377 | if (!err) { | ||
378 | v4l2_dbg(1, debug, &sdev->sd, "Powerup response: 0x%02x\n", | ||
379 | resp[0]); | ||
380 | v4l2_dbg(1, debug, &sdev->sd, "Device in power up mode\n"); | ||
381 | sdev->power_state = POWER_ON; | ||
382 | |||
383 | err = si4713_write_property(sdev, SI4713_GPO_IEN, | ||
384 | SI4713_STC_INT | SI4713_CTS); | ||
385 | } else { | ||
386 | sdev->platform_data->set_power(0); | ||
387 | } | ||
388 | |||
389 | return err; | ||
390 | } | ||
391 | |||
392 | /* | ||
393 | * si4713_powerdown - Powers the device down | ||
394 | * @sdev: si4713_device structure for the device we are communicating | ||
395 | */ | ||
396 | static int si4713_powerdown(struct si4713_device *sdev) | ||
397 | { | ||
398 | int err; | ||
399 | u8 resp[SI4713_PWDN_NRESP]; | ||
400 | |||
401 | if (!sdev->power_state) | ||
402 | return 0; | ||
403 | |||
404 | err = si4713_send_command(sdev, SI4713_CMD_POWER_DOWN, | ||
405 | NULL, 0, | ||
406 | resp, ARRAY_SIZE(resp), | ||
407 | DEFAULT_TIMEOUT); | ||
408 | |||
409 | if (!err) { | ||
410 | v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n", | ||
411 | resp[0]); | ||
412 | v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n"); | ||
413 | sdev->platform_data->set_power(0); | ||
414 | sdev->power_state = POWER_OFF; | ||
415 | } | ||
416 | |||
417 | return err; | ||
418 | } | ||
419 | |||
420 | /* | ||
421 | * si4713_checkrev - Checks if we are treating a device with the correct rev. | ||
422 | * @sdev: si4713_device structure for the device we are communicating | ||
423 | */ | ||
424 | static int si4713_checkrev(struct si4713_device *sdev) | ||
425 | { | ||
426 | struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); | ||
427 | int rval; | ||
428 | u8 resp[SI4713_GETREV_NRESP]; | ||
429 | |||
430 | mutex_lock(&sdev->mutex); | ||
431 | |||
432 | rval = si4713_send_command(sdev, SI4713_CMD_GET_REV, | ||
433 | NULL, 0, | ||
434 | resp, ARRAY_SIZE(resp), | ||
435 | DEFAULT_TIMEOUT); | ||
436 | |||
437 | if (rval < 0) | ||
438 | goto unlock; | ||
439 | |||
440 | if (resp[1] == SI4713_PRODUCT_NUMBER) { | ||
441 | v4l2_info(&sdev->sd, "chip found @ 0x%02x (%s)\n", | ||
442 | client->addr << 1, client->adapter->name); | ||
443 | } else { | ||
444 | v4l2_err(&sdev->sd, "Invalid product number\n"); | ||
445 | rval = -EINVAL; | ||
446 | } | ||
447 | |||
448 | unlock: | ||
449 | mutex_unlock(&sdev->mutex); | ||
450 | return rval; | ||
451 | } | ||
452 | |||
453 | /* | ||
454 | * si4713_wait_stc - Waits STC interrupt and clears status bits. Usefull | ||
455 | * for TX_TUNE_POWER, TX_TUNE_FREQ and TX_TUNE_MEAS | ||
456 | * @sdev: si4713_device structure for the device we are communicating | ||
457 | * @usecs: timeout to wait for STC interrupt signal | ||
458 | */ | ||
459 | static int si4713_wait_stc(struct si4713_device *sdev, const int usecs) | ||
460 | { | ||
461 | int err; | ||
462 | u8 resp[SI4713_GET_STATUS_NRESP]; | ||
463 | |||
464 | /* Wait response from STC interrupt */ | ||
465 | if (!wait_for_completion_timeout(&sdev->work, | ||
466 | usecs_to_jiffies(usecs) + 1)) | ||
467 | v4l2_warn(&sdev->sd, | ||
468 | "%s: device took too much time to answer (%d usec).\n", | ||
469 | __func__, usecs); | ||
470 | |||
471 | /* Clear status bits */ | ||
472 | err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS, | ||
473 | NULL, 0, | ||
474 | resp, ARRAY_SIZE(resp), | ||
475 | DEFAULT_TIMEOUT); | ||
476 | |||
477 | if (err < 0) | ||
478 | goto exit; | ||
479 | |||
480 | v4l2_dbg(1, debug, &sdev->sd, | ||
481 | "%s: status bits: 0x%02x\n", __func__, resp[0]); | ||
482 | |||
483 | if (!(resp[0] & SI4713_STC_INT)) | ||
484 | err = -EIO; | ||
485 | |||
486 | exit: | ||
487 | return err; | ||
488 | } | ||
489 | |||
490 | /* | ||
491 | * si4713_tx_tune_freq - Sets the state of the RF carrier and sets the tuning | ||
492 | * frequency between 76 and 108 MHz in 10 kHz units and | ||
493 | * steps of 50 kHz. | ||
494 | * @sdev: si4713_device structure for the device we are communicating | ||
495 | * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) | ||
496 | */ | ||
497 | static int si4713_tx_tune_freq(struct si4713_device *sdev, u16 frequency) | ||
498 | { | ||
499 | int err; | ||
500 | u8 val[SI4713_TXFREQ_NRESP]; | ||
501 | /* | ||
502 | * .First byte = 0 | ||
503 | * .Second byte = frequency's MSB | ||
504 | * .Third byte = frequency's LSB | ||
505 | */ | ||
506 | const u8 args[SI4713_TXFREQ_NARGS] = { | ||
507 | 0x00, | ||
508 | msb(frequency), | ||
509 | lsb(frequency), | ||
510 | }; | ||
511 | |||
512 | err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_FREQ, | ||
513 | args, ARRAY_SIZE(args), val, | ||
514 | ARRAY_SIZE(val), DEFAULT_TIMEOUT); | ||
515 | |||
516 | if (err < 0) | ||
517 | return err; | ||
518 | |||
519 | v4l2_dbg(1, debug, &sdev->sd, | ||
520 | "%s: frequency=0x%02x status=0x%02x\n", __func__, | ||
521 | frequency, val[0]); | ||
522 | |||
523 | err = si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); | ||
524 | if (err < 0) | ||
525 | return err; | ||
526 | |||
527 | return compose_u16(args[1], args[2]); | ||
528 | } | ||
529 | |||
530 | /* | ||
531 | * si4713_tx_tune_power - Sets the RF voltage level between 88 and 115 dBuV in | ||
532 | * 1 dB units. A value of 0x00 indicates off. The command | ||
533 | * also sets the antenna tuning capacitance. A value of 0 | ||
534 | * indicates autotuning, and a value of 1 - 191 indicates | ||
535 | * a manual override, which results in a tuning | ||
536 | * capacitance of 0.25 pF x @antcap. | ||
537 | * @sdev: si4713_device structure for the device we are communicating | ||
538 | * @power: tuning power (88 - 115 dBuV, unit/step 1 dB) | ||
539 | * @antcap: value of antenna tuning capacitor (0 - 191) | ||
540 | */ | ||
541 | static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power, | ||
542 | u8 antcap) | ||
543 | { | ||
544 | int err; | ||
545 | u8 val[SI4713_TXPWR_NRESP]; | ||
546 | /* | ||
547 | * .First byte = 0 | ||
548 | * .Second byte = 0 | ||
549 | * .Third byte = power | ||
550 | * .Fourth byte = antcap | ||
551 | */ | ||
552 | const u8 args[SI4713_TXPWR_NARGS] = { | ||
553 | 0x00, | ||
554 | 0x00, | ||
555 | power, | ||
556 | antcap, | ||
557 | }; | ||
558 | |||
559 | if (((power > 0) && (power < SI4713_MIN_POWER)) || | ||
560 | power > SI4713_MAX_POWER || antcap > SI4713_MAX_ANTCAP) | ||
561 | return -EDOM; | ||
562 | |||
563 | err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_POWER, | ||
564 | args, ARRAY_SIZE(args), val, | ||
565 | ARRAY_SIZE(val), DEFAULT_TIMEOUT); | ||
566 | |||
567 | if (err < 0) | ||
568 | return err; | ||
569 | |||
570 | v4l2_dbg(1, debug, &sdev->sd, | ||
571 | "%s: power=0x%02x antcap=0x%02x status=0x%02x\n", | ||
572 | __func__, power, antcap, val[0]); | ||
573 | |||
574 | return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE_POWER); | ||
575 | } | ||
576 | |||
577 | /* | ||
578 | * si4713_tx_tune_measure - Enters receive mode and measures the received noise | ||
579 | * level in units of dBuV on the selected frequency. | ||
580 | * The Frequency must be between 76 and 108 MHz in 10 kHz | ||
581 | * units and steps of 50 kHz. The command also sets the | ||
582 | * antenna tuning capacitance. A value of 0 means | ||
583 | * autotuning, and a value of 1 to 191 indicates manual | ||
584 | * override. | ||
585 | * @sdev: si4713_device structure for the device we are communicating | ||
586 | * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) | ||
587 | * @antcap: value of antenna tuning capacitor (0 - 191) | ||
588 | */ | ||
589 | static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency, | ||
590 | u8 antcap) | ||
591 | { | ||
592 | int err; | ||
593 | u8 val[SI4713_TXMEA_NRESP]; | ||
594 | /* | ||
595 | * .First byte = 0 | ||
596 | * .Second byte = frequency's MSB | ||
597 | * .Third byte = frequency's LSB | ||
598 | * .Fourth byte = antcap | ||
599 | */ | ||
600 | const u8 args[SI4713_TXMEA_NARGS] = { | ||
601 | 0x00, | ||
602 | msb(frequency), | ||
603 | lsb(frequency), | ||
604 | antcap, | ||
605 | }; | ||
606 | |||
607 | sdev->tune_rnl = DEFAULT_TUNE_RNL; | ||
608 | |||
609 | if (antcap > SI4713_MAX_ANTCAP) | ||
610 | return -EDOM; | ||
611 | |||
612 | err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_MEASURE, | ||
613 | args, ARRAY_SIZE(args), val, | ||
614 | ARRAY_SIZE(val), DEFAULT_TIMEOUT); | ||
615 | |||
616 | if (err < 0) | ||
617 | return err; | ||
618 | |||
619 | v4l2_dbg(1, debug, &sdev->sd, | ||
620 | "%s: frequency=0x%02x antcap=0x%02x status=0x%02x\n", | ||
621 | __func__, frequency, antcap, val[0]); | ||
622 | |||
623 | return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); | ||
624 | } | ||
625 | |||
626 | /* | ||
627 | * si4713_tx_tune_status- Returns the status of the tx_tune_freq, tx_tune_mea or | ||
628 | * tx_tune_power commands. This command return the current | ||
629 | * frequency, output voltage in dBuV, the antenna tunning | ||
630 | * capacitance value and the received noise level. The | ||
631 | * command also clears the stcint interrupt bit when the | ||
632 | * first bit of its arguments is high. | ||
633 | * @sdev: si4713_device structure for the device we are communicating | ||
634 | * @intack: 0x01 to clear the seek/tune complete interrupt status indicator. | ||
635 | * @frequency: returned frequency | ||
636 | * @power: returned power | ||
637 | * @antcap: returned antenna capacitance | ||
638 | * @noise: returned noise level | ||
639 | */ | ||
640 | static int si4713_tx_tune_status(struct si4713_device *sdev, u8 intack, | ||
641 | u16 *frequency, u8 *power, | ||
642 | u8 *antcap, u8 *noise) | ||
643 | { | ||
644 | int err; | ||
645 | u8 val[SI4713_TXSTATUS_NRESP]; | ||
646 | /* | ||
647 | * .First byte = intack bit | ||
648 | */ | ||
649 | const u8 args[SI4713_TXSTATUS_NARGS] = { | ||
650 | intack & SI4713_INTACK_MASK, | ||
651 | }; | ||
652 | |||
653 | err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_STATUS, | ||
654 | args, ARRAY_SIZE(args), val, | ||
655 | ARRAY_SIZE(val), DEFAULT_TIMEOUT); | ||
656 | |||
657 | if (!err) { | ||
658 | v4l2_dbg(1, debug, &sdev->sd, | ||
659 | "%s: status=0x%02x\n", __func__, val[0]); | ||
660 | *frequency = compose_u16(val[2], val[3]); | ||
661 | sdev->frequency = *frequency; | ||
662 | *power = val[5]; | ||
663 | *antcap = val[6]; | ||
664 | *noise = val[7]; | ||
665 | v4l2_dbg(1, debug, &sdev->sd, "%s: response: %d x 10 kHz " | ||
666 | "(power %d, antcap %d, rnl %d)\n", __func__, | ||
667 | *frequency, *power, *antcap, *noise); | ||
668 | } | ||
669 | |||
670 | return err; | ||
671 | } | ||
672 | |||
673 | /* | ||
674 | * si4713_tx_rds_buff - Loads the RDS group buffer FIFO or circular buffer. | ||
675 | * @sdev: si4713_device structure for the device we are communicating | ||
676 | * @mode: the buffer operation mode. | ||
677 | * @rdsb: RDS Block B | ||
678 | * @rdsc: RDS Block C | ||
679 | * @rdsd: RDS Block D | ||
680 | * @cbleft: returns the number of available circular buffer blocks minus the | ||
681 | * number of used circular buffer blocks. | ||
682 | */ | ||
683 | static int si4713_tx_rds_buff(struct si4713_device *sdev, u8 mode, u16 rdsb, | ||
684 | u16 rdsc, u16 rdsd, s8 *cbleft) | ||
685 | { | ||
686 | int err; | ||
687 | u8 val[SI4713_RDSBUFF_NRESP]; | ||
688 | |||
689 | const u8 args[SI4713_RDSBUFF_NARGS] = { | ||
690 | mode & SI4713_RDSBUFF_MODE_MASK, | ||
691 | msb(rdsb), | ||
692 | lsb(rdsb), | ||
693 | msb(rdsc), | ||
694 | lsb(rdsc), | ||
695 | msb(rdsd), | ||
696 | lsb(rdsd), | ||
697 | }; | ||
698 | |||
699 | err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_BUFF, | ||
700 | args, ARRAY_SIZE(args), val, | ||
701 | ARRAY_SIZE(val), DEFAULT_TIMEOUT); | ||
702 | |||
703 | if (!err) { | ||
704 | v4l2_dbg(1, debug, &sdev->sd, | ||
705 | "%s: status=0x%02x\n", __func__, val[0]); | ||
706 | *cbleft = (s8)val[2] - val[3]; | ||
707 | v4l2_dbg(1, debug, &sdev->sd, "%s: response: interrupts" | ||
708 | " 0x%02x cb avail: %d cb used %d fifo avail" | ||
709 | " %d fifo used %d\n", __func__, val[1], | ||
710 | val[2], val[3], val[4], val[5]); | ||
711 | } | ||
712 | |||
713 | return err; | ||
714 | } | ||
715 | |||
716 | /* | ||
717 | * si4713_tx_rds_ps - Loads the program service buffer. | ||
718 | * @sdev: si4713_device structure for the device we are communicating | ||
719 | * @psid: program service id to be loaded. | ||
720 | * @pschar: assumed 4 size char array to be loaded into the program service | ||
721 | */ | ||
722 | static int si4713_tx_rds_ps(struct si4713_device *sdev, u8 psid, | ||
723 | unsigned char *pschar) | ||
724 | { | ||
725 | int err; | ||
726 | u8 val[SI4713_RDSPS_NRESP]; | ||
727 | |||
728 | const u8 args[SI4713_RDSPS_NARGS] = { | ||
729 | psid & SI4713_RDSPS_PSID_MASK, | ||
730 | pschar[0], | ||
731 | pschar[1], | ||
732 | pschar[2], | ||
733 | pschar[3], | ||
734 | }; | ||
735 | |||
736 | err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_PS, | ||
737 | args, ARRAY_SIZE(args), val, | ||
738 | ARRAY_SIZE(val), DEFAULT_TIMEOUT); | ||
739 | |||
740 | if (err < 0) | ||
741 | return err; | ||
742 | |||
743 | v4l2_dbg(1, debug, &sdev->sd, "%s: status=0x%02x\n", __func__, val[0]); | ||
744 | |||
745 | return err; | ||
746 | } | ||
747 | |||
748 | static int si4713_set_power_state(struct si4713_device *sdev, u8 value) | ||
749 | { | ||
750 | int rval; | ||
751 | |||
752 | mutex_lock(&sdev->mutex); | ||
753 | |||
754 | if (value) | ||
755 | rval = si4713_powerup(sdev); | ||
756 | else | ||
757 | rval = si4713_powerdown(sdev); | ||
758 | |||
759 | mutex_unlock(&sdev->mutex); | ||
760 | return rval; | ||
761 | } | ||
762 | |||
763 | static int si4713_set_mute(struct si4713_device *sdev, u16 mute) | ||
764 | { | ||
765 | int rval = 0; | ||
766 | |||
767 | mute = set_mute(mute); | ||
768 | |||
769 | mutex_lock(&sdev->mutex); | ||
770 | |||
771 | if (sdev->power_state) | ||
772 | rval = si4713_write_property(sdev, | ||
773 | SI4713_TX_LINE_INPUT_MUTE, mute); | ||
774 | |||
775 | if (rval >= 0) | ||
776 | sdev->mute = get_mute(mute); | ||
777 | |||
778 | mutex_unlock(&sdev->mutex); | ||
779 | |||
780 | return rval; | ||
781 | } | ||
782 | |||
783 | static int si4713_set_rds_ps_name(struct si4713_device *sdev, char *ps_name) | ||
784 | { | ||
785 | int rval = 0, i; | ||
786 | u8 len = 0; | ||
787 | |||
788 | /* We want to clear the whole thing */ | ||
789 | if (!strlen(ps_name)) | ||
790 | memset(ps_name, 0, MAX_RDS_PS_NAME + 1); | ||
791 | |||
792 | mutex_lock(&sdev->mutex); | ||
793 | |||
794 | if (sdev->power_state) { | ||
795 | /* Write the new ps name and clear the padding */ | ||
796 | for (i = 0; i < MAX_RDS_PS_NAME; i += (RDS_BLOCK / 2)) { | ||
797 | rval = si4713_tx_rds_ps(sdev, (i / (RDS_BLOCK / 2)), | ||
798 | ps_name + i); | ||
799 | if (rval < 0) | ||
800 | goto unlock; | ||
801 | } | ||
802 | |||
803 | /* Setup the size to be sent */ | ||
804 | if (strlen(ps_name)) | ||
805 | len = strlen(ps_name) - 1; | ||
806 | else | ||
807 | len = 1; | ||
808 | |||
809 | rval = si4713_write_property(sdev, | ||
810 | SI4713_TX_RDS_PS_MESSAGE_COUNT, | ||
811 | rds_ps_nblocks(len)); | ||
812 | if (rval < 0) | ||
813 | goto unlock; | ||
814 | |||
815 | rval = si4713_write_property(sdev, | ||
816 | SI4713_TX_RDS_PS_REPEAT_COUNT, | ||
817 | DEFAULT_RDS_PS_REPEAT_COUNT * 2); | ||
818 | if (rval < 0) | ||
819 | goto unlock; | ||
820 | } | ||
821 | |||
822 | strncpy(sdev->rds_info.ps_name, ps_name, MAX_RDS_PS_NAME); | ||
823 | |||
824 | unlock: | ||
825 | mutex_unlock(&sdev->mutex); | ||
826 | return rval; | ||
827 | } | ||
828 | |||
829 | static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt) | ||
830 | { | ||
831 | int rval = 0, i; | ||
832 | u16 t_index = 0; | ||
833 | u8 b_index = 0, cr_inserted = 0; | ||
834 | s8 left; | ||
835 | |||
836 | mutex_lock(&sdev->mutex); | ||
837 | |||
838 | if (!sdev->power_state) | ||
839 | goto copy; | ||
840 | |||
841 | rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_CLEAR, 0, 0, 0, &left); | ||
842 | if (rval < 0) | ||
843 | goto unlock; | ||
844 | |||
845 | if (!strlen(rt)) | ||
846 | goto copy; | ||
847 | |||
848 | do { | ||
849 | /* RDS spec says that if the last block isn't used, | ||
850 | * then apply a carriage return | ||
851 | */ | ||
852 | if (t_index < (RDS_RADIOTEXT_INDEX_MAX * | ||
853 | RDS_RADIOTEXT_BLK_SIZE)) { | ||
854 | for (i = 0; i < RDS_RADIOTEXT_BLK_SIZE; i++) { | ||
855 | if (!rt[t_index + i] || rt[t_index + i] == | ||
856 | RDS_CARRIAGE_RETURN) { | ||
857 | rt[t_index + i] = RDS_CARRIAGE_RETURN; | ||
858 | cr_inserted = 1; | ||
859 | break; | ||
860 | } | ||
861 | } | ||
862 | } | ||
863 | |||
864 | rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_LOAD, | ||
865 | compose_u16(RDS_RADIOTEXT_2A, b_index++), | ||
866 | compose_u16(rt[t_index], rt[t_index + 1]), | ||
867 | compose_u16(rt[t_index + 2], rt[t_index + 3]), | ||
868 | &left); | ||
869 | if (rval < 0) | ||
870 | goto unlock; | ||
871 | |||
872 | t_index += RDS_RADIOTEXT_BLK_SIZE; | ||
873 | |||
874 | if (cr_inserted) | ||
875 | break; | ||
876 | } while (left > 0); | ||
877 | |||
878 | copy: | ||
879 | strncpy(sdev->rds_info.radio_text, rt, MAX_RDS_RADIO_TEXT); | ||
880 | |||
881 | unlock: | ||
882 | mutex_unlock(&sdev->mutex); | ||
883 | return rval; | ||
884 | } | ||
885 | |||
886 | static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id, | ||
887 | u32 **shadow, s32 *bit, s32 *mask, u16 *property, int *mul, | ||
888 | unsigned long **table, int *size) | ||
889 | { | ||
890 | s32 rval = 0; | ||
891 | |||
892 | switch (id) { | ||
893 | /* FM_TX class controls */ | ||
894 | case V4L2_CID_RDS_TX_PI: | ||
895 | *property = SI4713_TX_RDS_PI; | ||
896 | *mul = 1; | ||
897 | *shadow = &sdev->rds_info.pi; | ||
898 | break; | ||
899 | case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: | ||
900 | *property = SI4713_TX_ACOMP_THRESHOLD; | ||
901 | *mul = 1; | ||
902 | *shadow = &sdev->acomp_info.threshold; | ||
903 | break; | ||
904 | case V4L2_CID_AUDIO_COMPRESSION_GAIN: | ||
905 | *property = SI4713_TX_ACOMP_GAIN; | ||
906 | *mul = 1; | ||
907 | *shadow = &sdev->acomp_info.gain; | ||
908 | break; | ||
909 | case V4L2_CID_PILOT_TONE_FREQUENCY: | ||
910 | *property = SI4713_TX_PILOT_FREQUENCY; | ||
911 | *mul = 1; | ||
912 | *shadow = &sdev->pilot_info.frequency; | ||
913 | break; | ||
914 | case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: | ||
915 | *property = SI4713_TX_ACOMP_ATTACK_TIME; | ||
916 | *mul = ATTACK_TIME_UNIT; | ||
917 | *shadow = &sdev->acomp_info.attack_time; | ||
918 | break; | ||
919 | case V4L2_CID_PILOT_TONE_DEVIATION: | ||
920 | *property = SI4713_TX_PILOT_DEVIATION; | ||
921 | *mul = 10; | ||
922 | *shadow = &sdev->pilot_info.deviation; | ||
923 | break; | ||
924 | case V4L2_CID_AUDIO_LIMITER_DEVIATION: | ||
925 | *property = SI4713_TX_AUDIO_DEVIATION; | ||
926 | *mul = 10; | ||
927 | *shadow = &sdev->limiter_info.deviation; | ||
928 | break; | ||
929 | case V4L2_CID_RDS_TX_DEVIATION: | ||
930 | *property = SI4713_TX_RDS_DEVIATION; | ||
931 | *mul = 1; | ||
932 | *shadow = &sdev->rds_info.deviation; | ||
933 | break; | ||
934 | |||
935 | case V4L2_CID_RDS_TX_PTY: | ||
936 | *property = SI4713_TX_RDS_PS_MISC; | ||
937 | *bit = 5; | ||
938 | *mask = 0x1F << 5; | ||
939 | *shadow = &sdev->rds_info.pty; | ||
940 | break; | ||
941 | case V4L2_CID_AUDIO_LIMITER_ENABLED: | ||
942 | *property = SI4713_TX_ACOMP_ENABLE; | ||
943 | *bit = 1; | ||
944 | *mask = 1 << 1; | ||
945 | *shadow = &sdev->limiter_info.enabled; | ||
946 | break; | ||
947 | case V4L2_CID_AUDIO_COMPRESSION_ENABLED: | ||
948 | *property = SI4713_TX_ACOMP_ENABLE; | ||
949 | *bit = 0; | ||
950 | *mask = 1 << 0; | ||
951 | *shadow = &sdev->acomp_info.enabled; | ||
952 | break; | ||
953 | case V4L2_CID_PILOT_TONE_ENABLED: | ||
954 | *property = SI4713_TX_COMPONENT_ENABLE; | ||
955 | *bit = 0; | ||
956 | *mask = 1 << 0; | ||
957 | *shadow = &sdev->pilot_info.enabled; | ||
958 | break; | ||
959 | |||
960 | case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: | ||
961 | *property = SI4713_TX_LIMITER_RELEASE_TIME; | ||
962 | *table = limiter_times; | ||
963 | *size = ARRAY_SIZE(limiter_times); | ||
964 | *shadow = &sdev->limiter_info.release_time; | ||
965 | break; | ||
966 | case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: | ||
967 | *property = SI4713_TX_ACOMP_RELEASE_TIME; | ||
968 | *table = acomp_rtimes; | ||
969 | *size = ARRAY_SIZE(acomp_rtimes); | ||
970 | *shadow = &sdev->acomp_info.release_time; | ||
971 | break; | ||
972 | case V4L2_CID_TUNE_PREEMPHASIS: | ||
973 | *property = SI4713_TX_PREEMPHASIS; | ||
974 | *table = preemphasis_values; | ||
975 | *size = ARRAY_SIZE(preemphasis_values); | ||
976 | *shadow = &sdev->preemphasis; | ||
977 | break; | ||
978 | |||
979 | default: | ||
980 | rval = -EINVAL; | ||
981 | }; | ||
982 | |||
983 | return rval; | ||
984 | } | ||
985 | |||
986 | static int si4713_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc); | ||
987 | |||
988 | /* write string property */ | ||
989 | static int si4713_write_econtrol_string(struct si4713_device *sdev, | ||
990 | struct v4l2_ext_control *control) | ||
991 | { | ||
992 | struct v4l2_queryctrl vqc; | ||
993 | int len; | ||
994 | s32 rval = 0; | ||
995 | |||
996 | vqc.id = control->id; | ||
997 | rval = si4713_queryctrl(&sdev->sd, &vqc); | ||
998 | if (rval < 0) | ||
999 | goto exit; | ||
1000 | |||
1001 | switch (control->id) { | ||
1002 | case V4L2_CID_RDS_TX_PS_NAME: { | ||
1003 | char ps_name[MAX_RDS_PS_NAME + 1]; | ||
1004 | |||
1005 | len = control->size - 1; | ||
1006 | if (len > MAX_RDS_PS_NAME) { | ||
1007 | rval = -ERANGE; | ||
1008 | goto exit; | ||
1009 | } | ||
1010 | rval = copy_from_user(ps_name, control->string, len); | ||
1011 | if (rval < 0) | ||
1012 | goto exit; | ||
1013 | ps_name[len] = '\0'; | ||
1014 | |||
1015 | if (strlen(ps_name) % vqc.step) { | ||
1016 | rval = -ERANGE; | ||
1017 | goto exit; | ||
1018 | } | ||
1019 | |||
1020 | rval = si4713_set_rds_ps_name(sdev, ps_name); | ||
1021 | } | ||
1022 | break; | ||
1023 | |||
1024 | case V4L2_CID_RDS_TX_RADIO_TEXT: { | ||
1025 | char radio_text[MAX_RDS_RADIO_TEXT + 1]; | ||
1026 | |||
1027 | len = control->size - 1; | ||
1028 | if (len > MAX_RDS_RADIO_TEXT) { | ||
1029 | rval = -ERANGE; | ||
1030 | goto exit; | ||
1031 | } | ||
1032 | rval = copy_from_user(radio_text, control->string, len); | ||
1033 | if (rval < 0) | ||
1034 | goto exit; | ||
1035 | radio_text[len] = '\0'; | ||
1036 | |||
1037 | if (strlen(radio_text) % vqc.step) { | ||
1038 | rval = -ERANGE; | ||
1039 | goto exit; | ||
1040 | } | ||
1041 | |||
1042 | rval = si4713_set_rds_radio_text(sdev, radio_text); | ||
1043 | } | ||
1044 | break; | ||
1045 | |||
1046 | default: | ||
1047 | rval = -EINVAL; | ||
1048 | break; | ||
1049 | }; | ||
1050 | |||
1051 | exit: | ||
1052 | return rval; | ||
1053 | } | ||
1054 | |||
1055 | static int validate_range(struct v4l2_subdev *sd, | ||
1056 | struct v4l2_ext_control *control) | ||
1057 | { | ||
1058 | struct v4l2_queryctrl vqc; | ||
1059 | int rval; | ||
1060 | |||
1061 | vqc.id = control->id; | ||
1062 | rval = si4713_queryctrl(sd, &vqc); | ||
1063 | if (rval < 0) | ||
1064 | goto exit; | ||
1065 | |||
1066 | if (control->value < vqc.minimum || control->value > vqc.maximum) | ||
1067 | rval = -ERANGE; | ||
1068 | |||
1069 | exit: | ||
1070 | return rval; | ||
1071 | } | ||
1072 | |||
1073 | /* properties which use tx_tune_power*/ | ||
1074 | static int si4713_write_econtrol_tune(struct si4713_device *sdev, | ||
1075 | struct v4l2_ext_control *control) | ||
1076 | { | ||
1077 | s32 rval = 0; | ||
1078 | u8 power, antcap; | ||
1079 | |||
1080 | rval = validate_range(&sdev->sd, control); | ||
1081 | if (rval < 0) | ||
1082 | goto exit; | ||
1083 | |||
1084 | mutex_lock(&sdev->mutex); | ||
1085 | |||
1086 | switch (control->id) { | ||
1087 | case V4L2_CID_TUNE_POWER_LEVEL: | ||
1088 | power = control->value; | ||
1089 | antcap = sdev->antenna_capacitor; | ||
1090 | break; | ||
1091 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: | ||
1092 | power = sdev->power_level; | ||
1093 | antcap = control->value; | ||
1094 | break; | ||
1095 | default: | ||
1096 | rval = -EINVAL; | ||
1097 | goto unlock; | ||
1098 | }; | ||
1099 | |||
1100 | if (sdev->power_state) | ||
1101 | rval = si4713_tx_tune_power(sdev, power, antcap); | ||
1102 | |||
1103 | if (rval == 0) { | ||
1104 | sdev->power_level = power; | ||
1105 | sdev->antenna_capacitor = antcap; | ||
1106 | } | ||
1107 | |||
1108 | unlock: | ||
1109 | mutex_unlock(&sdev->mutex); | ||
1110 | exit: | ||
1111 | return rval; | ||
1112 | } | ||
1113 | |||
1114 | static int si4713_write_econtrol_integers(struct si4713_device *sdev, | ||
1115 | struct v4l2_ext_control *control) | ||
1116 | { | ||
1117 | s32 rval; | ||
1118 | u32 *shadow = NULL, val = 0; | ||
1119 | s32 bit = 0, mask = 0; | ||
1120 | u16 property = 0; | ||
1121 | int mul = 0; | ||
1122 | unsigned long *table = NULL; | ||
1123 | int size = 0; | ||
1124 | |||
1125 | rval = validate_range(&sdev->sd, control); | ||
1126 | if (rval < 0) | ||
1127 | goto exit; | ||
1128 | |||
1129 | rval = si4713_choose_econtrol_action(sdev, control->id, &shadow, &bit, | ||
1130 | &mask, &property, &mul, &table, &size); | ||
1131 | if (rval < 0) | ||
1132 | goto exit; | ||
1133 | |||
1134 | val = control->value; | ||
1135 | if (mul) { | ||
1136 | val = control->value / mul; | ||
1137 | } else if (table) { | ||
1138 | rval = usecs_to_dev(control->value, table, size); | ||
1139 | if (rval < 0) | ||
1140 | goto exit; | ||
1141 | val = rval; | ||
1142 | rval = 0; | ||
1143 | } | ||
1144 | |||
1145 | mutex_lock(&sdev->mutex); | ||
1146 | |||
1147 | if (sdev->power_state) { | ||
1148 | if (mask) { | ||
1149 | rval = si4713_read_property(sdev, property, &val); | ||
1150 | if (rval < 0) | ||
1151 | goto unlock; | ||
1152 | val = set_bits(val, control->value, bit, mask); | ||
1153 | } | ||
1154 | |||
1155 | rval = si4713_write_property(sdev, property, val); | ||
1156 | if (rval < 0) | ||
1157 | goto unlock; | ||
1158 | if (mask) | ||
1159 | val = control->value; | ||
1160 | } | ||
1161 | |||
1162 | if (mul) { | ||
1163 | *shadow = val * mul; | ||
1164 | } else if (table) { | ||
1165 | rval = dev_to_usecs(val, table, size); | ||
1166 | if (rval < 0) | ||
1167 | goto unlock; | ||
1168 | *shadow = rval; | ||
1169 | rval = 0; | ||
1170 | } else { | ||
1171 | *shadow = val; | ||
1172 | } | ||
1173 | |||
1174 | unlock: | ||
1175 | mutex_unlock(&sdev->mutex); | ||
1176 | exit: | ||
1177 | return rval; | ||
1178 | } | ||
1179 | |||
1180 | static int si4713_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f); | ||
1181 | static int si4713_s_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *); | ||
1182 | /* | ||
1183 | * si4713_setup - Sets the device up with current configuration. | ||
1184 | * @sdev: si4713_device structure for the device we are communicating | ||
1185 | */ | ||
1186 | static int si4713_setup(struct si4713_device *sdev) | ||
1187 | { | ||
1188 | struct v4l2_ext_control ctrl; | ||
1189 | struct v4l2_frequency f; | ||
1190 | struct v4l2_modulator vm; | ||
1191 | struct si4713_device *tmp; | ||
1192 | int rval = 0; | ||
1193 | |||
1194 | tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); | ||
1195 | if (!tmp) | ||
1196 | return -ENOMEM; | ||
1197 | |||
1198 | /* Get a local copy to avoid race */ | ||
1199 | mutex_lock(&sdev->mutex); | ||
1200 | memcpy(tmp, sdev, sizeof(*sdev)); | ||
1201 | mutex_unlock(&sdev->mutex); | ||
1202 | |||
1203 | ctrl.id = V4L2_CID_RDS_TX_PI; | ||
1204 | ctrl.value = tmp->rds_info.pi; | ||
1205 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1206 | |||
1207 | ctrl.id = V4L2_CID_AUDIO_COMPRESSION_THRESHOLD; | ||
1208 | ctrl.value = tmp->acomp_info.threshold; | ||
1209 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1210 | |||
1211 | ctrl.id = V4L2_CID_AUDIO_COMPRESSION_GAIN; | ||
1212 | ctrl.value = tmp->acomp_info.gain; | ||
1213 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1214 | |||
1215 | ctrl.id = V4L2_CID_PILOT_TONE_FREQUENCY; | ||
1216 | ctrl.value = tmp->pilot_info.frequency; | ||
1217 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1218 | |||
1219 | ctrl.id = V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME; | ||
1220 | ctrl.value = tmp->acomp_info.attack_time; | ||
1221 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1222 | |||
1223 | ctrl.id = V4L2_CID_PILOT_TONE_DEVIATION; | ||
1224 | ctrl.value = tmp->pilot_info.deviation; | ||
1225 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1226 | |||
1227 | ctrl.id = V4L2_CID_AUDIO_LIMITER_DEVIATION; | ||
1228 | ctrl.value = tmp->limiter_info.deviation; | ||
1229 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1230 | |||
1231 | ctrl.id = V4L2_CID_RDS_TX_DEVIATION; | ||
1232 | ctrl.value = tmp->rds_info.deviation; | ||
1233 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1234 | |||
1235 | ctrl.id = V4L2_CID_RDS_TX_PTY; | ||
1236 | ctrl.value = tmp->rds_info.pty; | ||
1237 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1238 | |||
1239 | ctrl.id = V4L2_CID_AUDIO_LIMITER_ENABLED; | ||
1240 | ctrl.value = tmp->limiter_info.enabled; | ||
1241 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1242 | |||
1243 | ctrl.id = V4L2_CID_AUDIO_COMPRESSION_ENABLED; | ||
1244 | ctrl.value = tmp->acomp_info.enabled; | ||
1245 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1246 | |||
1247 | ctrl.id = V4L2_CID_PILOT_TONE_ENABLED; | ||
1248 | ctrl.value = tmp->pilot_info.enabled; | ||
1249 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1250 | |||
1251 | ctrl.id = V4L2_CID_AUDIO_LIMITER_RELEASE_TIME; | ||
1252 | ctrl.value = tmp->limiter_info.release_time; | ||
1253 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1254 | |||
1255 | ctrl.id = V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME; | ||
1256 | ctrl.value = tmp->acomp_info.release_time; | ||
1257 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1258 | |||
1259 | ctrl.id = V4L2_CID_TUNE_PREEMPHASIS; | ||
1260 | ctrl.value = tmp->preemphasis; | ||
1261 | rval |= si4713_write_econtrol_integers(sdev, &ctrl); | ||
1262 | |||
1263 | ctrl.id = V4L2_CID_RDS_TX_PS_NAME; | ||
1264 | rval |= si4713_set_rds_ps_name(sdev, tmp->rds_info.ps_name); | ||
1265 | |||
1266 | ctrl.id = V4L2_CID_RDS_TX_RADIO_TEXT; | ||
1267 | rval |= si4713_set_rds_radio_text(sdev, tmp->rds_info.radio_text); | ||
1268 | |||
1269 | /* Device procedure needs to set frequency first */ | ||
1270 | f.frequency = tmp->frequency ? tmp->frequency : DEFAULT_FREQUENCY; | ||
1271 | f.frequency = si4713_to_v4l2(f.frequency); | ||
1272 | rval |= si4713_s_frequency(&sdev->sd, &f); | ||
1273 | |||
1274 | ctrl.id = V4L2_CID_TUNE_POWER_LEVEL; | ||
1275 | ctrl.value = tmp->power_level; | ||
1276 | rval |= si4713_write_econtrol_tune(sdev, &ctrl); | ||
1277 | |||
1278 | ctrl.id = V4L2_CID_TUNE_ANTENNA_CAPACITOR; | ||
1279 | ctrl.value = tmp->antenna_capacitor; | ||
1280 | rval |= si4713_write_econtrol_tune(sdev, &ctrl); | ||
1281 | |||
1282 | vm.index = 0; | ||
1283 | if (tmp->stereo) | ||
1284 | vm.txsubchans = V4L2_TUNER_SUB_STEREO; | ||
1285 | else | ||
1286 | vm.txsubchans = V4L2_TUNER_SUB_MONO; | ||
1287 | if (tmp->rds_info.enabled) | ||
1288 | vm.txsubchans |= V4L2_TUNER_SUB_RDS; | ||
1289 | si4713_s_modulator(&sdev->sd, &vm); | ||
1290 | |||
1291 | kfree(tmp); | ||
1292 | |||
1293 | return rval; | ||
1294 | } | ||
1295 | |||
1296 | /* | ||
1297 | * si4713_initialize - Sets the device up with default configuration. | ||
1298 | * @sdev: si4713_device structure for the device we are communicating | ||
1299 | */ | ||
1300 | static int si4713_initialize(struct si4713_device *sdev) | ||
1301 | { | ||
1302 | int rval; | ||
1303 | |||
1304 | rval = si4713_set_power_state(sdev, POWER_ON); | ||
1305 | if (rval < 0) | ||
1306 | goto exit; | ||
1307 | |||
1308 | rval = si4713_checkrev(sdev); | ||
1309 | if (rval < 0) | ||
1310 | goto exit; | ||
1311 | |||
1312 | rval = si4713_set_power_state(sdev, POWER_OFF); | ||
1313 | if (rval < 0) | ||
1314 | goto exit; | ||
1315 | |||
1316 | mutex_lock(&sdev->mutex); | ||
1317 | |||
1318 | sdev->rds_info.pi = DEFAULT_RDS_PI; | ||
1319 | sdev->rds_info.pty = DEFAULT_RDS_PTY; | ||
1320 | sdev->rds_info.deviation = DEFAULT_RDS_DEVIATION; | ||
1321 | strlcpy(sdev->rds_info.ps_name, DEFAULT_RDS_PS_NAME, MAX_RDS_PS_NAME); | ||
1322 | strlcpy(sdev->rds_info.radio_text, DEFAULT_RDS_RADIO_TEXT, | ||
1323 | MAX_RDS_RADIO_TEXT); | ||
1324 | sdev->rds_info.enabled = 1; | ||
1325 | |||
1326 | sdev->limiter_info.release_time = DEFAULT_LIMITER_RTIME; | ||
1327 | sdev->limiter_info.deviation = DEFAULT_LIMITER_DEV; | ||
1328 | sdev->limiter_info.enabled = 1; | ||
1329 | |||
1330 | sdev->pilot_info.deviation = DEFAULT_PILOT_DEVIATION; | ||
1331 | sdev->pilot_info.frequency = DEFAULT_PILOT_FREQUENCY; | ||
1332 | sdev->pilot_info.enabled = 1; | ||
1333 | |||
1334 | sdev->acomp_info.release_time = DEFAULT_ACOMP_RTIME; | ||
1335 | sdev->acomp_info.attack_time = DEFAULT_ACOMP_ATIME; | ||
1336 | sdev->acomp_info.threshold = DEFAULT_ACOMP_THRESHOLD; | ||
1337 | sdev->acomp_info.gain = DEFAULT_ACOMP_GAIN; | ||
1338 | sdev->acomp_info.enabled = 1; | ||
1339 | |||
1340 | sdev->frequency = DEFAULT_FREQUENCY; | ||
1341 | sdev->preemphasis = DEFAULT_PREEMPHASIS; | ||
1342 | sdev->mute = DEFAULT_MUTE; | ||
1343 | sdev->power_level = DEFAULT_POWER_LEVEL; | ||
1344 | sdev->antenna_capacitor = 0; | ||
1345 | sdev->stereo = 1; | ||
1346 | sdev->tune_rnl = DEFAULT_TUNE_RNL; | ||
1347 | |||
1348 | mutex_unlock(&sdev->mutex); | ||
1349 | |||
1350 | exit: | ||
1351 | return rval; | ||
1352 | } | ||
1353 | |||
1354 | /* read string property */ | ||
1355 | static int si4713_read_econtrol_string(struct si4713_device *sdev, | ||
1356 | struct v4l2_ext_control *control) | ||
1357 | { | ||
1358 | s32 rval = 0; | ||
1359 | |||
1360 | switch (control->id) { | ||
1361 | case V4L2_CID_RDS_TX_PS_NAME: | ||
1362 | if (strlen(sdev->rds_info.ps_name) + 1 > control->size) { | ||
1363 | control->size = MAX_RDS_PS_NAME + 1; | ||
1364 | rval = -ENOSPC; | ||
1365 | goto exit; | ||
1366 | } | ||
1367 | rval = copy_to_user(control->string, sdev->rds_info.ps_name, | ||
1368 | strlen(sdev->rds_info.ps_name) + 1); | ||
1369 | break; | ||
1370 | |||
1371 | case V4L2_CID_RDS_TX_RADIO_TEXT: | ||
1372 | if (strlen(sdev->rds_info.radio_text) + 1 > control->size) { | ||
1373 | control->size = MAX_RDS_RADIO_TEXT + 1; | ||
1374 | rval = -ENOSPC; | ||
1375 | goto exit; | ||
1376 | } | ||
1377 | rval = copy_to_user(control->string, sdev->rds_info.radio_text, | ||
1378 | strlen(sdev->rds_info.radio_text) + 1); | ||
1379 | break; | ||
1380 | |||
1381 | default: | ||
1382 | rval = -EINVAL; | ||
1383 | break; | ||
1384 | }; | ||
1385 | |||
1386 | exit: | ||
1387 | return rval; | ||
1388 | } | ||
1389 | |||
1390 | /* | ||
1391 | * si4713_update_tune_status - update properties from tx_tune_status | ||
1392 | * command. Must be called with sdev->mutex held. | ||
1393 | * @sdev: si4713_device structure for the device we are communicating | ||
1394 | */ | ||
1395 | static int si4713_update_tune_status(struct si4713_device *sdev) | ||
1396 | { | ||
1397 | int rval; | ||
1398 | u16 f = 0; | ||
1399 | u8 p = 0, a = 0, n = 0; | ||
1400 | |||
1401 | rval = si4713_tx_tune_status(sdev, 0x00, &f, &p, &a, &n); | ||
1402 | |||
1403 | if (rval < 0) | ||
1404 | goto exit; | ||
1405 | |||
1406 | sdev->power_level = p; | ||
1407 | sdev->antenna_capacitor = a; | ||
1408 | sdev->tune_rnl = n; | ||
1409 | |||
1410 | exit: | ||
1411 | return rval; | ||
1412 | } | ||
1413 | |||
1414 | /* properties which use tx_tune_status */ | ||
1415 | static int si4713_read_econtrol_tune(struct si4713_device *sdev, | ||
1416 | struct v4l2_ext_control *control) | ||
1417 | { | ||
1418 | s32 rval = 0; | ||
1419 | |||
1420 | mutex_lock(&sdev->mutex); | ||
1421 | |||
1422 | if (sdev->power_state) { | ||
1423 | rval = si4713_update_tune_status(sdev); | ||
1424 | if (rval < 0) | ||
1425 | goto unlock; | ||
1426 | } | ||
1427 | |||
1428 | switch (control->id) { | ||
1429 | case V4L2_CID_TUNE_POWER_LEVEL: | ||
1430 | control->value = sdev->power_level; | ||
1431 | break; | ||
1432 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: | ||
1433 | control->value = sdev->antenna_capacitor; | ||
1434 | break; | ||
1435 | default: | ||
1436 | rval = -EINVAL; | ||
1437 | }; | ||
1438 | |||
1439 | unlock: | ||
1440 | mutex_unlock(&sdev->mutex); | ||
1441 | return rval; | ||
1442 | } | ||
1443 | |||
1444 | static int si4713_read_econtrol_integers(struct si4713_device *sdev, | ||
1445 | struct v4l2_ext_control *control) | ||
1446 | { | ||
1447 | s32 rval; | ||
1448 | u32 *shadow = NULL, val = 0; | ||
1449 | s32 bit = 0, mask = 0; | ||
1450 | u16 property = 0; | ||
1451 | int mul = 0; | ||
1452 | unsigned long *table = NULL; | ||
1453 | int size = 0; | ||
1454 | |||
1455 | rval = si4713_choose_econtrol_action(sdev, control->id, &shadow, &bit, | ||
1456 | &mask, &property, &mul, &table, &size); | ||
1457 | if (rval < 0) | ||
1458 | goto exit; | ||
1459 | |||
1460 | mutex_lock(&sdev->mutex); | ||
1461 | |||
1462 | if (sdev->power_state) { | ||
1463 | rval = si4713_read_property(sdev, property, &val); | ||
1464 | if (rval < 0) | ||
1465 | goto unlock; | ||
1466 | |||
1467 | /* Keep negative values for threshold */ | ||
1468 | if (control->id == V4L2_CID_AUDIO_COMPRESSION_THRESHOLD) | ||
1469 | *shadow = (s16)val; | ||
1470 | else if (mask) | ||
1471 | *shadow = get_status_bit(val, bit, mask); | ||
1472 | else if (mul) | ||
1473 | *shadow = val * mul; | ||
1474 | else | ||
1475 | *shadow = dev_to_usecs(val, table, size); | ||
1476 | } | ||
1477 | |||
1478 | control->value = *shadow; | ||
1479 | |||
1480 | unlock: | ||
1481 | mutex_unlock(&sdev->mutex); | ||
1482 | exit: | ||
1483 | return rval; | ||
1484 | } | ||
1485 | |||
1486 | /* | ||
1487 | * Video4Linux Subdev Interface | ||
1488 | */ | ||
1489 | /* si4713_s_ext_ctrls - set extended controls value */ | ||
1490 | static int si4713_s_ext_ctrls(struct v4l2_subdev *sd, | ||
1491 | struct v4l2_ext_controls *ctrls) | ||
1492 | { | ||
1493 | struct si4713_device *sdev = to_si4713_device(sd); | ||
1494 | int i; | ||
1495 | |||
1496 | if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) | ||
1497 | return -EINVAL; | ||
1498 | |||
1499 | for (i = 0; i < ctrls->count; i++) { | ||
1500 | int err; | ||
1501 | |||
1502 | switch ((ctrls->controls + i)->id) { | ||
1503 | case V4L2_CID_RDS_TX_PS_NAME: | ||
1504 | case V4L2_CID_RDS_TX_RADIO_TEXT: | ||
1505 | err = si4713_write_econtrol_string(sdev, | ||
1506 | ctrls->controls + i); | ||
1507 | break; | ||
1508 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: | ||
1509 | case V4L2_CID_TUNE_POWER_LEVEL: | ||
1510 | err = si4713_write_econtrol_tune(sdev, | ||
1511 | ctrls->controls + i); | ||
1512 | break; | ||
1513 | default: | ||
1514 | err = si4713_write_econtrol_integers(sdev, | ||
1515 | ctrls->controls + i); | ||
1516 | } | ||
1517 | |||
1518 | if (err < 0) { | ||
1519 | ctrls->error_idx = i; | ||
1520 | return err; | ||
1521 | } | ||
1522 | } | ||
1523 | |||
1524 | return 0; | ||
1525 | } | ||
1526 | |||
1527 | /* si4713_g_ext_ctrls - get extended controls value */ | ||
1528 | static int si4713_g_ext_ctrls(struct v4l2_subdev *sd, | ||
1529 | struct v4l2_ext_controls *ctrls) | ||
1530 | { | ||
1531 | struct si4713_device *sdev = to_si4713_device(sd); | ||
1532 | int i; | ||
1533 | |||
1534 | if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) | ||
1535 | return -EINVAL; | ||
1536 | |||
1537 | for (i = 0; i < ctrls->count; i++) { | ||
1538 | int err; | ||
1539 | |||
1540 | switch ((ctrls->controls + i)->id) { | ||
1541 | case V4L2_CID_RDS_TX_PS_NAME: | ||
1542 | case V4L2_CID_RDS_TX_RADIO_TEXT: | ||
1543 | err = si4713_read_econtrol_string(sdev, | ||
1544 | ctrls->controls + i); | ||
1545 | break; | ||
1546 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: | ||
1547 | case V4L2_CID_TUNE_POWER_LEVEL: | ||
1548 | err = si4713_read_econtrol_tune(sdev, | ||
1549 | ctrls->controls + i); | ||
1550 | break; | ||
1551 | default: | ||
1552 | err = si4713_read_econtrol_integers(sdev, | ||
1553 | ctrls->controls + i); | ||
1554 | } | ||
1555 | |||
1556 | if (err < 0) { | ||
1557 | ctrls->error_idx = i; | ||
1558 | return err; | ||
1559 | } | ||
1560 | } | ||
1561 | |||
1562 | return 0; | ||
1563 | } | ||
1564 | |||
1565 | /* si4713_queryctrl - enumerate control items */ | ||
1566 | static int si4713_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) | ||
1567 | { | ||
1568 | int rval = 0; | ||
1569 | |||
1570 | switch (qc->id) { | ||
1571 | /* User class controls */ | ||
1572 | case V4L2_CID_AUDIO_MUTE: | ||
1573 | rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, DEFAULT_MUTE); | ||
1574 | break; | ||
1575 | /* FM_TX class controls */ | ||
1576 | case V4L2_CID_RDS_TX_PI: | ||
1577 | rval = v4l2_ctrl_query_fill(qc, 0, 0xFFFF, 1, DEFAULT_RDS_PI); | ||
1578 | break; | ||
1579 | case V4L2_CID_RDS_TX_PTY: | ||
1580 | rval = v4l2_ctrl_query_fill(qc, 0, 31, 1, DEFAULT_RDS_PTY); | ||
1581 | break; | ||
1582 | case V4L2_CID_RDS_TX_DEVIATION: | ||
1583 | rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_DEVIATION, | ||
1584 | 10, DEFAULT_RDS_DEVIATION); | ||
1585 | break; | ||
1586 | case V4L2_CID_RDS_TX_PS_NAME: | ||
1587 | /* | ||
1588 | * Report step as 8. From RDS spec, psname | ||
1589 | * should be 8. But there are receivers which scroll strings | ||
1590 | * sized as 8xN. | ||
1591 | */ | ||
1592 | rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_PS_NAME, 8, 0); | ||
1593 | break; | ||
1594 | case V4L2_CID_RDS_TX_RADIO_TEXT: | ||
1595 | /* | ||
1596 | * Report step as 32 (2A block). From RDS spec, | ||
1597 | * radio text should be 32 for 2A block. But there are receivers | ||
1598 | * which scroll strings sized as 32xN. Setting default to 32. | ||
1599 | */ | ||
1600 | rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_RADIO_TEXT, 32, 0); | ||
1601 | break; | ||
1602 | |||
1603 | case V4L2_CID_AUDIO_LIMITER_ENABLED: | ||
1604 | rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | ||
1605 | break; | ||
1606 | case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: | ||
1607 | rval = v4l2_ctrl_query_fill(qc, 250, MAX_LIMITER_RELEASE_TIME, | ||
1608 | 50, DEFAULT_LIMITER_RTIME); | ||
1609 | break; | ||
1610 | case V4L2_CID_AUDIO_LIMITER_DEVIATION: | ||
1611 | rval = v4l2_ctrl_query_fill(qc, 0, MAX_LIMITER_DEVIATION, | ||
1612 | 10, DEFAULT_LIMITER_DEV); | ||
1613 | break; | ||
1614 | |||
1615 | case V4L2_CID_AUDIO_COMPRESSION_ENABLED: | ||
1616 | rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | ||
1617 | break; | ||
1618 | case V4L2_CID_AUDIO_COMPRESSION_GAIN: | ||
1619 | rval = v4l2_ctrl_query_fill(qc, 0, MAX_ACOMP_GAIN, 1, | ||
1620 | DEFAULT_ACOMP_GAIN); | ||
1621 | break; | ||
1622 | case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: | ||
1623 | rval = v4l2_ctrl_query_fill(qc, MIN_ACOMP_THRESHOLD, | ||
1624 | MAX_ACOMP_THRESHOLD, 1, | ||
1625 | DEFAULT_ACOMP_THRESHOLD); | ||
1626 | break; | ||
1627 | case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: | ||
1628 | rval = v4l2_ctrl_query_fill(qc, 0, MAX_ACOMP_ATTACK_TIME, | ||
1629 | 500, DEFAULT_ACOMP_ATIME); | ||
1630 | break; | ||
1631 | case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: | ||
1632 | rval = v4l2_ctrl_query_fill(qc, 100000, MAX_ACOMP_RELEASE_TIME, | ||
1633 | 100000, DEFAULT_ACOMP_RTIME); | ||
1634 | break; | ||
1635 | |||
1636 | case V4L2_CID_PILOT_TONE_ENABLED: | ||
1637 | rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | ||
1638 | break; | ||
1639 | case V4L2_CID_PILOT_TONE_DEVIATION: | ||
1640 | rval = v4l2_ctrl_query_fill(qc, 0, MAX_PILOT_DEVIATION, | ||
1641 | 10, DEFAULT_PILOT_DEVIATION); | ||
1642 | break; | ||
1643 | case V4L2_CID_PILOT_TONE_FREQUENCY: | ||
1644 | rval = v4l2_ctrl_query_fill(qc, 0, MAX_PILOT_FREQUENCY, | ||
1645 | 1, DEFAULT_PILOT_FREQUENCY); | ||
1646 | break; | ||
1647 | |||
1648 | case V4L2_CID_TUNE_PREEMPHASIS: | ||
1649 | rval = v4l2_ctrl_query_fill(qc, V4L2_PREEMPHASIS_DISABLED, | ||
1650 | V4L2_PREEMPHASIS_75_uS, 1, | ||
1651 | V4L2_PREEMPHASIS_50_uS); | ||
1652 | break; | ||
1653 | case V4L2_CID_TUNE_POWER_LEVEL: | ||
1654 | rval = v4l2_ctrl_query_fill(qc, 0, 120, 1, DEFAULT_POWER_LEVEL); | ||
1655 | break; | ||
1656 | case V4L2_CID_TUNE_ANTENNA_CAPACITOR: | ||
1657 | rval = v4l2_ctrl_query_fill(qc, 0, 191, 1, 0); | ||
1658 | break; | ||
1659 | default: | ||
1660 | rval = -EINVAL; | ||
1661 | break; | ||
1662 | }; | ||
1663 | |||
1664 | return rval; | ||
1665 | } | ||
1666 | |||
1667 | /* si4713_g_ctrl - get the value of a control */ | ||
1668 | static int si4713_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
1669 | { | ||
1670 | struct si4713_device *sdev = to_si4713_device(sd); | ||
1671 | int rval = 0; | ||
1672 | |||
1673 | if (!sdev) | ||
1674 | return -ENODEV; | ||
1675 | |||
1676 | mutex_lock(&sdev->mutex); | ||
1677 | |||
1678 | if (sdev->power_state) { | ||
1679 | rval = si4713_read_property(sdev, SI4713_TX_LINE_INPUT_MUTE, | ||
1680 | &sdev->mute); | ||
1681 | |||
1682 | if (rval < 0) | ||
1683 | goto unlock; | ||
1684 | } | ||
1685 | |||
1686 | switch (ctrl->id) { | ||
1687 | case V4L2_CID_AUDIO_MUTE: | ||
1688 | ctrl->value = get_mute(sdev->mute); | ||
1689 | break; | ||
1690 | } | ||
1691 | |||
1692 | unlock: | ||
1693 | mutex_unlock(&sdev->mutex); | ||
1694 | return rval; | ||
1695 | } | ||
1696 | |||
1697 | /* si4713_s_ctrl - set the value of a control */ | ||
1698 | static int si4713_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
1699 | { | ||
1700 | struct si4713_device *sdev = to_si4713_device(sd); | ||
1701 | int rval = 0; | ||
1702 | |||
1703 | if (!sdev) | ||
1704 | return -ENODEV; | ||
1705 | |||
1706 | switch (ctrl->id) { | ||
1707 | case V4L2_CID_AUDIO_MUTE: | ||
1708 | if (ctrl->value) { | ||
1709 | rval = si4713_set_mute(sdev, ctrl->value); | ||
1710 | if (rval < 0) | ||
1711 | goto exit; | ||
1712 | |||
1713 | rval = si4713_set_power_state(sdev, POWER_DOWN); | ||
1714 | } else { | ||
1715 | rval = si4713_set_power_state(sdev, POWER_UP); | ||
1716 | if (rval < 0) | ||
1717 | goto exit; | ||
1718 | |||
1719 | rval = si4713_setup(sdev); | ||
1720 | if (rval < 0) | ||
1721 | goto exit; | ||
1722 | |||
1723 | rval = si4713_set_mute(sdev, ctrl->value); | ||
1724 | } | ||
1725 | break; | ||
1726 | } | ||
1727 | |||
1728 | exit: | ||
1729 | return rval; | ||
1730 | } | ||
1731 | |||
1732 | /* si4713_ioctl - deal with private ioctls (only rnl for now) */ | ||
1733 | long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
1734 | { | ||
1735 | struct si4713_device *sdev = to_si4713_device(sd); | ||
1736 | struct si4713_rnl *rnl = arg; | ||
1737 | u16 frequency; | ||
1738 | int rval = 0; | ||
1739 | |||
1740 | if (!arg) | ||
1741 | return -EINVAL; | ||
1742 | |||
1743 | mutex_lock(&sdev->mutex); | ||
1744 | switch (cmd) { | ||
1745 | case SI4713_IOC_MEASURE_RNL: | ||
1746 | frequency = v4l2_to_si4713(rnl->frequency); | ||
1747 | |||
1748 | if (sdev->power_state) { | ||
1749 | /* Set desired measurement frequency */ | ||
1750 | rval = si4713_tx_tune_measure(sdev, frequency, 0); | ||
1751 | if (rval < 0) | ||
1752 | goto unlock; | ||
1753 | /* get results from tune status */ | ||
1754 | rval = si4713_update_tune_status(sdev); | ||
1755 | if (rval < 0) | ||
1756 | goto unlock; | ||
1757 | } | ||
1758 | rnl->rnl = sdev->tune_rnl; | ||
1759 | break; | ||
1760 | |||
1761 | default: | ||
1762 | /* nothing */ | ||
1763 | rval = -ENOIOCTLCMD; | ||
1764 | } | ||
1765 | |||
1766 | unlock: | ||
1767 | mutex_unlock(&sdev->mutex); | ||
1768 | return rval; | ||
1769 | } | ||
1770 | |||
1771 | static const struct v4l2_subdev_core_ops si4713_subdev_core_ops = { | ||
1772 | .queryctrl = si4713_queryctrl, | ||
1773 | .g_ext_ctrls = si4713_g_ext_ctrls, | ||
1774 | .s_ext_ctrls = si4713_s_ext_ctrls, | ||
1775 | .g_ctrl = si4713_g_ctrl, | ||
1776 | .s_ctrl = si4713_s_ctrl, | ||
1777 | .ioctl = si4713_ioctl, | ||
1778 | }; | ||
1779 | |||
1780 | /* si4713_g_modulator - get modulator attributes */ | ||
1781 | static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) | ||
1782 | { | ||
1783 | struct si4713_device *sdev = to_si4713_device(sd); | ||
1784 | int rval = 0; | ||
1785 | |||
1786 | if (!sdev) { | ||
1787 | rval = -ENODEV; | ||
1788 | goto exit; | ||
1789 | } | ||
1790 | |||
1791 | if (vm->index > 0) { | ||
1792 | rval = -EINVAL; | ||
1793 | goto exit; | ||
1794 | } | ||
1795 | |||
1796 | strncpy(vm->name, "FM Modulator", 32); | ||
1797 | vm->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW | | ||
1798 | V4L2_TUNER_CAP_RDS; | ||
1799 | |||
1800 | /* Report current frequency range limits */ | ||
1801 | vm->rangelow = si4713_to_v4l2(FREQ_RANGE_LOW); | ||
1802 | vm->rangehigh = si4713_to_v4l2(FREQ_RANGE_HIGH); | ||
1803 | |||
1804 | mutex_lock(&sdev->mutex); | ||
1805 | |||
1806 | if (sdev->power_state) { | ||
1807 | u32 comp_en = 0; | ||
1808 | |||
1809 | rval = si4713_read_property(sdev, SI4713_TX_COMPONENT_ENABLE, | ||
1810 | &comp_en); | ||
1811 | if (rval < 0) | ||
1812 | goto unlock; | ||
1813 | |||
1814 | sdev->stereo = get_status_bit(comp_en, 1, 1 << 1); | ||
1815 | sdev->rds_info.enabled = get_status_bit(comp_en, 2, 1 << 2); | ||
1816 | } | ||
1817 | |||
1818 | /* Report current audio mode: mono or stereo */ | ||
1819 | if (sdev->stereo) | ||
1820 | vm->txsubchans = V4L2_TUNER_SUB_STEREO; | ||
1821 | else | ||
1822 | vm->txsubchans = V4L2_TUNER_SUB_MONO; | ||
1823 | |||
1824 | /* Report rds feature status */ | ||
1825 | if (sdev->rds_info.enabled) | ||
1826 | vm->txsubchans |= V4L2_TUNER_SUB_RDS; | ||
1827 | else | ||
1828 | vm->txsubchans &= ~V4L2_TUNER_SUB_RDS; | ||
1829 | |||
1830 | unlock: | ||
1831 | mutex_unlock(&sdev->mutex); | ||
1832 | exit: | ||
1833 | return rval; | ||
1834 | } | ||
1835 | |||
1836 | /* si4713_s_modulator - set modulator attributes */ | ||
1837 | static int si4713_s_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) | ||
1838 | { | ||
1839 | struct si4713_device *sdev = to_si4713_device(sd); | ||
1840 | int rval = 0; | ||
1841 | u16 stereo, rds; | ||
1842 | u32 p; | ||
1843 | |||
1844 | if (!sdev) { | ||
1845 | rval = -ENODEV; | ||
1846 | goto exit; | ||
1847 | } | ||
1848 | |||
1849 | if (vm->index > 0) { | ||
1850 | rval = -EINVAL; | ||
1851 | goto exit; | ||
1852 | } | ||
1853 | |||
1854 | /* Set audio mode: mono or stereo */ | ||
1855 | if (vm->txsubchans & V4L2_TUNER_SUB_STEREO) | ||
1856 | stereo = 1; | ||
1857 | else if (vm->txsubchans & V4L2_TUNER_SUB_MONO) | ||
1858 | stereo = 0; | ||
1859 | else | ||
1860 | rval = -EINVAL; | ||
1861 | if (rval < 0) | ||
1862 | goto exit; | ||
1863 | |||
1864 | rds = !!(vm->txsubchans & V4L2_TUNER_SUB_RDS); | ||
1865 | |||
1866 | mutex_lock(&sdev->mutex); | ||
1867 | |||
1868 | if (sdev->power_state) { | ||
1869 | rval = si4713_read_property(sdev, | ||
1870 | SI4713_TX_COMPONENT_ENABLE, &p); | ||
1871 | if (rval < 0) | ||
1872 | goto unlock; | ||
1873 | |||
1874 | p = set_bits(p, stereo, 1, 1 << 1); | ||
1875 | p = set_bits(p, rds, 2, 1 << 2); | ||
1876 | |||
1877 | rval = si4713_write_property(sdev, | ||
1878 | SI4713_TX_COMPONENT_ENABLE, p); | ||
1879 | if (rval < 0) | ||
1880 | goto unlock; | ||
1881 | } | ||
1882 | |||
1883 | sdev->stereo = stereo; | ||
1884 | sdev->rds_info.enabled = rds; | ||
1885 | |||
1886 | unlock: | ||
1887 | mutex_unlock(&sdev->mutex); | ||
1888 | exit: | ||
1889 | return rval; | ||
1890 | } | ||
1891 | |||
1892 | /* si4713_g_frequency - get tuner or modulator radio frequency */ | ||
1893 | static int si4713_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) | ||
1894 | { | ||
1895 | struct si4713_device *sdev = to_si4713_device(sd); | ||
1896 | int rval = 0; | ||
1897 | |||
1898 | f->type = V4L2_TUNER_RADIO; | ||
1899 | |||
1900 | mutex_lock(&sdev->mutex); | ||
1901 | |||
1902 | if (sdev->power_state) { | ||
1903 | u16 freq; | ||
1904 | u8 p, a, n; | ||
1905 | |||
1906 | rval = si4713_tx_tune_status(sdev, 0x00, &freq, &p, &a, &n); | ||
1907 | if (rval < 0) | ||
1908 | goto unlock; | ||
1909 | |||
1910 | sdev->frequency = freq; | ||
1911 | } | ||
1912 | |||
1913 | f->frequency = si4713_to_v4l2(sdev->frequency); | ||
1914 | |||
1915 | unlock: | ||
1916 | mutex_unlock(&sdev->mutex); | ||
1917 | return rval; | ||
1918 | } | ||
1919 | |||
1920 | /* si4713_s_frequency - set tuner or modulator radio frequency */ | ||
1921 | static int si4713_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) | ||
1922 | { | ||
1923 | struct si4713_device *sdev = to_si4713_device(sd); | ||
1924 | int rval = 0; | ||
1925 | u16 frequency = v4l2_to_si4713(f->frequency); | ||
1926 | |||
1927 | /* Check frequency range */ | ||
1928 | if (frequency < FREQ_RANGE_LOW || frequency > FREQ_RANGE_HIGH) | ||
1929 | return -EDOM; | ||
1930 | |||
1931 | mutex_lock(&sdev->mutex); | ||
1932 | |||
1933 | if (sdev->power_state) { | ||
1934 | rval = si4713_tx_tune_freq(sdev, frequency); | ||
1935 | if (rval < 0) | ||
1936 | goto unlock; | ||
1937 | frequency = rval; | ||
1938 | rval = 0; | ||
1939 | } | ||
1940 | sdev->frequency = frequency; | ||
1941 | f->frequency = si4713_to_v4l2(frequency); | ||
1942 | |||
1943 | unlock: | ||
1944 | mutex_unlock(&sdev->mutex); | ||
1945 | return rval; | ||
1946 | } | ||
1947 | |||
1948 | static const struct v4l2_subdev_tuner_ops si4713_subdev_tuner_ops = { | ||
1949 | .g_frequency = si4713_g_frequency, | ||
1950 | .s_frequency = si4713_s_frequency, | ||
1951 | .g_modulator = si4713_g_modulator, | ||
1952 | .s_modulator = si4713_s_modulator, | ||
1953 | }; | ||
1954 | |||
1955 | static const struct v4l2_subdev_ops si4713_subdev_ops = { | ||
1956 | .core = &si4713_subdev_core_ops, | ||
1957 | .tuner = &si4713_subdev_tuner_ops, | ||
1958 | }; | ||
1959 | |||
1960 | /* | ||
1961 | * I2C driver interface | ||
1962 | */ | ||
1963 | /* si4713_probe - probe for the device */ | ||
1964 | static int si4713_probe(struct i2c_client *client, | ||
1965 | const struct i2c_device_id *id) | ||
1966 | { | ||
1967 | struct si4713_device *sdev; | ||
1968 | int rval; | ||
1969 | |||
1970 | sdev = kzalloc(sizeof *sdev, GFP_KERNEL); | ||
1971 | if (!sdev) { | ||
1972 | dev_err(&client->dev, "Failed to alloc video device.\n"); | ||
1973 | rval = -ENOMEM; | ||
1974 | goto exit; | ||
1975 | } | ||
1976 | |||
1977 | sdev->platform_data = client->dev.platform_data; | ||
1978 | if (!sdev->platform_data) { | ||
1979 | v4l2_err(&sdev->sd, "No platform data registered.\n"); | ||
1980 | rval = -ENODEV; | ||
1981 | goto free_sdev; | ||
1982 | } | ||
1983 | |||
1984 | v4l2_i2c_subdev_init(&sdev->sd, client, &si4713_subdev_ops); | ||
1985 | |||
1986 | mutex_init(&sdev->mutex); | ||
1987 | init_completion(&sdev->work); | ||
1988 | |||
1989 | if (client->irq) { | ||
1990 | rval = request_irq(client->irq, | ||
1991 | si4713_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED, | ||
1992 | client->name, sdev); | ||
1993 | if (rval < 0) { | ||
1994 | v4l2_err(&sdev->sd, "Could not request IRQ\n"); | ||
1995 | goto free_sdev; | ||
1996 | } | ||
1997 | v4l2_dbg(1, debug, &sdev->sd, "IRQ requested.\n"); | ||
1998 | } else { | ||
1999 | v4l2_warn(&sdev->sd, "IRQ not configured. Using timeouts.\n"); | ||
2000 | } | ||
2001 | |||
2002 | rval = si4713_initialize(sdev); | ||
2003 | if (rval < 0) { | ||
2004 | v4l2_err(&sdev->sd, "Failed to probe device information.\n"); | ||
2005 | goto free_irq; | ||
2006 | } | ||
2007 | |||
2008 | return 0; | ||
2009 | |||
2010 | free_irq: | ||
2011 | if (client->irq) | ||
2012 | free_irq(client->irq, sdev); | ||
2013 | free_sdev: | ||
2014 | kfree(sdev); | ||
2015 | exit: | ||
2016 | return rval; | ||
2017 | } | ||
2018 | |||
2019 | /* si4713_remove - remove the device */ | ||
2020 | static int si4713_remove(struct i2c_client *client) | ||
2021 | { | ||
2022 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
2023 | struct si4713_device *sdev = to_si4713_device(sd); | ||
2024 | |||
2025 | if (sdev->power_state) | ||
2026 | si4713_set_power_state(sdev, POWER_DOWN); | ||
2027 | |||
2028 | if (client->irq > 0) | ||
2029 | free_irq(client->irq, sdev); | ||
2030 | |||
2031 | v4l2_device_unregister_subdev(sd); | ||
2032 | |||
2033 | kfree(sdev); | ||
2034 | |||
2035 | return 0; | ||
2036 | } | ||
2037 | |||
2038 | /* si4713_i2c_driver - i2c driver interface */ | ||
2039 | static const struct i2c_device_id si4713_id[] = { | ||
2040 | { "si4713" , 0 }, | ||
2041 | { }, | ||
2042 | }; | ||
2043 | MODULE_DEVICE_TABLE(i2c, si4713_id); | ||
2044 | |||
2045 | static struct i2c_driver si4713_i2c_driver = { | ||
2046 | .driver = { | ||
2047 | .name = "si4713", | ||
2048 | }, | ||
2049 | .probe = si4713_probe, | ||
2050 | .remove = si4713_remove, | ||
2051 | .id_table = si4713_id, | ||
2052 | }; | ||
2053 | |||
2054 | /* Module Interface */ | ||
2055 | static int __init si4713_module_init(void) | ||
2056 | { | ||
2057 | return i2c_add_driver(&si4713_i2c_driver); | ||
2058 | } | ||
2059 | |||
2060 | static void __exit si4713_module_exit(void) | ||
2061 | { | ||
2062 | i2c_del_driver(&si4713_i2c_driver); | ||
2063 | } | ||
2064 | |||
2065 | module_init(si4713_module_init); | ||
2066 | module_exit(si4713_module_exit); | ||
2067 | |||
diff --git a/drivers/media/radio/si4713-i2c.h b/drivers/media/radio/si4713-i2c.h new file mode 100644 index 000000000000..faf8cff124f1 --- /dev/null +++ b/drivers/media/radio/si4713-i2c.h | |||
@@ -0,0 +1,237 @@ | |||
1 | /* | ||
2 | * drivers/media/radio/si4713-i2c.h | ||
3 | * | ||
4 | * Property and commands definitions for Si4713 radio transmitter chip. | ||
5 | * | ||
6 | * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT | ||
7 | * Contact: Eduardo Valentin <eduardo.valentin@nokia.com> | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public License | ||
10 | * version 2. This program is licensed "as is" without any warranty of any | ||
11 | * kind, whether express or implied. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef SI4713_I2C_H | ||
16 | #define SI4713_I2C_H | ||
17 | |||
18 | #include <media/v4l2-subdev.h> | ||
19 | #include <media/si4713.h> | ||
20 | |||
21 | #define SI4713_PRODUCT_NUMBER 0x0D | ||
22 | |||
23 | /* Command Timeouts */ | ||
24 | #define DEFAULT_TIMEOUT 500 | ||
25 | #define TIMEOUT_SET_PROPERTY 20 | ||
26 | #define TIMEOUT_TX_TUNE_POWER 30000 | ||
27 | #define TIMEOUT_TX_TUNE 110000 | ||
28 | #define TIMEOUT_POWER_UP 200000 | ||
29 | |||
30 | /* | ||
31 | * Command and its arguments definitions | ||
32 | */ | ||
33 | #define SI4713_PWUP_CTSIEN (1<<7) | ||
34 | #define SI4713_PWUP_GPO2OEN (1<<6) | ||
35 | #define SI4713_PWUP_PATCH (1<<5) | ||
36 | #define SI4713_PWUP_XOSCEN (1<<4) | ||
37 | #define SI4713_PWUP_FUNC_TX 0x02 | ||
38 | #define SI4713_PWUP_FUNC_PATCH 0x0F | ||
39 | #define SI4713_PWUP_OPMOD_ANALOG 0x50 | ||
40 | #define SI4713_PWUP_OPMOD_DIGITAL 0x0F | ||
41 | #define SI4713_PWUP_NARGS 2 | ||
42 | #define SI4713_PWUP_NRESP 1 | ||
43 | #define SI4713_CMD_POWER_UP 0x01 | ||
44 | |||
45 | #define SI4713_GETREV_NRESP 9 | ||
46 | #define SI4713_CMD_GET_REV 0x10 | ||
47 | |||
48 | #define SI4713_PWDN_NRESP 1 | ||
49 | #define SI4713_CMD_POWER_DOWN 0x11 | ||
50 | |||
51 | #define SI4713_SET_PROP_NARGS 5 | ||
52 | #define SI4713_SET_PROP_NRESP 1 | ||
53 | #define SI4713_CMD_SET_PROPERTY 0x12 | ||
54 | |||
55 | #define SI4713_GET_PROP_NARGS 3 | ||
56 | #define SI4713_GET_PROP_NRESP 4 | ||
57 | #define SI4713_CMD_GET_PROPERTY 0x13 | ||
58 | |||
59 | #define SI4713_GET_STATUS_NRESP 1 | ||
60 | #define SI4713_CMD_GET_INT_STATUS 0x14 | ||
61 | |||
62 | #define SI4713_CMD_PATCH_ARGS 0x15 | ||
63 | #define SI4713_CMD_PATCH_DATA 0x16 | ||
64 | |||
65 | #define SI4713_MAX_FREQ 10800 | ||
66 | #define SI4713_MIN_FREQ 7600 | ||
67 | #define SI4713_TXFREQ_NARGS 3 | ||
68 | #define SI4713_TXFREQ_NRESP 1 | ||
69 | #define SI4713_CMD_TX_TUNE_FREQ 0x30 | ||
70 | |||
71 | #define SI4713_MAX_POWER 120 | ||
72 | #define SI4713_MIN_POWER 88 | ||
73 | #define SI4713_MAX_ANTCAP 191 | ||
74 | #define SI4713_MIN_ANTCAP 0 | ||
75 | #define SI4713_TXPWR_NARGS 4 | ||
76 | #define SI4713_TXPWR_NRESP 1 | ||
77 | #define SI4713_CMD_TX_TUNE_POWER 0x31 | ||
78 | |||
79 | #define SI4713_TXMEA_NARGS 4 | ||
80 | #define SI4713_TXMEA_NRESP 1 | ||
81 | #define SI4713_CMD_TX_TUNE_MEASURE 0x32 | ||
82 | |||
83 | #define SI4713_INTACK_MASK 0x01 | ||
84 | #define SI4713_TXSTATUS_NARGS 1 | ||
85 | #define SI4713_TXSTATUS_NRESP 8 | ||
86 | #define SI4713_CMD_TX_TUNE_STATUS 0x33 | ||
87 | |||
88 | #define SI4713_OVERMOD_BIT (1 << 2) | ||
89 | #define SI4713_IALH_BIT (1 << 1) | ||
90 | #define SI4713_IALL_BIT (1 << 0) | ||
91 | #define SI4713_ASQSTATUS_NARGS 1 | ||
92 | #define SI4713_ASQSTATUS_NRESP 5 | ||
93 | #define SI4713_CMD_TX_ASQ_STATUS 0x34 | ||
94 | |||
95 | #define SI4713_RDSBUFF_MODE_MASK 0x87 | ||
96 | #define SI4713_RDSBUFF_NARGS 7 | ||
97 | #define SI4713_RDSBUFF_NRESP 6 | ||
98 | #define SI4713_CMD_TX_RDS_BUFF 0x35 | ||
99 | |||
100 | #define SI4713_RDSPS_PSID_MASK 0x1F | ||
101 | #define SI4713_RDSPS_NARGS 5 | ||
102 | #define SI4713_RDSPS_NRESP 1 | ||
103 | #define SI4713_CMD_TX_RDS_PS 0x36 | ||
104 | |||
105 | #define SI4713_CMD_GPO_CTL 0x80 | ||
106 | #define SI4713_CMD_GPO_SET 0x81 | ||
107 | |||
108 | /* | ||
109 | * Bits from status response | ||
110 | */ | ||
111 | #define SI4713_CTS (1<<7) | ||
112 | #define SI4713_ERR (1<<6) | ||
113 | #define SI4713_RDS_INT (1<<2) | ||
114 | #define SI4713_ASQ_INT (1<<1) | ||
115 | #define SI4713_STC_INT (1<<0) | ||
116 | |||
117 | /* | ||
118 | * Property definitions | ||
119 | */ | ||
120 | #define SI4713_GPO_IEN 0x0001 | ||
121 | #define SI4713_DIG_INPUT_FORMAT 0x0101 | ||
122 | #define SI4713_DIG_INPUT_SAMPLE_RATE 0x0103 | ||
123 | #define SI4713_REFCLK_FREQ 0x0201 | ||
124 | #define SI4713_REFCLK_PRESCALE 0x0202 | ||
125 | #define SI4713_TX_COMPONENT_ENABLE 0x2100 | ||
126 | #define SI4713_TX_AUDIO_DEVIATION 0x2101 | ||
127 | #define SI4713_TX_PILOT_DEVIATION 0x2102 | ||
128 | #define SI4713_TX_RDS_DEVIATION 0x2103 | ||
129 | #define SI4713_TX_LINE_INPUT_LEVEL 0x2104 | ||
130 | #define SI4713_TX_LINE_INPUT_MUTE 0x2105 | ||
131 | #define SI4713_TX_PREEMPHASIS 0x2106 | ||
132 | #define SI4713_TX_PILOT_FREQUENCY 0x2107 | ||
133 | #define SI4713_TX_ACOMP_ENABLE 0x2200 | ||
134 | #define SI4713_TX_ACOMP_THRESHOLD 0x2201 | ||
135 | #define SI4713_TX_ACOMP_ATTACK_TIME 0x2202 | ||
136 | #define SI4713_TX_ACOMP_RELEASE_TIME 0x2203 | ||
137 | #define SI4713_TX_ACOMP_GAIN 0x2204 | ||
138 | #define SI4713_TX_LIMITER_RELEASE_TIME 0x2205 | ||
139 | #define SI4713_TX_ASQ_INTERRUPT_SOURCE 0x2300 | ||
140 | #define SI4713_TX_ASQ_LEVEL_LOW 0x2301 | ||
141 | #define SI4713_TX_ASQ_DURATION_LOW 0x2302 | ||
142 | #define SI4713_TX_ASQ_LEVEL_HIGH 0x2303 | ||
143 | #define SI4713_TX_ASQ_DURATION_HIGH 0x2304 | ||
144 | #define SI4713_TX_RDS_INTERRUPT_SOURCE 0x2C00 | ||
145 | #define SI4713_TX_RDS_PI 0x2C01 | ||
146 | #define SI4713_TX_RDS_PS_MIX 0x2C02 | ||
147 | #define SI4713_TX_RDS_PS_MISC 0x2C03 | ||
148 | #define SI4713_TX_RDS_PS_REPEAT_COUNT 0x2C04 | ||
149 | #define SI4713_TX_RDS_PS_MESSAGE_COUNT 0x2C05 | ||
150 | #define SI4713_TX_RDS_PS_AF 0x2C06 | ||
151 | #define SI4713_TX_RDS_FIFO_SIZE 0x2C07 | ||
152 | |||
153 | #define PREEMPHASIS_USA 75 | ||
154 | #define PREEMPHASIS_EU 50 | ||
155 | #define PREEMPHASIS_DISABLED 0 | ||
156 | #define FMPE_USA 0x00 | ||
157 | #define FMPE_EU 0x01 | ||
158 | #define FMPE_DISABLED 0x02 | ||
159 | |||
160 | #define POWER_UP 0x01 | ||
161 | #define POWER_DOWN 0x00 | ||
162 | |||
163 | struct rds_info { | ||
164 | u32 pi; | ||
165 | #define MAX_RDS_PTY 31 | ||
166 | u32 pty; | ||
167 | #define MAX_RDS_DEVIATION 90000 | ||
168 | u32 deviation; | ||
169 | /* | ||
170 | * PSNAME is known to be defined as 8 character sized (RDS Spec). | ||
171 | * However, there is receivers which scroll PSNAME 8xN sized. | ||
172 | */ | ||
173 | #define MAX_RDS_PS_NAME 96 | ||
174 | u8 ps_name[MAX_RDS_PS_NAME + 1]; | ||
175 | /* | ||
176 | * MAX_RDS_RADIO_TEXT is known to be defined as 32 (2A group) or 64 (2B group) | ||
177 | * character sized (RDS Spec). | ||
178 | * However, there is receivers which scroll them as well. | ||
179 | */ | ||
180 | #define MAX_RDS_RADIO_TEXT 384 | ||
181 | u8 radio_text[MAX_RDS_RADIO_TEXT + 1]; | ||
182 | u32 enabled; | ||
183 | }; | ||
184 | |||
185 | struct limiter_info { | ||
186 | #define MAX_LIMITER_RELEASE_TIME 102390 | ||
187 | u32 release_time; | ||
188 | #define MAX_LIMITER_DEVIATION 90000 | ||
189 | u32 deviation; | ||
190 | u32 enabled; | ||
191 | }; | ||
192 | |||
193 | struct pilot_info { | ||
194 | #define MAX_PILOT_DEVIATION 90000 | ||
195 | u32 deviation; | ||
196 | #define MAX_PILOT_FREQUENCY 19000 | ||
197 | u32 frequency; | ||
198 | u32 enabled; | ||
199 | }; | ||
200 | |||
201 | struct acomp_info { | ||
202 | #define MAX_ACOMP_RELEASE_TIME 1000000 | ||
203 | u32 release_time; | ||
204 | #define MAX_ACOMP_ATTACK_TIME 5000 | ||
205 | u32 attack_time; | ||
206 | #define MAX_ACOMP_THRESHOLD 0 | ||
207 | #define MIN_ACOMP_THRESHOLD (-40) | ||
208 | s32 threshold; | ||
209 | #define MAX_ACOMP_GAIN 20 | ||
210 | u32 gain; | ||
211 | u32 enabled; | ||
212 | }; | ||
213 | |||
214 | /* | ||
215 | * si4713_device - private data | ||
216 | */ | ||
217 | struct si4713_device { | ||
218 | /* v4l2_subdev and i2c reference (v4l2_subdev priv data) */ | ||
219 | struct v4l2_subdev sd; | ||
220 | /* private data structures */ | ||
221 | struct mutex mutex; | ||
222 | struct completion work; | ||
223 | struct si4713_platform_data *platform_data; | ||
224 | struct rds_info rds_info; | ||
225 | struct limiter_info limiter_info; | ||
226 | struct pilot_info pilot_info; | ||
227 | struct acomp_info acomp_info; | ||
228 | u32 frequency; | ||
229 | u32 preemphasis; | ||
230 | u32 mute; | ||
231 | u32 power_level; | ||
232 | u32 power_state; | ||
233 | u32 antenna_capacitor; | ||
234 | u32 stereo; | ||
235 | u32 tune_rnl; | ||
236 | }; | ||
237 | #endif /* ifndef SI4713_I2C_H */ | ||