Commit | Line | Data |
---|---|---|
8e8e69d6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
730a30ab AS |
2 | /* |
3 | * drivers/mfd/si476x-i2c.c -- Core device driver for si476x MFD | |
4 | * device | |
5 | * | |
6 | * Copyright (C) 2012 Innovative Converged Devices(ICD) | |
7 | * Copyright (C) 2013 Andrey Smirnov | |
8 | * | |
9 | * Author: Andrey Smirnov <andrew.smirnov@gmail.com> | |
730a30ab AS |
10 | */ |
11 | #include <linux/module.h> | |
12 | ||
13 | #include <linux/slab.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/gpio.h> | |
17 | #include <linux/regulator/consumer.h> | |
18 | #include <linux/i2c.h> | |
19 | #include <linux/err.h> | |
20 | ||
21 | #include <linux/mfd/si476x-core.h> | |
22 | ||
23 | #define SI476X_MAX_IO_ERRORS 10 | |
24 | #define SI476X_DRIVER_RDS_FIFO_DEPTH 128 | |
25 | ||
26 | /** | |
27 | * si476x_core_config_pinmux() - pin function configuration function | |
28 | * | |
29 | * @core: Core device structure | |
30 | * | |
31 | * Configure the functions of the pins of the radio chip. | |
32 | * | |
33 | * The function returns zero in case of succes or negative error code | |
34 | * otherwise. | |
35 | */ | |
36 | static int si476x_core_config_pinmux(struct si476x_core *core) | |
37 | { | |
38 | int err; | |
39 | dev_dbg(&core->client->dev, "Configuring pinmux\n"); | |
40 | err = si476x_core_cmd_dig_audio_pin_cfg(core, | |
41 | core->pinmux.dclk, | |
42 | core->pinmux.dfs, | |
43 | core->pinmux.dout, | |
44 | core->pinmux.xout); | |
45 | if (err < 0) { | |
46 | dev_err(&core->client->dev, | |
47 | "Failed to configure digital audio pins(err = %d)\n", | |
48 | err); | |
49 | return err; | |
50 | } | |
51 | ||
52 | err = si476x_core_cmd_zif_pin_cfg(core, | |
53 | core->pinmux.iqclk, | |
54 | core->pinmux.iqfs, | |
55 | core->pinmux.iout, | |
56 | core->pinmux.qout); | |
57 | if (err < 0) { | |
58 | dev_err(&core->client->dev, | |
59 | "Failed to configure ZIF pins(err = %d)\n", | |
60 | err); | |
61 | return err; | |
62 | } | |
63 | ||
64 | err = si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(core, | |
65 | core->pinmux.icin, | |
66 | core->pinmux.icip, | |
67 | core->pinmux.icon, | |
68 | core->pinmux.icop); | |
69 | if (err < 0) { | |
70 | dev_err(&core->client->dev, | |
71 | "Failed to configure IC-Link/GPO pins(err = %d)\n", | |
72 | err); | |
73 | return err; | |
74 | } | |
75 | ||
76 | err = si476x_core_cmd_ana_audio_pin_cfg(core, | |
77 | core->pinmux.lrout); | |
78 | if (err < 0) { | |
79 | dev_err(&core->client->dev, | |
80 | "Failed to configure analog audio pins(err = %d)\n", | |
81 | err); | |
82 | return err; | |
83 | } | |
84 | ||
85 | err = si476x_core_cmd_intb_pin_cfg(core, | |
86 | core->pinmux.intb, | |
87 | core->pinmux.a1); | |
88 | if (err < 0) { | |
89 | dev_err(&core->client->dev, | |
90 | "Failed to configure interrupt pins(err = %d)\n", | |
91 | err); | |
92 | return err; | |
93 | } | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | static inline void si476x_core_schedule_polling_work(struct si476x_core *core) | |
99 | { | |
100 | schedule_delayed_work(&core->status_monitor, | |
101 | usecs_to_jiffies(SI476X_STATUS_POLL_US)); | |
102 | } | |
103 | ||
104 | /** | |
105 | * si476x_core_start() - early chip startup function | |
106 | * @core: Core device structure | |
107 | * @soft: When set, this flag forces "soft" startup, where "soft" | |
108 | * power down is the one done by sending appropriate command instead | |
109 | * of using reset pin of the tuner | |
110 | * | |
111 | * Perform required startup sequence to correctly power | |
112 | * up the chip and perform initial configuration. It does the | |
113 | * following sequence of actions: | |
114 | * 1. Claims and enables the power supplies VD and VIO1 required | |
115 | * for I2C interface of the chip operation. | |
116 | * 2. Waits for 100us, pulls the reset line up, enables irq, | |
117 | * waits for another 100us as it is specified by the | |
118 | * datasheet. | |
119 | * 3. Sends 'POWER_UP' command to the device with all provided | |
120 | * information about power-up parameters. | |
121 | * 4. Configures, pin multiplexor, disables digital audio and | |
122 | * configures interrupt sources. | |
123 | * | |
124 | * The function returns zero in case of succes or negative error code | |
125 | * otherwise. | |
126 | */ | |
127 | int si476x_core_start(struct si476x_core *core, bool soft) | |
128 | { | |
129 | struct i2c_client *client = core->client; | |
130 | int err; | |
131 | ||
132 | if (!soft) { | |
133 | if (gpio_is_valid(core->gpio_reset)) | |
134 | gpio_set_value_cansleep(core->gpio_reset, 1); | |
135 | ||
136 | if (client->irq) | |
137 | enable_irq(client->irq); | |
138 | ||
139 | udelay(100); | |
140 | ||
141 | if (!client->irq) { | |
142 | atomic_set(&core->is_alive, 1); | |
143 | si476x_core_schedule_polling_work(core); | |
144 | } | |
145 | } else { | |
146 | if (client->irq) | |
147 | enable_irq(client->irq); | |
148 | else { | |
149 | atomic_set(&core->is_alive, 1); | |
150 | si476x_core_schedule_polling_work(core); | |
151 | } | |
152 | } | |
153 | ||
154 | err = si476x_core_cmd_power_up(core, | |
155 | &core->power_up_parameters); | |
156 | ||
157 | if (err < 0) { | |
158 | dev_err(&core->client->dev, | |
159 | "Power up failure(err = %d)\n", | |
160 | err); | |
161 | goto disable_irq; | |
162 | } | |
163 | ||
164 | if (client->irq) | |
165 | atomic_set(&core->is_alive, 1); | |
166 | ||
167 | err = si476x_core_config_pinmux(core); | |
168 | if (err < 0) { | |
169 | dev_err(&core->client->dev, | |
170 | "Failed to configure pinmux(err = %d)\n", | |
171 | err); | |
172 | goto disable_irq; | |
173 | } | |
174 | ||
175 | if (client->irq) { | |
176 | err = regmap_write(core->regmap, | |
177 | SI476X_PROP_INT_CTL_ENABLE, | |
178 | SI476X_RDSIEN | | |
179 | SI476X_STCIEN | | |
180 | SI476X_CTSIEN); | |
181 | if (err < 0) { | |
182 | dev_err(&core->client->dev, | |
183 | "Failed to configure interrupt sources" | |
184 | "(err = %d)\n", err); | |
185 | goto disable_irq; | |
186 | } | |
187 | } | |
188 | ||
189 | return 0; | |
190 | ||
191 | disable_irq: | |
192 | if (err == -ENODEV) | |
193 | atomic_set(&core->is_alive, 0); | |
194 | ||
195 | if (client->irq) | |
196 | disable_irq(client->irq); | |
197 | else | |
198 | cancel_delayed_work_sync(&core->status_monitor); | |
199 | ||
200 | if (gpio_is_valid(core->gpio_reset)) | |
201 | gpio_set_value_cansleep(core->gpio_reset, 0); | |
202 | ||
203 | return err; | |
204 | } | |
205 | EXPORT_SYMBOL_GPL(si476x_core_start); | |
206 | ||
207 | /** | |
208 | * si476x_core_stop() - chip power-down function | |
209 | * @core: Core device structure | |
210 | * @soft: When set, function sends a POWER_DOWN command instead of | |
211 | * bringing reset line low | |
212 | * | |
213 | * Power down the chip by performing following actions: | |
214 | * 1. Disable IRQ or stop the polling worker | |
215 | * 2. Send the POWER_DOWN command if the power down is soft or bring | |
216 | * reset line low if not. | |
217 | * | |
218 | * The function returns zero in case of succes or negative error code | |
219 | * otherwise. | |
220 | */ | |
221 | int si476x_core_stop(struct si476x_core *core, bool soft) | |
222 | { | |
223 | int err = 0; | |
224 | atomic_set(&core->is_alive, 0); | |
225 | ||
226 | if (soft) { | |
227 | /* TODO: This probably shoud be a configurable option, | |
228 | * so it is possible to have the chips keep their | |
229 | * oscillators running | |
230 | */ | |
231 | struct si476x_power_down_args args = { | |
232 | .xosc = false, | |
233 | }; | |
234 | err = si476x_core_cmd_power_down(core, &args); | |
235 | } | |
236 | ||
237 | /* We couldn't disable those before | |
238 | * 'si476x_core_cmd_power_down' since we expect to get CTS | |
239 | * interrupt */ | |
240 | if (core->client->irq) | |
241 | disable_irq(core->client->irq); | |
242 | else | |
243 | cancel_delayed_work_sync(&core->status_monitor); | |
244 | ||
245 | if (!soft) { | |
246 | if (gpio_is_valid(core->gpio_reset)) | |
247 | gpio_set_value_cansleep(core->gpio_reset, 0); | |
248 | } | |
249 | return err; | |
250 | } | |
251 | EXPORT_SYMBOL_GPL(si476x_core_stop); | |
252 | ||
253 | /** | |
254 | * si476x_core_set_power_state() - set the level at which the power is | |
255 | * supplied for the chip. | |
256 | * @core: Core device structure | |
257 | * @next_state: enum si476x_power_state describing power state to | |
258 | * switch to. | |
259 | * | |
260 | * Switch on all the required power supplies | |
261 | * | |
262 | * This function returns 0 in case of suvccess and negative error code | |
263 | * otherwise. | |
264 | */ | |
265 | int si476x_core_set_power_state(struct si476x_core *core, | |
266 | enum si476x_power_state next_state) | |
267 | { | |
268 | /* | |
269 | It is not clear form the datasheet if it is possible to | |
270 | work with device if not all power domains are operational. | |
271 | So for now the power-up policy is "power-up all the things!" | |
272 | */ | |
273 | int err = 0; | |
274 | ||
275 | if (core->power_state == SI476X_POWER_INCONSISTENT) { | |
276 | dev_err(&core->client->dev, | |
277 | "The device in inconsistent power state\n"); | |
278 | return -EINVAL; | |
279 | } | |
280 | ||
281 | if (next_state != core->power_state) { | |
282 | switch (next_state) { | |
283 | case SI476X_POWER_UP_FULL: | |
284 | err = regulator_bulk_enable(ARRAY_SIZE(core->supplies), | |
285 | core->supplies); | |
286 | if (err < 0) { | |
287 | core->power_state = SI476X_POWER_INCONSISTENT; | |
288 | break; | |
289 | } | |
290 | /* | |
291 | * Startup timing diagram recommends to have a | |
292 | * 100 us delay between enabling of the power | |
293 | * supplies and turning the tuner on. | |
294 | */ | |
295 | udelay(100); | |
296 | ||
297 | err = si476x_core_start(core, false); | |
298 | if (err < 0) | |
299 | goto disable_regulators; | |
300 | ||
301 | core->power_state = next_state; | |
302 | break; | |
303 | ||
304 | case SI476X_POWER_DOWN: | |
305 | core->power_state = next_state; | |
306 | err = si476x_core_stop(core, false); | |
307 | if (err < 0) | |
308 | core->power_state = SI476X_POWER_INCONSISTENT; | |
309 | disable_regulators: | |
310 | err = regulator_bulk_disable(ARRAY_SIZE(core->supplies), | |
311 | core->supplies); | |
312 | if (err < 0) | |
313 | core->power_state = SI476X_POWER_INCONSISTENT; | |
314 | break; | |
315 | default: | |
316 | BUG(); | |
317 | } | |
318 | } | |
319 | ||
320 | return err; | |
321 | } | |
322 | EXPORT_SYMBOL_GPL(si476x_core_set_power_state); | |
323 | ||
324 | /** | |
325 | * si476x_core_report_drainer_stop() - mark the completion of the RDS | |
326 | * buffer drain porcess by the worker. | |
327 | * | |
328 | * @core: Core device structure | |
329 | */ | |
330 | static inline void si476x_core_report_drainer_stop(struct si476x_core *core) | |
331 | { | |
332 | mutex_lock(&core->rds_drainer_status_lock); | |
333 | core->rds_drainer_is_working = false; | |
334 | mutex_unlock(&core->rds_drainer_status_lock); | |
335 | } | |
336 | ||
337 | /** | |
338 | * si476x_core_start_rds_drainer_once() - start RDS drainer worker if | |
339 | * ther is none working, do nothing otherwise | |
340 | * | |
341 | * @core: Datastructure corresponding to the chip. | |
342 | */ | |
343 | static inline void si476x_core_start_rds_drainer_once(struct si476x_core *core) | |
344 | { | |
345 | mutex_lock(&core->rds_drainer_status_lock); | |
346 | if (!core->rds_drainer_is_working) { | |
347 | core->rds_drainer_is_working = true; | |
348 | schedule_work(&core->rds_fifo_drainer); | |
349 | } | |
350 | mutex_unlock(&core->rds_drainer_status_lock); | |
351 | } | |
352 | /** | |
769b7608 | 353 | * si476x_core_drain_rds_fifo() - RDS buffer drainer. |
730a30ab AS |
354 | * @work: struct work_struct being ppassed to the function by the |
355 | * kernel. | |
356 | * | |
357 | * Drain the contents of the RDS FIFO of | |
358 | */ | |
359 | static void si476x_core_drain_rds_fifo(struct work_struct *work) | |
360 | { | |
361 | int err; | |
362 | ||
363 | struct si476x_core *core = container_of(work, struct si476x_core, | |
364 | rds_fifo_drainer); | |
365 | ||
366 | struct si476x_rds_status_report report; | |
367 | ||
368 | si476x_core_lock(core); | |
369 | err = si476x_core_cmd_fm_rds_status(core, true, false, false, &report); | |
370 | if (!err) { | |
371 | int i = report.rdsfifoused; | |
372 | dev_dbg(&core->client->dev, | |
373 | "%d elements in RDS FIFO. Draining.\n", i); | |
374 | for (; i > 0; --i) { | |
375 | err = si476x_core_cmd_fm_rds_status(core, false, false, | |
376 | (i == 1), &report); | |
377 | if (err < 0) | |
378 | goto unlock; | |
379 | ||
380 | kfifo_in(&core->rds_fifo, report.rds, | |
381 | sizeof(report.rds)); | |
382 | dev_dbg(&core->client->dev, "RDS data:\n %*ph\n", | |
4adedc57 | 383 | (int)sizeof(report.rds), report.rds); |
730a30ab AS |
384 | } |
385 | dev_dbg(&core->client->dev, "Drrrrained!\n"); | |
386 | wake_up_interruptible(&core->rds_read_queue); | |
387 | } | |
388 | ||
389 | unlock: | |
390 | si476x_core_unlock(core); | |
391 | si476x_core_report_drainer_stop(core); | |
392 | } | |
393 | ||
394 | /** | |
395 | * si476x_core_pronounce_dead() | |
396 | * | |
397 | * @core: Core device structure | |
398 | * | |
399 | * Mark the device as being dead and wake up all potentially waiting | |
400 | * threads of execution. | |
401 | * | |
402 | */ | |
403 | static void si476x_core_pronounce_dead(struct si476x_core *core) | |
404 | { | |
405 | dev_info(&core->client->dev, "Core device is dead.\n"); | |
406 | ||
407 | atomic_set(&core->is_alive, 0); | |
408 | ||
409 | /* Wake up al possible waiting processes */ | |
410 | wake_up_interruptible(&core->rds_read_queue); | |
411 | ||
412 | atomic_set(&core->cts, 1); | |
413 | wake_up(&core->command); | |
414 | ||
415 | atomic_set(&core->stc, 1); | |
416 | wake_up(&core->tuning); | |
417 | } | |
418 | ||
419 | /** | |
420 | * si476x_core_i2c_xfer() | |
421 | * | |
422 | * @core: Core device structure | |
423 | * @type: Transfer type | |
424 | * @buf: Transfer buffer for/with data | |
425 | * @count: Transfer buffer size | |
426 | * | |
427 | * Perfrom and I2C transfer(either read or write) and keep a counter | |
428 | * of I/O errors. If the error counter rises above the threshold | |
429 | * pronounce device dead. | |
430 | * | |
431 | * The function returns zero on succes or negative error code on | |
432 | * failure. | |
433 | */ | |
434 | int si476x_core_i2c_xfer(struct si476x_core *core, | |
435 | enum si476x_i2c_type type, | |
436 | char *buf, int count) | |
437 | { | |
438 | static int io_errors_count; | |
439 | int err; | |
440 | if (type == SI476X_I2C_SEND) | |
441 | err = i2c_master_send(core->client, buf, count); | |
442 | else | |
443 | err = i2c_master_recv(core->client, buf, count); | |
444 | ||
445 | if (err < 0) { | |
446 | if (io_errors_count++ > SI476X_MAX_IO_ERRORS) | |
447 | si476x_core_pronounce_dead(core); | |
448 | } else { | |
449 | io_errors_count = 0; | |
450 | } | |
451 | ||
452 | return err; | |
453 | } | |
454 | EXPORT_SYMBOL_GPL(si476x_core_i2c_xfer); | |
455 | ||
456 | /** | |
769b7608 | 457 | * si476x_core_get_status() |
730a30ab AS |
458 | * @core: Core device structure |
459 | * | |
460 | * Get the status byte of the core device by berforming one byte I2C | |
461 | * read. | |
462 | * | |
463 | * The function returns a status value or a negative error code on | |
464 | * error. | |
465 | */ | |
466 | static int si476x_core_get_status(struct si476x_core *core) | |
467 | { | |
468 | u8 response; | |
469 | int err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, | |
470 | &response, sizeof(response)); | |
471 | ||
472 | return (err < 0) ? err : response; | |
473 | } | |
474 | ||
475 | /** | |
769b7608 | 476 | * si476x_core_get_and_signal_status() - IRQ dispatcher |
730a30ab AS |
477 | * @core: Core device structure |
478 | * | |
479 | * Dispatch the arrived interrupt request based on the value of the | |
480 | * status byte reported by the tuner. | |
481 | * | |
482 | */ | |
483 | static void si476x_core_get_and_signal_status(struct si476x_core *core) | |
484 | { | |
485 | int status = si476x_core_get_status(core); | |
486 | if (status < 0) { | |
487 | dev_err(&core->client->dev, "Failed to get status\n"); | |
488 | return; | |
489 | } | |
490 | ||
491 | if (status & SI476X_CTS) { | |
492 | /* Unfortunately completions could not be used for | |
493 | * signalling CTS since this flag cannot be cleared | |
494 | * in status byte, and therefore once it becomes true | |
495 | * multiple calls to 'complete' would cause the | |
496 | * commands following the current one to be completed | |
497 | * before they actually are */ | |
498 | dev_dbg(&core->client->dev, "[interrupt] CTSINT\n"); | |
499 | atomic_set(&core->cts, 1); | |
500 | wake_up(&core->command); | |
501 | } | |
502 | ||
503 | if (status & SI476X_FM_RDS_INT) { | |
504 | dev_dbg(&core->client->dev, "[interrupt] RDSINT\n"); | |
505 | si476x_core_start_rds_drainer_once(core); | |
506 | } | |
507 | ||
508 | if (status & SI476X_STC_INT) { | |
509 | dev_dbg(&core->client->dev, "[interrupt] STCINT\n"); | |
510 | atomic_set(&core->stc, 1); | |
511 | wake_up(&core->tuning); | |
512 | } | |
513 | } | |
514 | ||
515 | static void si476x_core_poll_loop(struct work_struct *work) | |
516 | { | |
517 | struct si476x_core *core = SI476X_WORK_TO_CORE(work); | |
518 | ||
519 | si476x_core_get_and_signal_status(core); | |
520 | ||
521 | if (atomic_read(&core->is_alive)) | |
522 | si476x_core_schedule_polling_work(core); | |
523 | } | |
524 | ||
525 | static irqreturn_t si476x_core_interrupt(int irq, void *dev) | |
526 | { | |
527 | struct si476x_core *core = dev; | |
528 | ||
529 | si476x_core_get_and_signal_status(core); | |
530 | ||
531 | return IRQ_HANDLED; | |
532 | } | |
533 | ||
534 | /** | |
769b7608 | 535 | * si476x_core_fwver_to_revision() |
730a30ab | 536 | * @core: Core device structure |
c9b55f99 LJ |
537 | * @func: Selects the boot function of the device: |
538 | * *_BOOTLOADER - Boot loader | |
539 | * *_FM_RECEIVER - FM receiver | |
540 | * *_AM_RECEIVER - AM receiver | |
541 | * *_WB_RECEIVER - Weatherband receiver | |
730a30ab AS |
542 | * @major: Firmware major number |
543 | * @minor1: Firmware first minor number | |
544 | * @minor2: Firmware second minor number | |
545 | * | |
546 | * Convert a chip's firmware version number into an offset that later | |
547 | * will be used to as offset in "vtable" of tuner functions | |
548 | * | |
549 | * This function returns a positive offset in case of success and a -1 | |
550 | * in case of failure. | |
551 | */ | |
552 | static int si476x_core_fwver_to_revision(struct si476x_core *core, | |
553 | int func, int major, | |
554 | int minor1, int minor2) | |
555 | { | |
556 | switch (func) { | |
557 | case SI476X_FUNC_FM_RECEIVER: | |
558 | switch (major) { | |
559 | case 5: | |
560 | return SI476X_REVISION_A10; | |
561 | case 8: | |
562 | return SI476X_REVISION_A20; | |
563 | case 10: | |
564 | return SI476X_REVISION_A30; | |
565 | default: | |
566 | goto unknown_revision; | |
567 | } | |
568 | case SI476X_FUNC_AM_RECEIVER: | |
569 | switch (major) { | |
570 | case 5: | |
571 | return SI476X_REVISION_A10; | |
572 | case 7: | |
573 | return SI476X_REVISION_A20; | |
574 | case 9: | |
575 | return SI476X_REVISION_A30; | |
576 | default: | |
577 | goto unknown_revision; | |
578 | } | |
579 | case SI476X_FUNC_WB_RECEIVER: | |
580 | switch (major) { | |
581 | case 3: | |
582 | return SI476X_REVISION_A10; | |
583 | case 5: | |
584 | return SI476X_REVISION_A20; | |
585 | case 7: | |
586 | return SI476X_REVISION_A30; | |
587 | default: | |
588 | goto unknown_revision; | |
589 | } | |
590 | case SI476X_FUNC_BOOTLOADER: | |
b1ded80a | 591 | default: /* FALLTHROUGH */ |
730a30ab AS |
592 | BUG(); |
593 | return -1; | |
594 | } | |
595 | ||
596 | unknown_revision: | |
597 | dev_err(&core->client->dev, | |
598 | "Unsupported version of the firmware: %d.%d.%d, " | |
521d9357 | 599 | "reverting to A10 compatible functions\n", |
730a30ab AS |
600 | major, minor1, minor2); |
601 | ||
602 | return SI476X_REVISION_A10; | |
603 | } | |
604 | ||
605 | /** | |
769b7608 | 606 | * si476x_core_get_revision_info() |
730a30ab AS |
607 | * @core: Core device structure |
608 | * | |
609 | * Get the firmware version number of the device. It is done in | |
610 | * following three steps: | |
611 | * 1. Power-up the device | |
612 | * 2. Send the 'FUNC_INFO' command | |
613 | * 3. Powering the device down. | |
614 | * | |
615 | * The function return zero on success and a negative error code on | |
616 | * failure. | |
617 | */ | |
618 | static int si476x_core_get_revision_info(struct si476x_core *core) | |
619 | { | |
620 | int rval; | |
621 | struct si476x_func_info info; | |
622 | ||
623 | si476x_core_lock(core); | |
624 | rval = si476x_core_set_power_state(core, SI476X_POWER_UP_FULL); | |
625 | if (rval < 0) | |
626 | goto exit; | |
627 | ||
628 | rval = si476x_core_cmd_func_info(core, &info); | |
629 | if (rval < 0) | |
630 | goto power_down; | |
631 | ||
632 | core->revision = si476x_core_fwver_to_revision(core, info.func, | |
633 | info.firmware.major, | |
634 | info.firmware.minor[0], | |
635 | info.firmware.minor[1]); | |
636 | power_down: | |
637 | si476x_core_set_power_state(core, SI476X_POWER_DOWN); | |
638 | exit: | |
639 | si476x_core_unlock(core); | |
640 | ||
641 | return rval; | |
642 | } | |
643 | ||
644 | bool si476x_core_has_am(struct si476x_core *core) | |
645 | { | |
646 | return core->chip_id == SI476X_CHIP_SI4761 || | |
647 | core->chip_id == SI476X_CHIP_SI4764; | |
648 | } | |
649 | EXPORT_SYMBOL_GPL(si476x_core_has_am); | |
650 | ||
651 | bool si476x_core_has_diversity(struct si476x_core *core) | |
652 | { | |
653 | return core->chip_id == SI476X_CHIP_SI4764; | |
654 | } | |
655 | EXPORT_SYMBOL_GPL(si476x_core_has_diversity); | |
656 | ||
657 | bool si476x_core_is_a_secondary_tuner(struct si476x_core *core) | |
658 | { | |
659 | return si476x_core_has_diversity(core) && | |
660 | (core->diversity_mode == SI476X_PHDIV_SECONDARY_ANTENNA || | |
661 | core->diversity_mode == SI476X_PHDIV_SECONDARY_COMBINING); | |
662 | } | |
663 | EXPORT_SYMBOL_GPL(si476x_core_is_a_secondary_tuner); | |
664 | ||
665 | bool si476x_core_is_a_primary_tuner(struct si476x_core *core) | |
666 | { | |
667 | return si476x_core_has_diversity(core) && | |
668 | (core->diversity_mode == SI476X_PHDIV_PRIMARY_ANTENNA || | |
669 | core->diversity_mode == SI476X_PHDIV_PRIMARY_COMBINING); | |
670 | } | |
671 | EXPORT_SYMBOL_GPL(si476x_core_is_a_primary_tuner); | |
672 | ||
673 | bool si476x_core_is_in_am_receiver_mode(struct si476x_core *core) | |
674 | { | |
675 | return si476x_core_has_am(core) && | |
676 | (core->power_up_parameters.func == SI476X_FUNC_AM_RECEIVER); | |
677 | } | |
678 | EXPORT_SYMBOL_GPL(si476x_core_is_in_am_receiver_mode); | |
679 | ||
680 | bool si476x_core_is_powered_up(struct si476x_core *core) | |
681 | { | |
682 | return core->power_state == SI476X_POWER_UP_FULL; | |
683 | } | |
684 | EXPORT_SYMBOL_GPL(si476x_core_is_powered_up); | |
685 | ||
ca08a4f3 | 686 | static int si476x_core_probe(struct i2c_client *client) |
730a30ab | 687 | { |
ca08a4f3 | 688 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
730a30ab AS |
689 | int rval; |
690 | struct si476x_core *core; | |
691 | struct si476x_platform_data *pdata; | |
692 | struct mfd_cell *cell; | |
693 | int cell_num; | |
694 | ||
695 | core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL); | |
a9241b09 | 696 | if (!core) |
730a30ab | 697 | return -ENOMEM; |
a9241b09 | 698 | |
730a30ab AS |
699 | core->client = client; |
700 | ||
701 | core->regmap = devm_regmap_init_si476x(core); | |
702 | if (IS_ERR(core->regmap)) { | |
703 | rval = PTR_ERR(core->regmap); | |
704 | dev_err(&client->dev, | |
705 | "Failed to allocate register map: %d\n", | |
706 | rval); | |
707 | return rval; | |
708 | } | |
709 | ||
710 | i2c_set_clientdata(client, core); | |
711 | ||
712 | atomic_set(&core->is_alive, 0); | |
713 | core->power_state = SI476X_POWER_DOWN; | |
714 | ||
334a41ce | 715 | pdata = dev_get_platdata(&client->dev); |
730a30ab AS |
716 | if (pdata) { |
717 | memcpy(&core->power_up_parameters, | |
718 | &pdata->power_up_parameters, | |
719 | sizeof(core->power_up_parameters)); | |
720 | ||
721 | core->gpio_reset = -1; | |
722 | if (gpio_is_valid(pdata->gpio_reset)) { | |
723 | rval = gpio_request(pdata->gpio_reset, "si476x reset"); | |
724 | if (rval) { | |
725 | dev_err(&client->dev, | |
726 | "Failed to request gpio: %d\n", rval); | |
727 | return rval; | |
728 | } | |
729 | core->gpio_reset = pdata->gpio_reset; | |
730 | gpio_direction_output(core->gpio_reset, 0); | |
731 | } | |
732 | ||
733 | core->diversity_mode = pdata->diversity_mode; | |
734 | memcpy(&core->pinmux, &pdata->pinmux, | |
735 | sizeof(struct si476x_pinmux)); | |
736 | } else { | |
737 | dev_err(&client->dev, "No platform data provided\n"); | |
738 | return -EINVAL; | |
739 | } | |
740 | ||
741 | core->supplies[0].supply = "vd"; | |
742 | core->supplies[1].supply = "va"; | |
743 | core->supplies[2].supply = "vio1"; | |
744 | core->supplies[3].supply = "vio2"; | |
745 | ||
746 | rval = devm_regulator_bulk_get(&client->dev, | |
747 | ARRAY_SIZE(core->supplies), | |
748 | core->supplies); | |
749 | if (rval) { | |
d4c55da2 | 750 | dev_err(&client->dev, "Failed to get all of the regulators\n"); |
730a30ab AS |
751 | goto free_gpio; |
752 | } | |
753 | ||
754 | mutex_init(&core->cmd_lock); | |
755 | init_waitqueue_head(&core->command); | |
756 | init_waitqueue_head(&core->tuning); | |
757 | ||
758 | rval = kfifo_alloc(&core->rds_fifo, | |
759 | SI476X_DRIVER_RDS_FIFO_DEPTH * | |
760 | sizeof(struct v4l2_rds_data), | |
761 | GFP_KERNEL); | |
762 | if (rval) { | |
f42cf8d6 | 763 | dev_err(&client->dev, "Could not allocate the FIFO\n"); |
730a30ab AS |
764 | goto free_gpio; |
765 | } | |
766 | mutex_init(&core->rds_drainer_status_lock); | |
767 | init_waitqueue_head(&core->rds_read_queue); | |
768 | INIT_WORK(&core->rds_fifo_drainer, si476x_core_drain_rds_fifo); | |
769 | ||
770 | if (client->irq) { | |
771 | rval = devm_request_threaded_irq(&client->dev, | |
772 | client->irq, NULL, | |
773 | si476x_core_interrupt, | |
ef76cc5b FE |
774 | IRQF_TRIGGER_FALLING | |
775 | IRQF_ONESHOT, | |
730a30ab AS |
776 | client->name, core); |
777 | if (rval < 0) { | |
778 | dev_err(&client->dev, "Could not request IRQ %d\n", | |
779 | client->irq); | |
780 | goto free_kfifo; | |
781 | } | |
782 | disable_irq(client->irq); | |
783 | dev_dbg(&client->dev, "IRQ requested.\n"); | |
784 | ||
785 | core->rds_fifo_depth = 20; | |
786 | } else { | |
787 | INIT_DELAYED_WORK(&core->status_monitor, | |
788 | si476x_core_poll_loop); | |
789 | dev_info(&client->dev, | |
790 | "No IRQ number specified, will use polling\n"); | |
791 | ||
792 | core->rds_fifo_depth = 5; | |
793 | } | |
794 | ||
795 | core->chip_id = id->driver_data; | |
796 | ||
797 | rval = si476x_core_get_revision_info(core); | |
798 | if (rval < 0) { | |
799 | rval = -ENODEV; | |
800 | goto free_kfifo; | |
801 | } | |
802 | ||
803 | cell_num = 0; | |
804 | ||
805 | cell = &core->cells[SI476X_RADIO_CELL]; | |
806 | cell->name = "si476x-radio"; | |
807 | cell_num++; | |
808 | ||
809 | #ifdef CONFIG_SND_SOC_SI476X | |
810 | if ((core->chip_id == SI476X_CHIP_SI4761 || | |
811 | core->chip_id == SI476X_CHIP_SI4764) && | |
812 | core->pinmux.dclk == SI476X_DCLK_DAUDIO && | |
813 | core->pinmux.dfs == SI476X_DFS_DAUDIO && | |
814 | core->pinmux.dout == SI476X_DOUT_I2S_OUTPUT && | |
815 | core->pinmux.xout == SI476X_XOUT_TRISTATE) { | |
816 | cell = &core->cells[SI476X_CODEC_CELL]; | |
817 | cell->name = "si476x-codec"; | |
818 | cell_num++; | |
819 | } | |
820 | #endif | |
821 | rval = mfd_add_devices(&client->dev, | |
822 | (client->adapter->nr << 8) + client->addr, | |
823 | core->cells, cell_num, | |
824 | NULL, 0, NULL); | |
825 | if (!rval) | |
826 | return 0; | |
827 | ||
828 | free_kfifo: | |
829 | kfifo_free(&core->rds_fifo); | |
830 | ||
831 | free_gpio: | |
832 | if (gpio_is_valid(core->gpio_reset)) | |
833 | gpio_free(core->gpio_reset); | |
834 | ||
835 | return rval; | |
836 | } | |
837 | ||
ed5c2f5f | 838 | static void si476x_core_remove(struct i2c_client *client) |
730a30ab AS |
839 | { |
840 | struct si476x_core *core = i2c_get_clientdata(client); | |
841 | ||
842 | si476x_core_pronounce_dead(core); | |
843 | mfd_remove_devices(&client->dev); | |
844 | ||
845 | if (client->irq) | |
846 | disable_irq(client->irq); | |
847 | else | |
848 | cancel_delayed_work_sync(&core->status_monitor); | |
849 | ||
850 | kfifo_free(&core->rds_fifo); | |
851 | ||
852 | if (gpio_is_valid(core->gpio_reset)) | |
853 | gpio_free(core->gpio_reset); | |
730a30ab AS |
854 | } |
855 | ||
856 | ||
857 | static const struct i2c_device_id si476x_id[] = { | |
858 | { "si4761", SI476X_CHIP_SI4761 }, | |
859 | { "si4764", SI476X_CHIP_SI4764 }, | |
860 | { "si4768", SI476X_CHIP_SI4768 }, | |
861 | { }, | |
862 | }; | |
863 | MODULE_DEVICE_TABLE(i2c, si476x_id); | |
864 | ||
865 | static struct i2c_driver si476x_core_driver = { | |
866 | .driver = { | |
867 | .name = "si476x-core", | |
730a30ab | 868 | }, |
ca08a4f3 | 869 | .probe_new = si476x_core_probe, |
730a30ab AS |
870 | .remove = si476x_core_remove, |
871 | .id_table = si476x_id, | |
872 | }; | |
873 | module_i2c_driver(si476x_core_driver); | |
874 | ||
875 | ||
876 | MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); | |
877 | MODULE_DESCRIPTION("Si4761/64/68 AM/FM MFD core device driver"); | |
878 | MODULE_LICENSE("GPL"); |