1 | // SPDX-License-Identifier: GPL-2.0-only |
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> |
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(dwork: &core->status_monitor, |
101 | delay: 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(number: core->gpio_reset)) |
134 | gpio_set_value_cansleep(gpio: core->gpio_reset, value: 1); |
135 | |
136 | if (client->irq) |
137 | enable_irq(irq: client->irq); |
138 | |
139 | udelay(100); |
140 | |
141 | if (!client->irq) { |
142 | atomic_set(v: &core->is_alive, i: 1); |
143 | si476x_core_schedule_polling_work(core); |
144 | } |
145 | } else { |
146 | if (client->irq) |
147 | enable_irq(irq: client->irq); |
148 | else { |
149 | atomic_set(v: &core->is_alive, i: 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(v: &core->is_alive, i: 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(map: core->regmap, |
177 | reg: SI476X_PROP_INT_CTL_ENABLE, |
178 | val: 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(v: &core->is_alive, i: 0); |
194 | |
195 | if (client->irq) |
196 | disable_irq(irq: client->irq); |
197 | else |
198 | cancel_delayed_work_sync(dwork: &core->status_monitor); |
199 | |
200 | if (gpio_is_valid(number: core->gpio_reset)) |
201 | gpio_set_value_cansleep(gpio: core->gpio_reset, value: 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(v: &core->is_alive, i: 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(irq: core->client->irq); |
242 | else |
243 | cancel_delayed_work_sync(dwork: &core->status_monitor); |
244 | |
245 | if (!soft) { |
246 | if (gpio_is_valid(number: core->gpio_reset)) |
247 | gpio_set_value_cansleep(gpio: core->gpio_reset, value: 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 | consumers: 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 | consumers: 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(lock: &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(work: &core->rds_fifo_drainer); |
349 | } |
350 | mutex_unlock(lock: &core->rds_drainer_status_lock); |
351 | } |
352 | /** |
353 | * si476x_core_drain_rds_fifo() - RDS buffer drainer. |
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" , |
383 | (int)sizeof(report.rds), report.rds); |
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(v: &core->is_alive, i: 0); |
408 | |
409 | /* Wake up al possible waiting processes */ |
410 | wake_up_interruptible(&core->rds_read_queue); |
411 | |
412 | atomic_set(v: &core->cts, i: 1); |
413 | wake_up(&core->command); |
414 | |
415 | atomic_set(v: &core->stc, i: 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(client: core->client, buf, count); |
442 | else |
443 | err = i2c_master_recv(client: 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 | /** |
457 | * si476x_core_get_status() |
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 | /** |
476 | * si476x_core_get_and_signal_status() - IRQ dispatcher |
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(v: &core->cts, i: 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(v: &core->stc, i: 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(v: &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 | /** |
535 | * si476x_core_fwver_to_revision() |
536 | * @core: Core device structure |
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 |
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: |
591 | default: /* FALLTHROUGH */ |
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, " |
599 | "reverting to A10 compatible functions\n" , |
600 | major, minor1, minor2); |
601 | |
602 | return SI476X_REVISION_A10; |
603 | } |
604 | |
605 | /** |
606 | * si476x_core_get_revision_info() |
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, func: info.func, |
633 | major: info.firmware.major, |
634 | minor1: info.firmware.minor[0], |
635 | minor2: 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 | |
686 | static int si476x_core_probe(struct i2c_client *client) |
687 | { |
688 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
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(dev: &client->dev, size: sizeof(*core), GFP_KERNEL); |
696 | if (!core) |
697 | return -ENOMEM; |
698 | |
699 | core->client = client; |
700 | |
701 | core->regmap = devm_regmap_init_si476x(core); |
702 | if (IS_ERR(ptr: core->regmap)) { |
703 | rval = PTR_ERR(ptr: 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, data: core); |
711 | |
712 | atomic_set(v: &core->is_alive, i: 0); |
713 | core->power_state = SI476X_POWER_DOWN; |
714 | |
715 | pdata = dev_get_platdata(dev: &client->dev); |
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(number: pdata->gpio_reset)) { |
723 | rval = gpio_request(gpio: pdata->gpio_reset, label: "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(gpio: core->gpio_reset, value: 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(dev: &client->dev, |
747 | ARRAY_SIZE(core->supplies), |
748 | consumers: core->supplies); |
749 | if (rval) { |
750 | dev_err(&client->dev, "Failed to get all of the regulators\n" ); |
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) { |
763 | dev_err(&client->dev, "Could not allocate the FIFO\n" ); |
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(dev: &client->dev, |
772 | irq: client->irq, NULL, |
773 | thread_fn: si476x_core_interrupt, |
774 | IRQF_TRIGGER_FALLING | |
775 | IRQF_ONESHOT, |
776 | devname: client->name, dev_id: 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(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(parent: &client->dev, |
822 | id: (client->adapter->nr << 8) + client->addr, |
823 | cells: core->cells, n_devs: cell_num, |
824 | NULL, irq_base: 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(number: core->gpio_reset)) |
833 | gpio_free(gpio: core->gpio_reset); |
834 | |
835 | return rval; |
836 | } |
837 | |
838 | static void si476x_core_remove(struct i2c_client *client) |
839 | { |
840 | struct si476x_core *core = i2c_get_clientdata(client); |
841 | |
842 | si476x_core_pronounce_dead(core); |
843 | mfd_remove_devices(parent: &client->dev); |
844 | |
845 | if (client->irq) |
846 | disable_irq(irq: client->irq); |
847 | else |
848 | cancel_delayed_work_sync(dwork: &core->status_monitor); |
849 | |
850 | kfifo_free(&core->rds_fifo); |
851 | |
852 | if (gpio_is_valid(number: core->gpio_reset)) |
853 | gpio_free(gpio: core->gpio_reset); |
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" , |
868 | }, |
869 | .probe = si476x_core_probe, |
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" ); |
879 | |