Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * drivers/mfd/si476x-cmd.c -- Subroutines implementing command
4 * protocol of si476x series of chips
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
12#include <linux/module.h>
13#include <linux/completion.h>
14#include <linux/delay.h>
15#include <linux/atomic.h>
16#include <linux/i2c.h>
17#include <linux/device.h>
18#include <linux/gpio.h>
19#include <linux/videodev2.h>
20
21#include <linux/mfd/si476x-core.h>
22
23#include <asm/unaligned.h>
24
25#define msb(x) ((u8)((u16) x >> 8))
26#define lsb(x) ((u8)((u16) x & 0x00FF))
27
28
29
30#define CMD_POWER_UP 0x01
31#define CMD_POWER_UP_A10_NRESP 1
32#define CMD_POWER_UP_A10_NARGS 5
33
34#define CMD_POWER_UP_A20_NRESP 1
35#define CMD_POWER_UP_A20_NARGS 5
36
37#define POWER_UP_DELAY_MS 110
38
39#define CMD_POWER_DOWN 0x11
40#define CMD_POWER_DOWN_A10_NRESP 1
41
42#define CMD_POWER_DOWN_A20_NRESP 1
43#define CMD_POWER_DOWN_A20_NARGS 1
44
45#define CMD_FUNC_INFO 0x12
46#define CMD_FUNC_INFO_NRESP 7
47
48#define CMD_SET_PROPERTY 0x13
49#define CMD_SET_PROPERTY_NARGS 5
50#define CMD_SET_PROPERTY_NRESP 1
51
52#define CMD_GET_PROPERTY 0x14
53#define CMD_GET_PROPERTY_NARGS 3
54#define CMD_GET_PROPERTY_NRESP 4
55
56#define CMD_AGC_STATUS 0x17
57#define CMD_AGC_STATUS_NRESP_A10 2
58#define CMD_AGC_STATUS_NRESP_A20 6
59
60#define PIN_CFG_BYTE(x) (0x7F & (x))
61#define CMD_DIG_AUDIO_PIN_CFG 0x18
62#define CMD_DIG_AUDIO_PIN_CFG_NARGS 4
63#define CMD_DIG_AUDIO_PIN_CFG_NRESP 5
64
65#define CMD_ZIF_PIN_CFG 0x19
66#define CMD_ZIF_PIN_CFG_NARGS 4
67#define CMD_ZIF_PIN_CFG_NRESP 5
68
69#define CMD_IC_LINK_GPO_CTL_PIN_CFG 0x1A
70#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS 4
71#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP 5
72
73#define CMD_ANA_AUDIO_PIN_CFG 0x1B
74#define CMD_ANA_AUDIO_PIN_CFG_NARGS 1
75#define CMD_ANA_AUDIO_PIN_CFG_NRESP 2
76
77#define CMD_INTB_PIN_CFG 0x1C
78#define CMD_INTB_PIN_CFG_NARGS 2
79#define CMD_INTB_PIN_CFG_A10_NRESP 6
80#define CMD_INTB_PIN_CFG_A20_NRESP 3
81
82#define CMD_FM_TUNE_FREQ 0x30
83#define CMD_FM_TUNE_FREQ_A10_NARGS 5
84#define CMD_FM_TUNE_FREQ_A20_NARGS 3
85#define CMD_FM_TUNE_FREQ_NRESP 1
86
87#define CMD_FM_RSQ_STATUS 0x32
88
89#define CMD_FM_RSQ_STATUS_A10_NARGS 1
90#define CMD_FM_RSQ_STATUS_A10_NRESP 17
91#define CMD_FM_RSQ_STATUS_A30_NARGS 1
92#define CMD_FM_RSQ_STATUS_A30_NRESP 23
93
94
95#define CMD_FM_SEEK_START 0x31
96#define CMD_FM_SEEK_START_NARGS 1
97#define CMD_FM_SEEK_START_NRESP 1
98
99#define CMD_FM_RDS_STATUS 0x36
100#define CMD_FM_RDS_STATUS_NARGS 1
101#define CMD_FM_RDS_STATUS_NRESP 16
102
103#define CMD_FM_RDS_BLOCKCOUNT 0x37
104#define CMD_FM_RDS_BLOCKCOUNT_NARGS 1
105#define CMD_FM_RDS_BLOCKCOUNT_NRESP 8
106
107#define CMD_FM_PHASE_DIVERSITY 0x38
108#define CMD_FM_PHASE_DIVERSITY_NARGS 1
109#define CMD_FM_PHASE_DIVERSITY_NRESP 1
110
111#define CMD_FM_PHASE_DIV_STATUS 0x39
112#define CMD_FM_PHASE_DIV_STATUS_NRESP 2
113
114#define CMD_AM_TUNE_FREQ 0x40
115#define CMD_AM_TUNE_FREQ_NARGS 3
116#define CMD_AM_TUNE_FREQ_NRESP 1
117
118#define CMD_AM_RSQ_STATUS 0x42
119#define CMD_AM_RSQ_STATUS_NARGS 1
120#define CMD_AM_RSQ_STATUS_NRESP 13
121
122#define CMD_AM_SEEK_START 0x41
123#define CMD_AM_SEEK_START_NARGS 1
124#define CMD_AM_SEEK_START_NRESP 1
125
126
127#define CMD_AM_ACF_STATUS 0x45
128#define CMD_AM_ACF_STATUS_NRESP 6
129#define CMD_AM_ACF_STATUS_NARGS 1
130
131#define CMD_FM_ACF_STATUS 0x35
132#define CMD_FM_ACF_STATUS_NRESP 8
133#define CMD_FM_ACF_STATUS_NARGS 1
134
135#define CMD_MAX_ARGS_COUNT (10)
136
137
138enum si476x_acf_status_report_bits {
139 SI476X_ACF_BLEND_INT = (1 << 4),
140 SI476X_ACF_HIBLEND_INT = (1 << 3),
141 SI476X_ACF_HICUT_INT = (1 << 2),
142 SI476X_ACF_CHBW_INT = (1 << 1),
143 SI476X_ACF_SOFTMUTE_INT = (1 << 0),
144
145 SI476X_ACF_SMUTE = (1 << 0),
146 SI476X_ACF_SMATTN = 0x1f,
147 SI476X_ACF_PILOT = (1 << 7),
148 SI476X_ACF_STBLEND = ~SI476X_ACF_PILOT,
149};
150
151enum si476x_agc_status_report_bits {
152 SI476X_AGC_MXHI = (1 << 5),
153 SI476X_AGC_MXLO = (1 << 4),
154 SI476X_AGC_LNAHI = (1 << 3),
155 SI476X_AGC_LNALO = (1 << 2),
156};
157
158enum si476x_errors {
159 SI476X_ERR_BAD_COMMAND = 0x10,
160 SI476X_ERR_BAD_ARG1 = 0x11,
161 SI476X_ERR_BAD_ARG2 = 0x12,
162 SI476X_ERR_BAD_ARG3 = 0x13,
163 SI476X_ERR_BAD_ARG4 = 0x14,
164 SI476X_ERR_BUSY = 0x18,
165 SI476X_ERR_BAD_INTERNAL_MEMORY = 0x20,
166 SI476X_ERR_BAD_PATCH = 0x30,
167 SI476X_ERR_BAD_BOOT_MODE = 0x31,
168 SI476X_ERR_BAD_PROPERTY = 0x40,
169};
170
171static int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
172{
173 int err;
174 char *cause;
175 u8 buffer[2];
176
177 if (core->revision != SI476X_REVISION_A10) {
178 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
179 buffer, sizeof(buffer));
180 if (err == sizeof(buffer)) {
181 switch (buffer[1]) {
182 case SI476X_ERR_BAD_COMMAND:
183 cause = "Bad command";
184 err = -EINVAL;
185 break;
186 case SI476X_ERR_BAD_ARG1:
187 cause = "Bad argument #1";
188 err = -EINVAL;
189 break;
190 case SI476X_ERR_BAD_ARG2:
191 cause = "Bad argument #2";
192 err = -EINVAL;
193 break;
194 case SI476X_ERR_BAD_ARG3:
195 cause = "Bad argument #3";
196 err = -EINVAL;
197 break;
198 case SI476X_ERR_BAD_ARG4:
199 cause = "Bad argument #4";
200 err = -EINVAL;
201 break;
202 case SI476X_ERR_BUSY:
203 cause = "Chip is busy";
204 err = -EBUSY;
205 break;
206 case SI476X_ERR_BAD_INTERNAL_MEMORY:
207 cause = "Bad internal memory";
208 err = -EIO;
209 break;
210 case SI476X_ERR_BAD_PATCH:
211 cause = "Bad patch";
212 err = -EINVAL;
213 break;
214 case SI476X_ERR_BAD_BOOT_MODE:
215 cause = "Bad boot mode";
216 err = -EINVAL;
217 break;
218 case SI476X_ERR_BAD_PROPERTY:
219 cause = "Bad property";
220 err = -EINVAL;
221 break;
222 default:
223 cause = "Unknown";
224 err = -EIO;
225 }
226
227 dev_err(&core->client->dev,
228 "[Chip error status]: %s\n", cause);
229 } else {
230 dev_err(&core->client->dev,
231 "Failed to fetch error code\n");
232 err = (err >= 0) ? -EIO : err;
233 }
234 } else {
235 err = -EIO;
236 }
237
238 return err;
239}
240
241/**
242 * si476x_core_send_command() - sends a command to si476x and waits its
243 * response
244 * @core: si476x_device structure for the device we are
245 * communicating with
246 * @command: command id
247 * @args: command arguments we are sending
248 * @argn: actual size of @args
249 * @resp: buffer to place the expected response from the device
250 * @respn: actual size of @resp
251 * @usecs: amount of time to wait before reading the response (in
252 * usecs)
253 *
254 * Function returns 0 on succsess and negative error code on
255 * failure
256 */
257static int si476x_core_send_command(struct si476x_core *core,
258 const u8 command,
259 const u8 args[],
260 const int argn,
261 u8 resp[],
262 const int respn,
263 const int usecs)
264{
265 struct i2c_client *client = core->client;
266 int err;
267 u8 data[CMD_MAX_ARGS_COUNT + 1];
268
269 if (argn > CMD_MAX_ARGS_COUNT) {
270 err = -ENOMEM;
271 goto exit;
272 }
273
274 if (!client->adapter) {
275 err = -ENODEV;
276 goto exit;
277 }
278
279 /* First send the command and its arguments */
280 data[0] = command;
281 memcpy(&data[1], args, argn);
282 dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data);
283
284 err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND,
285 (char *) data, argn + 1);
286 if (err != argn + 1) {
287 dev_err(&core->client->dev,
288 "Error while sending command 0x%02x\n",
289 command);
290 err = (err >= 0) ? -EIO : err;
291 goto exit;
292 }
293 /* Set CTS to zero only after the command is send to avoid
294 * possible racing conditions when working in polling mode */
295 atomic_set(&core->cts, 0);
296
297 /* if (unlikely(command == CMD_POWER_DOWN) */
298 if (!wait_event_timeout(core->command,
299 atomic_read(&core->cts),
300 usecs_to_jiffies(usecs) + 1))
301 dev_warn(&core->client->dev,
302 "(%s) [CMD 0x%02x] Answer timeout.\n",
303 __func__, command);
304
305 /*
306 When working in polling mode, for some reason the tuner will
307 report CTS bit as being set in the first status byte read,
308 but all the consequtive ones will return zeros until the
309 tuner is actually completed the POWER_UP command. To
310 workaround that we wait for second CTS to be reported
311 */
312 if (unlikely(!core->client->irq && command == CMD_POWER_UP)) {
313 if (!wait_event_timeout(core->command,
314 atomic_read(&core->cts),
315 usecs_to_jiffies(usecs) + 1))
316 dev_warn(&core->client->dev,
317 "(%s) Power up took too much time.\n",
318 __func__);
319 }
320
321 /* Then get the response */
322 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn);
323 if (err != respn) {
324 dev_err(&core->client->dev,
325 "Error while reading response for command 0x%02x\n",
326 command);
327 err = (err >= 0) ? -EIO : err;
328 goto exit;
329 }
330 dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp);
331
332 err = 0;
333
334 if (resp[0] & SI476X_ERR) {
335 dev_err(&core->client->dev,
336 "[CMD 0x%02x] Chip set error flag\n", command);
337 err = si476x_core_parse_and_nag_about_error(core);
338 goto exit;
339 }
340
341 if (!(resp[0] & SI476X_CTS))
342 err = -EBUSY;
343exit:
344 return err;
345}
346
347static int si476x_cmd_clear_stc(struct si476x_core *core)
348{
349 int err;
350 struct si476x_rsq_status_args args = {
351 .primary = false,
352 .rsqack = false,
353 .attune = false,
354 .cancel = false,
355 .stcack = true,
356 };
357
358 switch (core->power_up_parameters.func) {
359 case SI476X_FUNC_FM_RECEIVER:
360 err = si476x_core_cmd_fm_rsq_status(core, &args, NULL);
361 break;
362 case SI476X_FUNC_AM_RECEIVER:
363 err = si476x_core_cmd_am_rsq_status(core, &args, NULL);
364 break;
365 default:
366 err = -EINVAL;
367 }
368
369 return err;
370}
371
372static int si476x_cmd_tune_seek_freq(struct si476x_core *core,
373 uint8_t cmd,
374 const uint8_t args[], size_t argn,
375 uint8_t *resp, size_t respn)
376{
377 int err;
378
379
380 atomic_set(&core->stc, 0);
381 err = si476x_core_send_command(core, cmd, args, argn, resp, respn,
382 SI476X_TIMEOUT_TUNE);
383 if (!err) {
384 wait_event_killable(core->tuning,
385 atomic_read(&core->stc));
386 si476x_cmd_clear_stc(core);
387 }
388
389 return err;
390}
391
392/**
393 * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device
394 * @core: device to send the command to
395 * @info: struct si476x_func_info to fill all the information
396 * returned by the command
397 *
398 * The command requests the firmware and patch version for currently
399 * loaded firmware (dependent on the function of the device FM/AM/WB)
400 *
401 * Function returns 0 on succsess and negative error code on
402 * failure
403 */
404int si476x_core_cmd_func_info(struct si476x_core *core,
405 struct si476x_func_info *info)
406{
407 int err;
408 u8 resp[CMD_FUNC_INFO_NRESP];
409
410 err = si476x_core_send_command(core, CMD_FUNC_INFO,
411 NULL, 0,
412 resp, ARRAY_SIZE(resp),
413 SI476X_DEFAULT_TIMEOUT);
414
415 info->firmware.major = resp[1];
416 info->firmware.minor[0] = resp[2];
417 info->firmware.minor[1] = resp[3];
418
419 info->patch_id = ((u16) resp[4] << 8) | resp[5];
420 info->func = resp[6];
421
422 return err;
423}
424EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
425
426/**
427 * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device
428 * @core: device to send the command to
429 * @property: property address
430 * @value: property value
431 *
432 * Function returns 0 on succsess and negative error code on
433 * failure
434 */
435int si476x_core_cmd_set_property(struct si476x_core *core,
436 u16 property, u16 value)
437{
438 u8 resp[CMD_SET_PROPERTY_NRESP];
439 const u8 args[CMD_SET_PROPERTY_NARGS] = {
440 0x00,
441 msb(property),
442 lsb(property),
443 msb(value),
444 lsb(value),
445 };
446
447 return si476x_core_send_command(core, CMD_SET_PROPERTY,
448 args, ARRAY_SIZE(args),
449 resp, ARRAY_SIZE(resp),
450 SI476X_DEFAULT_TIMEOUT);
451}
452EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
453
454/**
455 * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device
456 * @core: device to send the command to
457 * @property: property address
458 *
459 * Function return the value of property as u16 on success or a
460 * negative error on failure
461 */
462int si476x_core_cmd_get_property(struct si476x_core *core, u16 property)
463{
464 int err;
465 u8 resp[CMD_GET_PROPERTY_NRESP];
466 const u8 args[CMD_GET_PROPERTY_NARGS] = {
467 0x00,
468 msb(property),
469 lsb(property),
470 };
471
472 err = si476x_core_send_command(core, CMD_GET_PROPERTY,
473 args, ARRAY_SIZE(args),
474 resp, ARRAY_SIZE(resp),
475 SI476X_DEFAULT_TIMEOUT);
476 if (err < 0)
477 return err;
478 else
479 return get_unaligned_be16(resp + 2);
480}
481EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
482
483/**
484 * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
485 * the device
486 * @core: device to send the command to
487 * @dclk: DCLK pin function configuration:
488 * #SI476X_DCLK_NOOP - do not modify the behaviour
489 * #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
490 * enable 1MOhm pulldown
491 * #SI476X_DCLK_DAUDIO - set the pin to be a part of digital
492 * audio interface
493 * @dfs: DFS pin function configuration:
494 * #SI476X_DFS_NOOP - do not modify the behaviour
495 * #SI476X_DFS_TRISTATE - put the pin in tristate condition,
496 * enable 1MOhm pulldown
497 * SI476X_DFS_DAUDIO - set the pin to be a part of digital
498 * audio interface
499 * @dout: - DOUT pin function configuration:
500 * SI476X_DOUT_NOOP - do not modify the behaviour
501 * SI476X_DOUT_TRISTATE - put the pin in tristate condition,
502 * enable 1MOhm pulldown
503 * SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
504 * port 1
505 * SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S
506 * port 1
507 * @xout: - XOUT pin function configuration:
508 * SI476X_XOUT_NOOP - do not modify the behaviour
509 * SI476X_XOUT_TRISTATE - put the pin in tristate condition,
510 * enable 1MOhm pulldown
511 * SI476X_XOUT_I2S_INPUT - set this pin to be digital in on I2S
512 * port 1
513 * SI476X_XOUT_MODE_SELECT - set this pin to be the input that
514 * selects the mode of the I2S audio
515 * combiner (analog or HD)
516 * [SI4761/63/65/67 Only]
517 *
518 * Function returns 0 on success and negative error code on failure
519 */
520int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *core,
521 enum si476x_dclk_config dclk,
522 enum si476x_dfs_config dfs,
523 enum si476x_dout_config dout,
524 enum si476x_xout_config xout)
525{
526 u8 resp[CMD_DIG_AUDIO_PIN_CFG_NRESP];
527 const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = {
528 PIN_CFG_BYTE(dclk),
529 PIN_CFG_BYTE(dfs),
530 PIN_CFG_BYTE(dout),
531 PIN_CFG_BYTE(xout),
532 };
533
534 return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG,
535 args, ARRAY_SIZE(args),
536 resp, ARRAY_SIZE(resp),
537 SI476X_DEFAULT_TIMEOUT);
538}
539EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
540
541/**
542 * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
543 * @core: - device to send the command to
544 * @iqclk: - IQCL pin function configuration:
545 * SI476X_IQCLK_NOOP - do not modify the behaviour
546 * SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
547 * enable 1MOhm pulldown
548 * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace
549 * in master mode
550 * @iqfs: - IQFS pin function configuration:
551 * SI476X_IQFS_NOOP - do not modify the behaviour
552 * SI476X_IQFS_TRISTATE - put the pin in tristate condition,
553 * enable 1MOhm pulldown
554 * SI476X_IQFS_IQ - set pin to be a part of I/Q interace
555 * in master mode
556 * @iout: - IOUT pin function configuration:
557 * SI476X_IOUT_NOOP - do not modify the behaviour
558 * SI476X_IOUT_TRISTATE - put the pin in tristate condition,
559 * enable 1MOhm pulldown
560 * SI476X_IOUT_OUTPUT - set pin to be I out
561 * @qout: - QOUT pin function configuration:
562 * SI476X_QOUT_NOOP - do not modify the behaviour
563 * SI476X_QOUT_TRISTATE - put the pin in tristate condition,
564 * enable 1MOhm pulldown
565 * SI476X_QOUT_OUTPUT - set pin to be Q out
566 *
567 * Function returns 0 on success and negative error code on failure
568 */
569int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core,
570 enum si476x_iqclk_config iqclk,
571 enum si476x_iqfs_config iqfs,
572 enum si476x_iout_config iout,
573 enum si476x_qout_config qout)
574{
575 u8 resp[CMD_ZIF_PIN_CFG_NRESP];
576 const u8 args[CMD_ZIF_PIN_CFG_NARGS] = {
577 PIN_CFG_BYTE(iqclk),
578 PIN_CFG_BYTE(iqfs),
579 PIN_CFG_BYTE(iout),
580 PIN_CFG_BYTE(qout),
581 };
582
583 return si476x_core_send_command(core, CMD_ZIF_PIN_CFG,
584 args, ARRAY_SIZE(args),
585 resp, ARRAY_SIZE(resp),
586 SI476X_DEFAULT_TIMEOUT);
587}
588EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
589
590/**
591 * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send
592 * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
593 * @core: - device to send the command to
594 * @icin: - ICIN pin function configuration:
595 * SI476X_ICIN_NOOP - do not modify the behaviour
596 * SI476X_ICIN_TRISTATE - put the pin in tristate condition,
597 * enable 1MOhm pulldown
598 * SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
599 * SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low
600 * SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link
601 * @icip: - ICIP pin function configuration:
602 * SI476X_ICIP_NOOP - do not modify the behaviour
603 * SI476X_ICIP_TRISTATE - put the pin in tristate condition,
604 * enable 1MOhm pulldown
605 * SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
606 * SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low
607 * SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link
608 * @icon: - ICON pin function configuration:
609 * SI476X_ICON_NOOP - do not modify the behaviour
610 * SI476X_ICON_TRISTATE - put the pin in tristate condition,
611 * enable 1MOhm pulldown
612 * SI476X_ICON_I2S - set the pin to be a part of audio
613 * interface in slave mode (DCLK)
614 * SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link
615 * @icop: - ICOP pin function configuration:
616 * SI476X_ICOP_NOOP - do not modify the behaviour
617 * SI476X_ICOP_TRISTATE - put the pin in tristate condition,
618 * enable 1MOhm pulldown
619 * SI476X_ICOP_I2S - set the pin to be a part of audio
620 * interface in slave mode (DOUT)
621 * [Si4761/63/65/67 Only]
622 * SI476X_ICOP_IC_LINK - set the pin to be a part of Inter-Chip link
623 *
624 * Function returns 0 on success and negative error code on failure
625 */
626int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core,
627 enum si476x_icin_config icin,
628 enum si476x_icip_config icip,
629 enum si476x_icon_config icon,
630 enum si476x_icop_config icop)
631{
632 u8 resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP];
633 const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = {
634 PIN_CFG_BYTE(icin),
635 PIN_CFG_BYTE(icip),
636 PIN_CFG_BYTE(icon),
637 PIN_CFG_BYTE(icop),
638 };
639
640 return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG,
641 args, ARRAY_SIZE(args),
642 resp, ARRAY_SIZE(resp),
643 SI476X_DEFAULT_TIMEOUT);
644}
645EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
646
647/**
648 * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
649 * device
650 * @core: - device to send the command to
651 * @lrout: - LROUT pin function configuration:
652 * SI476X_LROUT_NOOP - do not modify the behaviour
653 * SI476X_LROUT_TRISTATE - put the pin in tristate condition,
654 * enable 1MOhm pulldown
655 * SI476X_LROUT_AUDIO - set pin to be audio output
656 * SI476X_LROUT_MPX - set pin to be MPX output
657 *
658 * Function returns 0 on success and negative error code on failure
659 */
660int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core,
661 enum si476x_lrout_config lrout)
662{
663 u8 resp[CMD_ANA_AUDIO_PIN_CFG_NRESP];
664 const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = {
665 PIN_CFG_BYTE(lrout),
666 };
667
668 return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG,
669 args, ARRAY_SIZE(args),
670 resp, ARRAY_SIZE(resp),
671 SI476X_DEFAULT_TIMEOUT);
672}
673EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
674
675
676/**
677 * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device
678 * @core: - device to send the command to
679 * @intb: - INTB pin function configuration:
680 * SI476X_INTB_NOOP - do not modify the behaviour
681 * SI476X_INTB_TRISTATE - put the pin in tristate condition,
682 * enable 1MOhm pulldown
683 * SI476X_INTB_DAUDIO - set pin to be a part of digital
684 * audio interface in slave mode
685 * SI476X_INTB_IRQ - set pin to be an interrupt request line
686 * @a1: - A1 pin function configuration:
687 * SI476X_A1_NOOP - do not modify the behaviour
688 * SI476X_A1_TRISTATE - put the pin in tristate condition,
689 * enable 1MOhm pulldown
690 * SI476X_A1_IRQ - set pin to be an interrupt request line
691 *
692 * Function returns 0 on success and negative error code on failure
693 */
694static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core,
695 enum si476x_intb_config intb,
696 enum si476x_a1_config a1)
697{
698 u8 resp[CMD_INTB_PIN_CFG_A10_NRESP];
699 const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
700 PIN_CFG_BYTE(intb),
701 PIN_CFG_BYTE(a1),
702 };
703
704 return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
705 args, ARRAY_SIZE(args),
706 resp, ARRAY_SIZE(resp),
707 SI476X_DEFAULT_TIMEOUT);
708}
709
710static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
711 enum si476x_intb_config intb,
712 enum si476x_a1_config a1)
713{
714 u8 resp[CMD_INTB_PIN_CFG_A20_NRESP];
715 const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
716 PIN_CFG_BYTE(intb),
717 PIN_CFG_BYTE(a1),
718 };
719
720 return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
721 args, ARRAY_SIZE(args),
722 resp, ARRAY_SIZE(resp),
723 SI476X_DEFAULT_TIMEOUT);
724}
725
726
727
728/**
729 * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
730 * device
731 * @core: - device to send the command to
732 * @rsqargs: - pointer to a structure containing a group of sub-args
733 * relevant to sending the RSQ status command
734 * @report: - all signal quality information retured by the command
735 * (if NULL then the output of the command is ignored)
736 *
737 * Function returns 0 on success and negative error code on failure
738 */
739int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
740 struct si476x_rsq_status_args *rsqargs,
741 struct si476x_rsq_status_report *report)
742{
743 int err;
744 u8 resp[CMD_AM_RSQ_STATUS_NRESP];
745 const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
746 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
747 rsqargs->cancel << 1 | rsqargs->stcack,
748 };
749
750 err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
751 args, ARRAY_SIZE(args),
752 resp, ARRAY_SIZE(resp),
753 SI476X_DEFAULT_TIMEOUT);
754 /*
755 * Besides getting received signal quality information this
756 * command can be used to just acknowledge different interrupt
757 * flags in those cases it is useless to copy and parse
758 * received data so user can pass NULL, and thus avoid
759 * unnecessary copying.
760 */
761 if (!report)
762 return err;
763
764 report->snrhint = 0x08 & resp[1];
765 report->snrlint = 0x04 & resp[1];
766 report->rssihint = 0x02 & resp[1];
767 report->rssilint = 0x01 & resp[1];
768
769 report->bltf = 0x80 & resp[2];
770 report->snr_ready = 0x20 & resp[2];
771 report->rssiready = 0x08 & resp[2];
772 report->afcrl = 0x02 & resp[2];
773 report->valid = 0x01 & resp[2];
774
775 report->readfreq = get_unaligned_be16(resp + 3);
776 report->freqoff = resp[5];
777 report->rssi = resp[6];
778 report->snr = resp[7];
779 report->lassi = resp[9];
780 report->hassi = resp[10];
781 report->mult = resp[11];
782 report->dev = resp[12];
783
784 return err;
785}
786EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
787
788int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
789 struct si476x_acf_status_report *report)
790{
791 int err;
792 u8 resp[CMD_FM_ACF_STATUS_NRESP];
793 const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
794 0x0,
795 };
796
797 if (!report)
798 return -EINVAL;
799
800 err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
801 args, ARRAY_SIZE(args),
802 resp, ARRAY_SIZE(resp),
803 SI476X_DEFAULT_TIMEOUT);
804 if (err < 0)
805 return err;
806
807 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
808 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
809 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
810 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
811 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
812 report->smute = resp[2] & SI476X_ACF_SMUTE;
813 report->smattn = resp[3] & SI476X_ACF_SMATTN;
814 report->chbw = resp[4];
815 report->hicut = resp[5];
816 report->hiblend = resp[6];
817 report->pilot = resp[7] & SI476X_ACF_PILOT;
818 report->stblend = resp[7] & SI476X_ACF_STBLEND;
819
820 return err;
821}
822EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
823
824int si476x_core_cmd_am_acf_status(struct si476x_core *core,
825 struct si476x_acf_status_report *report)
826{
827 int err;
828 u8 resp[CMD_AM_ACF_STATUS_NRESP];
829 const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
830 0x0,
831 };
832
833 if (!report)
834 return -EINVAL;
835
836 err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
837 args, ARRAY_SIZE(args),
838 resp, ARRAY_SIZE(resp),
839 SI476X_DEFAULT_TIMEOUT);
840 if (err < 0)
841 return err;
842
843 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
844 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
845 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
846 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
847 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
848 report->smute = resp[2] & SI476X_ACF_SMUTE;
849 report->smattn = resp[3] & SI476X_ACF_SMATTN;
850 report->chbw = resp[4];
851 report->hicut = resp[5];
852
853 return err;
854}
855EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
856
857
858/**
859 * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
860 * device
861 * @core: - device to send the command to
862 * @seekup: - if set the direction of the search is 'up'
863 * @wrap: - if set seek wraps when hitting band limit
864 *
865 * This function begins search for a valid station. The station is
866 * considered valid when 'FM_VALID_SNR_THRESHOLD' and
867 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
868 * are met.
869} *
870 * Function returns 0 on success and negative error code on failure
871 */
872int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
873 bool seekup, bool wrap)
874{
875 u8 resp[CMD_FM_SEEK_START_NRESP];
876 const u8 args[CMD_FM_SEEK_START_NARGS] = {
877 seekup << 3 | wrap << 2,
878 };
879
880 return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
881 args, sizeof(args),
882 resp, sizeof(resp));
883}
884EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
885
886/**
887 * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
888 * device
889 * @core: - device to send the command to
890 * @status_only: - if set the data is not removed from RDSFIFO,
891 * RDSFIFOUSED is not decremented and data in all the
892 * rest RDS data contains the last valid info received
893 * @mtfifo: if set the command clears RDS receive FIFO
894 * @intack: if set the command clards the RDSINT bit.
895 * @report: - all signal quality information retured by the command
896 * (if NULL then the output of the command is ignored)
897 *
898 * Function returns 0 on success and negative error code on failure
899 */
900int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
901 bool status_only,
902 bool mtfifo,
903 bool intack,
904 struct si476x_rds_status_report *report)
905{
906 int err;
907 u8 resp[CMD_FM_RDS_STATUS_NRESP];
908 const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
909 status_only << 2 | mtfifo << 1 | intack,
910 };
911
912 err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
913 args, ARRAY_SIZE(args),
914 resp, ARRAY_SIZE(resp),
915 SI476X_DEFAULT_TIMEOUT);
916 /*
917 * Besides getting RDS status information this command can be
918 * used to just acknowledge different interrupt flags in those
919 * cases it is useless to copy and parse received data so user
920 * can pass NULL, and thus avoid unnecessary copying.
921 */
922 if (err < 0 || report == NULL)
923 return err;
924
925 report->rdstpptyint = 0x10 & resp[1];
926 report->rdspiint = 0x08 & resp[1];
927 report->rdssyncint = 0x02 & resp[1];
928 report->rdsfifoint = 0x01 & resp[1];
929
930 report->tpptyvalid = 0x10 & resp[2];
931 report->pivalid = 0x08 & resp[2];
932 report->rdssync = 0x02 & resp[2];
933 report->rdsfifolost = 0x01 & resp[2];
934
935 report->tp = 0x20 & resp[3];
936 report->pty = 0x1f & resp[3];
937
938 report->pi = get_unaligned_be16(resp + 4);
939 report->rdsfifoused = resp[6];
940
941 report->ble[V4L2_RDS_BLOCK_A] = 0xc0 & resp[7];
942 report->ble[V4L2_RDS_BLOCK_B] = 0x30 & resp[7];
943 report->ble[V4L2_RDS_BLOCK_C] = 0x0c & resp[7];
944 report->ble[V4L2_RDS_BLOCK_D] = 0x03 & resp[7];
945
946 report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
947 report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
948 report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
949
950 report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
951 report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
952 report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
953
954 report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
955 report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
956 report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
957
958 report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
959 report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
960 report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
961
962 return err;
963}
964EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
965
966int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
967 bool clear,
968 struct si476x_rds_blockcount_report *report)
969{
970 int err;
971 u8 resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
972 const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
973 clear,
974 };
975
976 if (!report)
977 return -EINVAL;
978
979 err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
980 args, ARRAY_SIZE(args),
981 resp, ARRAY_SIZE(resp),
982 SI476X_DEFAULT_TIMEOUT);
983
984 if (!err) {
985 report->expected = get_unaligned_be16(resp + 2);
986 report->received = get_unaligned_be16(resp + 4);
987 report->uncorrectable = get_unaligned_be16(resp + 6);
988 }
989
990 return err;
991}
992EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
993
994int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
995 enum si476x_phase_diversity_mode mode)
996{
997 u8 resp[CMD_FM_PHASE_DIVERSITY_NRESP];
998 const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
999 mode & 0x07,
1000 };
1001
1002 return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
1003 args, ARRAY_SIZE(args),
1004 resp, ARRAY_SIZE(resp),
1005 SI476X_DEFAULT_TIMEOUT);
1006}
1007EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
1008/**
1009 * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
1010 * status
1011 *
1012 * @core: si476x device
1013 *
1014 * NOTE caller must hold core lock
1015 *
1016 * Function returns the value of the status bit in case of success and
1017 * negative error code in case of failre.
1018 */
1019int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
1020{
1021 int err;
1022 u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
1023
1024 err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
1025 NULL, 0,
1026 resp, ARRAY_SIZE(resp),
1027 SI476X_DEFAULT_TIMEOUT);
1028
1029 return (err < 0) ? err : resp[1];
1030}
1031EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
1032
1033
1034/**
1035 * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the
1036 * device
1037 * @core: - device to send the command to
1038 * @seekup: - if set the direction of the search is 'up'
1039 * @wrap: - if set seek wraps when hitting band limit
1040 *
1041 * This function begins search for a valid station. The station is
1042 * considered valid when 'FM_VALID_SNR_THRESHOLD' and
1043 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
1044 * are met.
1045 *
1046 * Function returns 0 on success and negative error code on failure
1047 */
1048int si476x_core_cmd_am_seek_start(struct si476x_core *core,
1049 bool seekup, bool wrap)
1050{
1051 u8 resp[CMD_AM_SEEK_START_NRESP];
1052 const u8 args[CMD_AM_SEEK_START_NARGS] = {
1053 seekup << 3 | wrap << 2,
1054 };
1055
1056 return si476x_cmd_tune_seek_freq(core, CMD_AM_SEEK_START,
1057 args, sizeof(args),
1058 resp, sizeof(resp));
1059}
1060EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1061
1062
1063
1064static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
1065 struct si476x_power_up_args *puargs)
1066{
1067 u8 resp[CMD_POWER_UP_A10_NRESP];
1068 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1069 const bool ctsen = (core->client->irq != 0);
1070 const u8 args[CMD_POWER_UP_A10_NARGS] = {
1071 0xF7, /* Reserved, always 0xF7 */
1072 0x3F & puargs->xcload, /* First two bits are reserved to be
1073 * zeros */
1074 ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
1075 * are reserved to
1076 * be written as 0x7 */
1077 puargs->func << 4 | puargs->freq,
1078 0x11, /* Reserved, always 0x11 */
1079 };
1080
1081 return si476x_core_send_command(core, CMD_POWER_UP,
1082 args, ARRAY_SIZE(args),
1083 resp, ARRAY_SIZE(resp),
1084 SI476X_TIMEOUT_POWER_UP);
1085}
1086
1087static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
1088 struct si476x_power_up_args *puargs)
1089{
1090 u8 resp[CMD_POWER_UP_A20_NRESP];
1091 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1092 const bool ctsen = (core->client->irq != 0);
1093 const u8 args[CMD_POWER_UP_A20_NARGS] = {
1094 puargs->ibias6x << 7 | puargs->xstart,
1095 0x3F & puargs->xcload, /* First two bits are reserved to be
1096 * zeros */
1097 ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
1098 puargs->xbiashc << 3 | puargs->xbias,
1099 puargs->func << 4 | puargs->freq,
1100 0x10 | puargs->xmode,
1101 };
1102
1103 return si476x_core_send_command(core, CMD_POWER_UP,
1104 args, ARRAY_SIZE(args),
1105 resp, ARRAY_SIZE(resp),
1106 SI476X_TIMEOUT_POWER_UP);
1107}
1108
1109static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
1110 struct si476x_power_down_args *pdargs)
1111{
1112 u8 resp[CMD_POWER_DOWN_A10_NRESP];
1113
1114 return si476x_core_send_command(core, CMD_POWER_DOWN,
1115 NULL, 0,
1116 resp, ARRAY_SIZE(resp),
1117 SI476X_DEFAULT_TIMEOUT);
1118}
1119
1120static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
1121 struct si476x_power_down_args *pdargs)
1122{
1123 u8 resp[CMD_POWER_DOWN_A20_NRESP];
1124 const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
1125 pdargs->xosc,
1126 };
1127 return si476x_core_send_command(core, CMD_POWER_DOWN,
1128 args, ARRAY_SIZE(args),
1129 resp, ARRAY_SIZE(resp),
1130 SI476X_DEFAULT_TIMEOUT);
1131}
1132
1133static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
1134 struct si476x_tune_freq_args *tuneargs)
1135{
1136
1137 const int am_freq = tuneargs->freq;
1138 u8 resp[CMD_AM_TUNE_FREQ_NRESP];
1139 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1140 (tuneargs->hd << 6),
1141 msb(am_freq),
1142 lsb(am_freq),
1143 };
1144
1145 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
1146 sizeof(args),
1147 resp, sizeof(resp));
1148}
1149
1150static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
1151 struct si476x_tune_freq_args *tuneargs)
1152{
1153 const int am_freq = tuneargs->freq;
1154 u8 resp[CMD_AM_TUNE_FREQ_NRESP];
1155 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1156 (tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
1157 msb(am_freq),
1158 lsb(am_freq),
1159 };
1160
1161 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
1162 args, sizeof(args),
1163 resp, sizeof(resp));
1164}
1165
1166static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
1167 struct si476x_rsq_status_args *rsqargs,
1168 struct si476x_rsq_status_report *report)
1169{
1170 int err;
1171 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1172 const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
1173 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
1174 rsqargs->cancel << 1 | rsqargs->stcack,
1175 };
1176
1177 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1178 args, ARRAY_SIZE(args),
1179 resp, ARRAY_SIZE(resp),
1180 SI476X_DEFAULT_TIMEOUT);
1181 /*
1182 * Besides getting received signal quality information this
1183 * command can be used to just acknowledge different interrupt
1184 * flags in those cases it is useless to copy and parse
1185 * received data so user can pass NULL, and thus avoid
1186 * unnecessary copying.
1187 */
1188 if (err < 0 || report == NULL)
1189 return err;
1190
1191 report->multhint = 0x80 & resp[1];
1192 report->multlint = 0x40 & resp[1];
1193 report->snrhint = 0x08 & resp[1];
1194 report->snrlint = 0x04 & resp[1];
1195 report->rssihint = 0x02 & resp[1];
1196 report->rssilint = 0x01 & resp[1];
1197
1198 report->bltf = 0x80 & resp[2];
1199 report->snr_ready = 0x20 & resp[2];
1200 report->rssiready = 0x08 & resp[2];
1201 report->afcrl = 0x02 & resp[2];
1202 report->valid = 0x01 & resp[2];
1203
1204 report->readfreq = get_unaligned_be16(resp + 3);
1205 report->freqoff = resp[5];
1206 report->rssi = resp[6];
1207 report->snr = resp[7];
1208 report->lassi = resp[9];
1209 report->hassi = resp[10];
1210 report->mult = resp[11];
1211 report->dev = resp[12];
1212 report->readantcap = get_unaligned_be16(resp + 13);
1213 report->assi = resp[15];
1214 report->usn = resp[16];
1215
1216 return err;
1217}
1218
1219static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
1220 struct si476x_rsq_status_args *rsqargs,
1221 struct si476x_rsq_status_report *report)
1222{
1223 int err;
1224 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1225 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1226 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1227 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1228 rsqargs->stcack,
1229 };
1230
1231 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1232 args, ARRAY_SIZE(args),
1233 resp, ARRAY_SIZE(resp),
1234 SI476X_DEFAULT_TIMEOUT);
1235 /*
1236 * Besides getting received signal quality information this
1237 * command can be used to just acknowledge different interrupt
1238 * flags in those cases it is useless to copy and parse
1239 * received data so user can pass NULL, and thus avoid
1240 * unnecessary copying.
1241 */
1242 if (err < 0 || report == NULL)
1243 return err;
1244
1245 report->multhint = 0x80 & resp[1];
1246 report->multlint = 0x40 & resp[1];
1247 report->snrhint = 0x08 & resp[1];
1248 report->snrlint = 0x04 & resp[1];
1249 report->rssihint = 0x02 & resp[1];
1250 report->rssilint = 0x01 & resp[1];
1251
1252 report->bltf = 0x80 & resp[2];
1253 report->snr_ready = 0x20 & resp[2];
1254 report->rssiready = 0x08 & resp[2];
1255 report->afcrl = 0x02 & resp[2];
1256 report->valid = 0x01 & resp[2];
1257
1258 report->readfreq = get_unaligned_be16(resp + 3);
1259 report->freqoff = resp[5];
1260 report->rssi = resp[6];
1261 report->snr = resp[7];
1262 report->lassi = resp[9];
1263 report->hassi = resp[10];
1264 report->mult = resp[11];
1265 report->dev = resp[12];
1266 report->readantcap = get_unaligned_be16(resp + 13);
1267 report->assi = resp[15];
1268 report->usn = resp[16];
1269
1270 return err;
1271}
1272
1273
1274static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
1275 struct si476x_rsq_status_args *rsqargs,
1276 struct si476x_rsq_status_report *report)
1277{
1278 int err;
1279 u8 resp[CMD_FM_RSQ_STATUS_A30_NRESP];
1280 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1281 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1282 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1283 rsqargs->stcack,
1284 };
1285
1286 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1287 args, ARRAY_SIZE(args),
1288 resp, ARRAY_SIZE(resp),
1289 SI476X_DEFAULT_TIMEOUT);
1290 /*
1291 * Besides getting received signal quality information this
1292 * command can be used to just acknowledge different interrupt
1293 * flags in those cases it is useless to copy and parse
1294 * received data so user can pass NULL, and thus avoid
1295 * unnecessary copying.
1296 */
1297 if (err < 0 || report == NULL)
1298 return err;
1299
1300 report->multhint = 0x80 & resp[1];
1301 report->multlint = 0x40 & resp[1];
1302 report->snrhint = 0x08 & resp[1];
1303 report->snrlint = 0x04 & resp[1];
1304 report->rssihint = 0x02 & resp[1];
1305 report->rssilint = 0x01 & resp[1];
1306
1307 report->bltf = 0x80 & resp[2];
1308 report->snr_ready = 0x20 & resp[2];
1309 report->rssiready = 0x08 & resp[2];
1310 report->injside = 0x04 & resp[2];
1311 report->afcrl = 0x02 & resp[2];
1312 report->valid = 0x01 & resp[2];
1313
1314 report->readfreq = get_unaligned_be16(resp + 3);
1315 report->freqoff = resp[5];
1316 report->rssi = resp[6];
1317 report->snr = resp[7];
1318 report->issi = resp[8];
1319 report->lassi = resp[9];
1320 report->hassi = resp[10];
1321 report->mult = resp[11];
1322 report->dev = resp[12];
1323 report->readantcap = get_unaligned_be16(resp + 13);
1324 report->assi = resp[15];
1325 report->usn = resp[16];
1326
1327 report->pilotdev = resp[17];
1328 report->rdsdev = resp[18];
1329 report->assidev = resp[19];
1330 report->strongdev = resp[20];
1331 report->rdspi = get_unaligned_be16(resp + 21);
1332
1333 return err;
1334}
1335
1336static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
1337 struct si476x_tune_freq_args *tuneargs)
1338{
1339 u8 resp[CMD_FM_TUNE_FREQ_NRESP];
1340 const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
1341 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1342 | (tuneargs->smoothmetrics << 2),
1343 msb(tuneargs->freq),
1344 lsb(tuneargs->freq),
1345 msb(tuneargs->antcap),
1346 lsb(tuneargs->antcap)
1347 };
1348
1349 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1350 args, sizeof(args),
1351 resp, sizeof(resp));
1352}
1353
1354static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
1355 struct si476x_tune_freq_args *tuneargs)
1356{
1357 u8 resp[CMD_FM_TUNE_FREQ_NRESP];
1358 const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
1359 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1360 | (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
1361 msb(tuneargs->freq),
1362 lsb(tuneargs->freq),
1363 };
1364
1365 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1366 args, sizeof(args),
1367 resp, sizeof(resp));
1368}
1369
1370static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
1371 struct si476x_agc_status_report *report)
1372{
1373 int err;
1374 u8 resp[CMD_AGC_STATUS_NRESP_A20];
1375
1376 if (!report)
1377 return -EINVAL;
1378
1379 err = si476x_core_send_command(core, CMD_AGC_STATUS,
1380 NULL, 0,
1381 resp, ARRAY_SIZE(resp),
1382 SI476X_DEFAULT_TIMEOUT);
1383 if (err < 0)
1384 return err;
1385
1386 report->mxhi = resp[1] & SI476X_AGC_MXHI;
1387 report->mxlo = resp[1] & SI476X_AGC_MXLO;
1388 report->lnahi = resp[1] & SI476X_AGC_LNAHI;
1389 report->lnalo = resp[1] & SI476X_AGC_LNALO;
1390 report->fmagc1 = resp[2];
1391 report->fmagc2 = resp[3];
1392 report->pgagain = resp[4];
1393 report->fmwblang = resp[5];
1394
1395 return err;
1396}
1397
1398static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
1399 struct si476x_agc_status_report *report)
1400{
1401 int err;
1402 u8 resp[CMD_AGC_STATUS_NRESP_A10];
1403
1404 if (!report)
1405 return -EINVAL;
1406
1407 err = si476x_core_send_command(core, CMD_AGC_STATUS,
1408 NULL, 0,
1409 resp, ARRAY_SIZE(resp),
1410 SI476X_DEFAULT_TIMEOUT);
1411 if (err < 0)
1412 return err;
1413
1414 report->mxhi = resp[1] & SI476X_AGC_MXHI;
1415 report->mxlo = resp[1] & SI476X_AGC_MXLO;
1416 report->lnahi = resp[1] & SI476X_AGC_LNAHI;
1417 report->lnalo = resp[1] & SI476X_AGC_LNALO;
1418
1419 return err;
1420}
1421
1422typedef int (*tune_freq_func_t) (struct si476x_core *core,
1423 struct si476x_tune_freq_args *tuneargs);
1424
1425static struct {
1426 int (*power_up)(struct si476x_core *,
1427 struct si476x_power_up_args *);
1428 int (*power_down)(struct si476x_core *,
1429 struct si476x_power_down_args *);
1430
1431 tune_freq_func_t fm_tune_freq;
1432 tune_freq_func_t am_tune_freq;
1433
1434 int (*fm_rsq_status)(struct si476x_core *,
1435 struct si476x_rsq_status_args *,
1436 struct si476x_rsq_status_report *);
1437
1438 int (*agc_status)(struct si476x_core *,
1439 struct si476x_agc_status_report *);
1440 int (*intb_pin_cfg)(struct si476x_core *core,
1441 enum si476x_intb_config intb,
1442 enum si476x_a1_config a1);
1443} si476x_cmds_vtable[] = {
1444 [SI476X_REVISION_A10] = {
1445 .power_up = si476x_core_cmd_power_up_a10,
1446 .power_down = si476x_core_cmd_power_down_a10,
1447 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a10,
1448 .am_tune_freq = si476x_core_cmd_am_tune_freq_a10,
1449 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a10,
1450 .agc_status = si476x_core_cmd_agc_status_a10,
1451 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a10,
1452 },
1453 [SI476X_REVISION_A20] = {
1454 .power_up = si476x_core_cmd_power_up_a20,
1455 .power_down = si476x_core_cmd_power_down_a20,
1456 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
1457 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
1458 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a20,
1459 .agc_status = si476x_core_cmd_agc_status_a20,
1460 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
1461 },
1462 [SI476X_REVISION_A30] = {
1463 .power_up = si476x_core_cmd_power_up_a20,
1464 .power_down = si476x_core_cmd_power_down_a20,
1465 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
1466 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
1467 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a30,
1468 .agc_status = si476x_core_cmd_agc_status_a20,
1469 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
1470 },
1471};
1472
1473int si476x_core_cmd_power_up(struct si476x_core *core,
1474 struct si476x_power_up_args *args)
1475{
1476 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1477 core->revision == -1);
1478 return si476x_cmds_vtable[core->revision].power_up(core, args);
1479}
1480EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1481
1482int si476x_core_cmd_power_down(struct si476x_core *core,
1483 struct si476x_power_down_args *args)
1484{
1485 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1486 core->revision == -1);
1487 return si476x_cmds_vtable[core->revision].power_down(core, args);
1488}
1489EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1490
1491int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
1492 struct si476x_tune_freq_args *args)
1493{
1494 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1495 core->revision == -1);
1496 return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
1497}
1498EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1499
1500int si476x_core_cmd_am_tune_freq(struct si476x_core *core,
1501 struct si476x_tune_freq_args *args)
1502{
1503 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1504 core->revision == -1);
1505 return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
1506}
1507EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1508
1509int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
1510 struct si476x_rsq_status_args *args,
1511 struct si476x_rsq_status_report *report)
1512
1513{
1514 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1515 core->revision == -1);
1516 return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
1517 report);
1518}
1519EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1520
1521int si476x_core_cmd_agc_status(struct si476x_core *core,
1522 struct si476x_agc_status_report *report)
1523
1524{
1525 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1526 core->revision == -1);
1527 return si476x_cmds_vtable[core->revision].agc_status(core, report);
1528}
1529EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1530
1531int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
1532 enum si476x_intb_config intb,
1533 enum si476x_a1_config a1)
1534{
1535 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1536 core->revision == -1);
1537
1538 return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
1539}
1540EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1541
1542MODULE_LICENSE("GPL");
1543MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1544MODULE_DESCRIPTION("API for command exchange for si476x");
1/*
2 * drivers/mfd/si476x-cmd.c -- Subroutines implementing command
3 * protocol of si476x series of chips
4 *
5 * Copyright (C) 2012 Innovative Converged Devices(ICD)
6 * Copyright (C) 2013 Andrey Smirnov
7 *
8 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 */
20
21#include <linux/module.h>
22#include <linux/completion.h>
23#include <linux/delay.h>
24#include <linux/atomic.h>
25#include <linux/i2c.h>
26#include <linux/device.h>
27#include <linux/gpio.h>
28#include <linux/videodev2.h>
29
30#include <linux/mfd/si476x-core.h>
31
32#include <asm/unaligned.h>
33
34#define msb(x) ((u8)((u16) x >> 8))
35#define lsb(x) ((u8)((u16) x & 0x00FF))
36
37
38
39#define CMD_POWER_UP 0x01
40#define CMD_POWER_UP_A10_NRESP 1
41#define CMD_POWER_UP_A10_NARGS 5
42
43#define CMD_POWER_UP_A20_NRESP 1
44#define CMD_POWER_UP_A20_NARGS 5
45
46#define POWER_UP_DELAY_MS 110
47
48#define CMD_POWER_DOWN 0x11
49#define CMD_POWER_DOWN_A10_NRESP 1
50
51#define CMD_POWER_DOWN_A20_NRESP 1
52#define CMD_POWER_DOWN_A20_NARGS 1
53
54#define CMD_FUNC_INFO 0x12
55#define CMD_FUNC_INFO_NRESP 7
56
57#define CMD_SET_PROPERTY 0x13
58#define CMD_SET_PROPERTY_NARGS 5
59#define CMD_SET_PROPERTY_NRESP 1
60
61#define CMD_GET_PROPERTY 0x14
62#define CMD_GET_PROPERTY_NARGS 3
63#define CMD_GET_PROPERTY_NRESP 4
64
65#define CMD_AGC_STATUS 0x17
66#define CMD_AGC_STATUS_NRESP_A10 2
67#define CMD_AGC_STATUS_NRESP_A20 6
68
69#define PIN_CFG_BYTE(x) (0x7F & (x))
70#define CMD_DIG_AUDIO_PIN_CFG 0x18
71#define CMD_DIG_AUDIO_PIN_CFG_NARGS 4
72#define CMD_DIG_AUDIO_PIN_CFG_NRESP 5
73
74#define CMD_ZIF_PIN_CFG 0x19
75#define CMD_ZIF_PIN_CFG_NARGS 4
76#define CMD_ZIF_PIN_CFG_NRESP 5
77
78#define CMD_IC_LINK_GPO_CTL_PIN_CFG 0x1A
79#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS 4
80#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP 5
81
82#define CMD_ANA_AUDIO_PIN_CFG 0x1B
83#define CMD_ANA_AUDIO_PIN_CFG_NARGS 1
84#define CMD_ANA_AUDIO_PIN_CFG_NRESP 2
85
86#define CMD_INTB_PIN_CFG 0x1C
87#define CMD_INTB_PIN_CFG_NARGS 2
88#define CMD_INTB_PIN_CFG_A10_NRESP 6
89#define CMD_INTB_PIN_CFG_A20_NRESP 3
90
91#define CMD_FM_TUNE_FREQ 0x30
92#define CMD_FM_TUNE_FREQ_A10_NARGS 5
93#define CMD_FM_TUNE_FREQ_A20_NARGS 3
94#define CMD_FM_TUNE_FREQ_NRESP 1
95
96#define CMD_FM_RSQ_STATUS 0x32
97
98#define CMD_FM_RSQ_STATUS_A10_NARGS 1
99#define CMD_FM_RSQ_STATUS_A10_NRESP 17
100#define CMD_FM_RSQ_STATUS_A30_NARGS 1
101#define CMD_FM_RSQ_STATUS_A30_NRESP 23
102
103
104#define CMD_FM_SEEK_START 0x31
105#define CMD_FM_SEEK_START_NARGS 1
106#define CMD_FM_SEEK_START_NRESP 1
107
108#define CMD_FM_RDS_STATUS 0x36
109#define CMD_FM_RDS_STATUS_NARGS 1
110#define CMD_FM_RDS_STATUS_NRESP 16
111
112#define CMD_FM_RDS_BLOCKCOUNT 0x37
113#define CMD_FM_RDS_BLOCKCOUNT_NARGS 1
114#define CMD_FM_RDS_BLOCKCOUNT_NRESP 8
115
116#define CMD_FM_PHASE_DIVERSITY 0x38
117#define CMD_FM_PHASE_DIVERSITY_NARGS 1
118#define CMD_FM_PHASE_DIVERSITY_NRESP 1
119
120#define CMD_FM_PHASE_DIV_STATUS 0x39
121#define CMD_FM_PHASE_DIV_STATUS_NRESP 2
122
123#define CMD_AM_TUNE_FREQ 0x40
124#define CMD_AM_TUNE_FREQ_NARGS 3
125#define CMD_AM_TUNE_FREQ_NRESP 1
126
127#define CMD_AM_RSQ_STATUS 0x42
128#define CMD_AM_RSQ_STATUS_NARGS 1
129#define CMD_AM_RSQ_STATUS_NRESP 13
130
131#define CMD_AM_SEEK_START 0x41
132#define CMD_AM_SEEK_START_NARGS 1
133#define CMD_AM_SEEK_START_NRESP 1
134
135
136#define CMD_AM_ACF_STATUS 0x45
137#define CMD_AM_ACF_STATUS_NRESP 6
138#define CMD_AM_ACF_STATUS_NARGS 1
139
140#define CMD_FM_ACF_STATUS 0x35
141#define CMD_FM_ACF_STATUS_NRESP 8
142#define CMD_FM_ACF_STATUS_NARGS 1
143
144#define CMD_MAX_ARGS_COUNT (10)
145
146
147enum si476x_acf_status_report_bits {
148 SI476X_ACF_BLEND_INT = (1 << 4),
149 SI476X_ACF_HIBLEND_INT = (1 << 3),
150 SI476X_ACF_HICUT_INT = (1 << 2),
151 SI476X_ACF_CHBW_INT = (1 << 1),
152 SI476X_ACF_SOFTMUTE_INT = (1 << 0),
153
154 SI476X_ACF_SMUTE = (1 << 0),
155 SI476X_ACF_SMATTN = 0x1f,
156 SI476X_ACF_PILOT = (1 << 7),
157 SI476X_ACF_STBLEND = ~SI476X_ACF_PILOT,
158};
159
160enum si476x_agc_status_report_bits {
161 SI476X_AGC_MXHI = (1 << 5),
162 SI476X_AGC_MXLO = (1 << 4),
163 SI476X_AGC_LNAHI = (1 << 3),
164 SI476X_AGC_LNALO = (1 << 2),
165};
166
167enum si476x_errors {
168 SI476X_ERR_BAD_COMMAND = 0x10,
169 SI476X_ERR_BAD_ARG1 = 0x11,
170 SI476X_ERR_BAD_ARG2 = 0x12,
171 SI476X_ERR_BAD_ARG3 = 0x13,
172 SI476X_ERR_BAD_ARG4 = 0x14,
173 SI476X_ERR_BUSY = 0x18,
174 SI476X_ERR_BAD_INTERNAL_MEMORY = 0x20,
175 SI476X_ERR_BAD_PATCH = 0x30,
176 SI476X_ERR_BAD_BOOT_MODE = 0x31,
177 SI476X_ERR_BAD_PROPERTY = 0x40,
178};
179
180static int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
181{
182 int err;
183 char *cause;
184 u8 buffer[2];
185
186 if (core->revision != SI476X_REVISION_A10) {
187 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
188 buffer, sizeof(buffer));
189 if (err == sizeof(buffer)) {
190 switch (buffer[1]) {
191 case SI476X_ERR_BAD_COMMAND:
192 cause = "Bad command";
193 err = -EINVAL;
194 break;
195 case SI476X_ERR_BAD_ARG1:
196 cause = "Bad argument #1";
197 err = -EINVAL;
198 break;
199 case SI476X_ERR_BAD_ARG2:
200 cause = "Bad argument #2";
201 err = -EINVAL;
202 break;
203 case SI476X_ERR_BAD_ARG3:
204 cause = "Bad argument #3";
205 err = -EINVAL;
206 break;
207 case SI476X_ERR_BAD_ARG4:
208 cause = "Bad argument #4";
209 err = -EINVAL;
210 break;
211 case SI476X_ERR_BUSY:
212 cause = "Chip is busy";
213 err = -EBUSY;
214 break;
215 case SI476X_ERR_BAD_INTERNAL_MEMORY:
216 cause = "Bad internal memory";
217 err = -EIO;
218 break;
219 case SI476X_ERR_BAD_PATCH:
220 cause = "Bad patch";
221 err = -EINVAL;
222 break;
223 case SI476X_ERR_BAD_BOOT_MODE:
224 cause = "Bad boot mode";
225 err = -EINVAL;
226 break;
227 case SI476X_ERR_BAD_PROPERTY:
228 cause = "Bad property";
229 err = -EINVAL;
230 break;
231 default:
232 cause = "Unknown";
233 err = -EIO;
234 }
235
236 dev_err(&core->client->dev,
237 "[Chip error status]: %s\n", cause);
238 } else {
239 dev_err(&core->client->dev,
240 "Failed to fetch error code\n");
241 err = (err >= 0) ? -EIO : err;
242 }
243 } else {
244 err = -EIO;
245 }
246
247 return err;
248}
249
250/**
251 * si476x_core_send_command() - sends a command to si476x and waits its
252 * response
253 * @core: si476x_device structure for the device we are
254 * communicating with
255 * @command: command id
256 * @args: command arguments we are sending
257 * @argn: actual size of @args
258 * @response: buffer to place the expected response from the device
259 * @respn: actual size of @response
260 * @usecs: amount of time to wait before reading the response (in
261 * usecs)
262 *
263 * Function returns 0 on succsess and negative error code on
264 * failure
265 */
266static int si476x_core_send_command(struct si476x_core *core,
267 const u8 command,
268 const u8 args[],
269 const int argn,
270 u8 resp[],
271 const int respn,
272 const int usecs)
273{
274 struct i2c_client *client = core->client;
275 int err;
276 u8 data[CMD_MAX_ARGS_COUNT + 1];
277
278 if (argn > CMD_MAX_ARGS_COUNT) {
279 err = -ENOMEM;
280 goto exit;
281 }
282
283 if (!client->adapter) {
284 err = -ENODEV;
285 goto exit;
286 }
287
288 /* First send the command and its arguments */
289 data[0] = command;
290 memcpy(&data[1], args, argn);
291 dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data);
292
293 err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND,
294 (char *) data, argn + 1);
295 if (err != argn + 1) {
296 dev_err(&core->client->dev,
297 "Error while sending command 0x%02x\n",
298 command);
299 err = (err >= 0) ? -EIO : err;
300 goto exit;
301 }
302 /* Set CTS to zero only after the command is send to avoid
303 * possible racing conditions when working in polling mode */
304 atomic_set(&core->cts, 0);
305
306 /* if (unlikely(command == CMD_POWER_DOWN) */
307 if (!wait_event_timeout(core->command,
308 atomic_read(&core->cts),
309 usecs_to_jiffies(usecs) + 1))
310 dev_warn(&core->client->dev,
311 "(%s) [CMD 0x%02x] Answer timeout.\n",
312 __func__, command);
313
314 /*
315 When working in polling mode, for some reason the tuner will
316 report CTS bit as being set in the first status byte read,
317 but all the consequtive ones will return zeros until the
318 tuner is actually completed the POWER_UP command. To
319 workaround that we wait for second CTS to be reported
320 */
321 if (unlikely(!core->client->irq && command == CMD_POWER_UP)) {
322 if (!wait_event_timeout(core->command,
323 atomic_read(&core->cts),
324 usecs_to_jiffies(usecs) + 1))
325 dev_warn(&core->client->dev,
326 "(%s) Power up took too much time.\n",
327 __func__);
328 }
329
330 /* Then get the response */
331 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn);
332 if (err != respn) {
333 dev_err(&core->client->dev,
334 "Error while reading response for command 0x%02x\n",
335 command);
336 err = (err >= 0) ? -EIO : err;
337 goto exit;
338 }
339 dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp);
340
341 err = 0;
342
343 if (resp[0] & SI476X_ERR) {
344 dev_err(&core->client->dev,
345 "[CMD 0x%02x] Chip set error flag\n", command);
346 err = si476x_core_parse_and_nag_about_error(core);
347 goto exit;
348 }
349
350 if (!(resp[0] & SI476X_CTS))
351 err = -EBUSY;
352exit:
353 return err;
354}
355
356static int si476x_cmd_clear_stc(struct si476x_core *core)
357{
358 int err;
359 struct si476x_rsq_status_args args = {
360 .primary = false,
361 .rsqack = false,
362 .attune = false,
363 .cancel = false,
364 .stcack = true,
365 };
366
367 switch (core->power_up_parameters.func) {
368 case SI476X_FUNC_FM_RECEIVER:
369 err = si476x_core_cmd_fm_rsq_status(core, &args, NULL);
370 break;
371 case SI476X_FUNC_AM_RECEIVER:
372 err = si476x_core_cmd_am_rsq_status(core, &args, NULL);
373 break;
374 default:
375 err = -EINVAL;
376 }
377
378 return err;
379}
380
381static int si476x_cmd_tune_seek_freq(struct si476x_core *core,
382 uint8_t cmd,
383 const uint8_t args[], size_t argn,
384 uint8_t *resp, size_t respn)
385{
386 int err;
387
388
389 atomic_set(&core->stc, 0);
390 err = si476x_core_send_command(core, cmd, args, argn, resp, respn,
391 SI476X_TIMEOUT_TUNE);
392 if (!err) {
393 wait_event_killable(core->tuning,
394 atomic_read(&core->stc));
395 si476x_cmd_clear_stc(core);
396 }
397
398 return err;
399}
400
401/**
402 * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device
403 * @core: device to send the command to
404 * @info: struct si476x_func_info to fill all the information
405 * returned by the command
406 *
407 * The command requests the firmware and patch version for currently
408 * loaded firmware (dependent on the function of the device FM/AM/WB)
409 *
410 * Function returns 0 on succsess and negative error code on
411 * failure
412 */
413int si476x_core_cmd_func_info(struct si476x_core *core,
414 struct si476x_func_info *info)
415{
416 int err;
417 u8 resp[CMD_FUNC_INFO_NRESP];
418
419 err = si476x_core_send_command(core, CMD_FUNC_INFO,
420 NULL, 0,
421 resp, ARRAY_SIZE(resp),
422 SI476X_DEFAULT_TIMEOUT);
423
424 info->firmware.major = resp[1];
425 info->firmware.minor[0] = resp[2];
426 info->firmware.minor[1] = resp[3];
427
428 info->patch_id = ((u16) resp[4] << 8) | resp[5];
429 info->func = resp[6];
430
431 return err;
432}
433EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
434
435/**
436 * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device
437 * @core: device to send the command to
438 * @property: property address
439 * @value: property value
440 *
441 * Function returns 0 on succsess and negative error code on
442 * failure
443 */
444int si476x_core_cmd_set_property(struct si476x_core *core,
445 u16 property, u16 value)
446{
447 u8 resp[CMD_SET_PROPERTY_NRESP];
448 const u8 args[CMD_SET_PROPERTY_NARGS] = {
449 0x00,
450 msb(property),
451 lsb(property),
452 msb(value),
453 lsb(value),
454 };
455
456 return si476x_core_send_command(core, CMD_SET_PROPERTY,
457 args, ARRAY_SIZE(args),
458 resp, ARRAY_SIZE(resp),
459 SI476X_DEFAULT_TIMEOUT);
460}
461EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
462
463/**
464 * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device
465 * @core: device to send the command to
466 * @property: property address
467 *
468 * Function return the value of property as u16 on success or a
469 * negative error on failure
470 */
471int si476x_core_cmd_get_property(struct si476x_core *core, u16 property)
472{
473 int err;
474 u8 resp[CMD_GET_PROPERTY_NRESP];
475 const u8 args[CMD_GET_PROPERTY_NARGS] = {
476 0x00,
477 msb(property),
478 lsb(property),
479 };
480
481 err = si476x_core_send_command(core, CMD_GET_PROPERTY,
482 args, ARRAY_SIZE(args),
483 resp, ARRAY_SIZE(resp),
484 SI476X_DEFAULT_TIMEOUT);
485 if (err < 0)
486 return err;
487 else
488 return get_unaligned_be16(resp + 2);
489}
490EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
491
492/**
493 * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
494 * the device
495 * @core: device to send the command to
496 * @dclk: DCLK pin function configuration:
497 * #SI476X_DCLK_NOOP - do not modify the behaviour
498 * #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
499 * enable 1MOhm pulldown
500 * #SI476X_DCLK_DAUDIO - set the pin to be a part of digital
501 * audio interface
502 * @dfs: DFS pin function configuration:
503 * #SI476X_DFS_NOOP - do not modify the behaviour
504 * #SI476X_DFS_TRISTATE - put the pin in tristate condition,
505 * enable 1MOhm pulldown
506 * SI476X_DFS_DAUDIO - set the pin to be a part of digital
507 * audio interface
508 * @dout - DOUT pin function configuration:
509 * SI476X_DOUT_NOOP - do not modify the behaviour
510 * SI476X_DOUT_TRISTATE - put the pin in tristate condition,
511 * enable 1MOhm pulldown
512 * SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
513 * port 1
514 * SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S
515 * port 1
516 * @xout - XOUT pin function configuration:
517 * SI476X_XOUT_NOOP - do not modify the behaviour
518 * SI476X_XOUT_TRISTATE - put the pin in tristate condition,
519 * enable 1MOhm pulldown
520 * SI476X_XOUT_I2S_INPUT - set this pin to be digital in on I2S
521 * port 1
522 * SI476X_XOUT_MODE_SELECT - set this pin to be the input that
523 * selects the mode of the I2S audio
524 * combiner (analog or HD)
525 * [SI4761/63/65/67 Only]
526 *
527 * Function returns 0 on success and negative error code on failure
528 */
529int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *core,
530 enum si476x_dclk_config dclk,
531 enum si476x_dfs_config dfs,
532 enum si476x_dout_config dout,
533 enum si476x_xout_config xout)
534{
535 u8 resp[CMD_DIG_AUDIO_PIN_CFG_NRESP];
536 const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = {
537 PIN_CFG_BYTE(dclk),
538 PIN_CFG_BYTE(dfs),
539 PIN_CFG_BYTE(dout),
540 PIN_CFG_BYTE(xout),
541 };
542
543 return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG,
544 args, ARRAY_SIZE(args),
545 resp, ARRAY_SIZE(resp),
546 SI476X_DEFAULT_TIMEOUT);
547}
548EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
549
550/**
551 * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
552 * @core - device to send the command to
553 * @iqclk - IQCL pin function configuration:
554 * SI476X_IQCLK_NOOP - do not modify the behaviour
555 * SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
556 * enable 1MOhm pulldown
557 * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace
558 * in master mode
559 * @iqfs - IQFS pin function configuration:
560 * SI476X_IQFS_NOOP - do not modify the behaviour
561 * SI476X_IQFS_TRISTATE - put the pin in tristate condition,
562 * enable 1MOhm pulldown
563 * SI476X_IQFS_IQ - set pin to be a part of I/Q interace
564 * in master mode
565 * @iout - IOUT pin function configuration:
566 * SI476X_IOUT_NOOP - do not modify the behaviour
567 * SI476X_IOUT_TRISTATE - put the pin in tristate condition,
568 * enable 1MOhm pulldown
569 * SI476X_IOUT_OUTPUT - set pin to be I out
570 * @qout - QOUT pin function configuration:
571 * SI476X_QOUT_NOOP - do not modify the behaviour
572 * SI476X_QOUT_TRISTATE - put the pin in tristate condition,
573 * enable 1MOhm pulldown
574 * SI476X_QOUT_OUTPUT - set pin to be Q out
575 *
576 * Function returns 0 on success and negative error code on failure
577 */
578int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core,
579 enum si476x_iqclk_config iqclk,
580 enum si476x_iqfs_config iqfs,
581 enum si476x_iout_config iout,
582 enum si476x_qout_config qout)
583{
584 u8 resp[CMD_ZIF_PIN_CFG_NRESP];
585 const u8 args[CMD_ZIF_PIN_CFG_NARGS] = {
586 PIN_CFG_BYTE(iqclk),
587 PIN_CFG_BYTE(iqfs),
588 PIN_CFG_BYTE(iout),
589 PIN_CFG_BYTE(qout),
590 };
591
592 return si476x_core_send_command(core, CMD_ZIF_PIN_CFG,
593 args, ARRAY_SIZE(args),
594 resp, ARRAY_SIZE(resp),
595 SI476X_DEFAULT_TIMEOUT);
596}
597EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
598
599/**
600 * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send
601 * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
602 * @core - device to send the command to
603 * @icin - ICIN pin function configuration:
604 * SI476X_ICIN_NOOP - do not modify the behaviour
605 * SI476X_ICIN_TRISTATE - put the pin in tristate condition,
606 * enable 1MOhm pulldown
607 * SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
608 * SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low
609 * SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link
610 * @icip - ICIP pin function configuration:
611 * SI476X_ICIP_NOOP - do not modify the behaviour
612 * SI476X_ICIP_TRISTATE - put the pin in tristate condition,
613 * enable 1MOhm pulldown
614 * SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
615 * SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low
616 * SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link
617 * @icon - ICON pin function configuration:
618 * SI476X_ICON_NOOP - do not modify the behaviour
619 * SI476X_ICON_TRISTATE - put the pin in tristate condition,
620 * enable 1MOhm pulldown
621 * SI476X_ICON_I2S - set the pin to be a part of audio
622 * interface in slave mode (DCLK)
623 * SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link
624 * @icop - ICOP pin function configuration:
625 * SI476X_ICOP_NOOP - do not modify the behaviour
626 * SI476X_ICOP_TRISTATE - put the pin in tristate condition,
627 * enable 1MOhm pulldown
628 * SI476X_ICOP_I2S - set the pin to be a part of audio
629 * interface in slave mode (DOUT)
630 * [Si4761/63/65/67 Only]
631 * SI476X_ICOP_IC_LINK - set the pin to be a part of Inter-Chip link
632 *
633 * Function returns 0 on success and negative error code on failure
634 */
635int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core,
636 enum si476x_icin_config icin,
637 enum si476x_icip_config icip,
638 enum si476x_icon_config icon,
639 enum si476x_icop_config icop)
640{
641 u8 resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP];
642 const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = {
643 PIN_CFG_BYTE(icin),
644 PIN_CFG_BYTE(icip),
645 PIN_CFG_BYTE(icon),
646 PIN_CFG_BYTE(icop),
647 };
648
649 return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG,
650 args, ARRAY_SIZE(args),
651 resp, ARRAY_SIZE(resp),
652 SI476X_DEFAULT_TIMEOUT);
653}
654EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
655
656/**
657 * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
658 * device
659 * @core - device to send the command to
660 * @lrout - LROUT pin function configuration:
661 * SI476X_LROUT_NOOP - do not modify the behaviour
662 * SI476X_LROUT_TRISTATE - put the pin in tristate condition,
663 * enable 1MOhm pulldown
664 * SI476X_LROUT_AUDIO - set pin to be audio output
665 * SI476X_LROUT_MPX - set pin to be MPX output
666 *
667 * Function returns 0 on success and negative error code on failure
668 */
669int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core,
670 enum si476x_lrout_config lrout)
671{
672 u8 resp[CMD_ANA_AUDIO_PIN_CFG_NRESP];
673 const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = {
674 PIN_CFG_BYTE(lrout),
675 };
676
677 return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG,
678 args, ARRAY_SIZE(args),
679 resp, ARRAY_SIZE(resp),
680 SI476X_DEFAULT_TIMEOUT);
681}
682EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
683
684
685/**
686 * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device
687 * @core - device to send the command to
688 * @intb - INTB pin function configuration:
689 * SI476X_INTB_NOOP - do not modify the behaviour
690 * SI476X_INTB_TRISTATE - put the pin in tristate condition,
691 * enable 1MOhm pulldown
692 * SI476X_INTB_DAUDIO - set pin to be a part of digital
693 * audio interface in slave mode
694 * SI476X_INTB_IRQ - set pin to be an interrupt request line
695 * @a1 - A1 pin function configuration:
696 * SI476X_A1_NOOP - do not modify the behaviour
697 * SI476X_A1_TRISTATE - put the pin in tristate condition,
698 * enable 1MOhm pulldown
699 * SI476X_A1_IRQ - set pin to be an interrupt request line
700 *
701 * Function returns 0 on success and negative error code on failure
702 */
703static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core,
704 enum si476x_intb_config intb,
705 enum si476x_a1_config a1)
706{
707 u8 resp[CMD_INTB_PIN_CFG_A10_NRESP];
708 const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
709 PIN_CFG_BYTE(intb),
710 PIN_CFG_BYTE(a1),
711 };
712
713 return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
714 args, ARRAY_SIZE(args),
715 resp, ARRAY_SIZE(resp),
716 SI476X_DEFAULT_TIMEOUT);
717}
718
719static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
720 enum si476x_intb_config intb,
721 enum si476x_a1_config a1)
722{
723 u8 resp[CMD_INTB_PIN_CFG_A20_NRESP];
724 const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
725 PIN_CFG_BYTE(intb),
726 PIN_CFG_BYTE(a1),
727 };
728
729 return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
730 args, ARRAY_SIZE(args),
731 resp, ARRAY_SIZE(resp),
732 SI476X_DEFAULT_TIMEOUT);
733}
734
735
736
737/**
738 * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
739 * device
740 * @core - device to send the command to
741 * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT,
742 * RSSSILINT, BLENDINT, MULTHINT and MULTLINT
743 * @attune - when set the values in the status report are the values
744 * that were calculated at tune
745 * @cancel - abort ongoing seek/tune opertation
746 * @stcack - clear the STCINT bin in status register
747 * @report - all signal quality information retured by the command
748 * (if NULL then the output of the command is ignored)
749 *
750 * Function returns 0 on success and negative error code on failure
751 */
752int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
753 struct si476x_rsq_status_args *rsqargs,
754 struct si476x_rsq_status_report *report)
755{
756 int err;
757 u8 resp[CMD_AM_RSQ_STATUS_NRESP];
758 const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
759 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
760 rsqargs->cancel << 1 | rsqargs->stcack,
761 };
762
763 err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
764 args, ARRAY_SIZE(args),
765 resp, ARRAY_SIZE(resp),
766 SI476X_DEFAULT_TIMEOUT);
767 /*
768 * Besides getting received signal quality information this
769 * command can be used to just acknowledge different interrupt
770 * flags in those cases it is useless to copy and parse
771 * received data so user can pass NULL, and thus avoid
772 * unnecessary copying.
773 */
774 if (!report)
775 return err;
776
777 report->snrhint = 0x08 & resp[1];
778 report->snrlint = 0x04 & resp[1];
779 report->rssihint = 0x02 & resp[1];
780 report->rssilint = 0x01 & resp[1];
781
782 report->bltf = 0x80 & resp[2];
783 report->snr_ready = 0x20 & resp[2];
784 report->rssiready = 0x08 & resp[2];
785 report->afcrl = 0x02 & resp[2];
786 report->valid = 0x01 & resp[2];
787
788 report->readfreq = get_unaligned_be16(resp + 3);
789 report->freqoff = resp[5];
790 report->rssi = resp[6];
791 report->snr = resp[7];
792 report->lassi = resp[9];
793 report->hassi = resp[10];
794 report->mult = resp[11];
795 report->dev = resp[12];
796
797 return err;
798}
799EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
800
801int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
802 struct si476x_acf_status_report *report)
803{
804 int err;
805 u8 resp[CMD_FM_ACF_STATUS_NRESP];
806 const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
807 0x0,
808 };
809
810 if (!report)
811 return -EINVAL;
812
813 err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
814 args, ARRAY_SIZE(args),
815 resp, ARRAY_SIZE(resp),
816 SI476X_DEFAULT_TIMEOUT);
817 if (err < 0)
818 return err;
819
820 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
821 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
822 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
823 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
824 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
825 report->smute = resp[2] & SI476X_ACF_SMUTE;
826 report->smattn = resp[3] & SI476X_ACF_SMATTN;
827 report->chbw = resp[4];
828 report->hicut = resp[5];
829 report->hiblend = resp[6];
830 report->pilot = resp[7] & SI476X_ACF_PILOT;
831 report->stblend = resp[7] & SI476X_ACF_STBLEND;
832
833 return err;
834}
835EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
836
837int si476x_core_cmd_am_acf_status(struct si476x_core *core,
838 struct si476x_acf_status_report *report)
839{
840 int err;
841 u8 resp[CMD_AM_ACF_STATUS_NRESP];
842 const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
843 0x0,
844 };
845
846 if (!report)
847 return -EINVAL;
848
849 err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
850 args, ARRAY_SIZE(args),
851 resp, ARRAY_SIZE(resp),
852 SI476X_DEFAULT_TIMEOUT);
853 if (err < 0)
854 return err;
855
856 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
857 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
858 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
859 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
860 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
861 report->smute = resp[2] & SI476X_ACF_SMUTE;
862 report->smattn = resp[3] & SI476X_ACF_SMATTN;
863 report->chbw = resp[4];
864 report->hicut = resp[5];
865
866 return err;
867}
868EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
869
870
871/**
872 * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
873 * device
874 * @core - device to send the command to
875 * @seekup - if set the direction of the search is 'up'
876 * @wrap - if set seek wraps when hitting band limit
877 *
878 * This function begins search for a valid station. The station is
879 * considered valid when 'FM_VALID_SNR_THRESHOLD' and
880 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
881 * are met.
882} *
883 * Function returns 0 on success and negative error code on failure
884 */
885int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
886 bool seekup, bool wrap)
887{
888 u8 resp[CMD_FM_SEEK_START_NRESP];
889 const u8 args[CMD_FM_SEEK_START_NARGS] = {
890 seekup << 3 | wrap << 2,
891 };
892
893 return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
894 args, sizeof(args),
895 resp, sizeof(resp));
896}
897EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
898
899/**
900 * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
901 * device
902 * @core - device to send the command to
903 * @status_only - if set the data is not removed from RDSFIFO,
904 * RDSFIFOUSED is not decremented and data in all the
905 * rest RDS data contains the last valid info received
906 * @mtfifo if set the command clears RDS receive FIFO
907 * @intack if set the command clards the RDSINT bit.
908 *
909 * Function returns 0 on success and negative error code on failure
910 */
911int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
912 bool status_only,
913 bool mtfifo,
914 bool intack,
915 struct si476x_rds_status_report *report)
916{
917 int err;
918 u8 resp[CMD_FM_RDS_STATUS_NRESP];
919 const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
920 status_only << 2 | mtfifo << 1 | intack,
921 };
922
923 err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
924 args, ARRAY_SIZE(args),
925 resp, ARRAY_SIZE(resp),
926 SI476X_DEFAULT_TIMEOUT);
927 /*
928 * Besides getting RDS status information this command can be
929 * used to just acknowledge different interrupt flags in those
930 * cases it is useless to copy and parse received data so user
931 * can pass NULL, and thus avoid unnecessary copying.
932 */
933 if (err < 0 || report == NULL)
934 return err;
935
936 report->rdstpptyint = 0x10 & resp[1];
937 report->rdspiint = 0x08 & resp[1];
938 report->rdssyncint = 0x02 & resp[1];
939 report->rdsfifoint = 0x01 & resp[1];
940
941 report->tpptyvalid = 0x10 & resp[2];
942 report->pivalid = 0x08 & resp[2];
943 report->rdssync = 0x02 & resp[2];
944 report->rdsfifolost = 0x01 & resp[2];
945
946 report->tp = 0x20 & resp[3];
947 report->pty = 0x1f & resp[3];
948
949 report->pi = get_unaligned_be16(resp + 4);
950 report->rdsfifoused = resp[6];
951
952 report->ble[V4L2_RDS_BLOCK_A] = 0xc0 & resp[7];
953 report->ble[V4L2_RDS_BLOCK_B] = 0x30 & resp[7];
954 report->ble[V4L2_RDS_BLOCK_C] = 0x0c & resp[7];
955 report->ble[V4L2_RDS_BLOCK_D] = 0x03 & resp[7];
956
957 report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
958 report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
959 report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
960
961 report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
962 report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
963 report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
964
965 report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
966 report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
967 report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
968
969 report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
970 report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
971 report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
972
973 return err;
974}
975EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
976
977int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
978 bool clear,
979 struct si476x_rds_blockcount_report *report)
980{
981 int err;
982 u8 resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
983 const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
984 clear,
985 };
986
987 if (!report)
988 return -EINVAL;
989
990 err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
991 args, ARRAY_SIZE(args),
992 resp, ARRAY_SIZE(resp),
993 SI476X_DEFAULT_TIMEOUT);
994
995 if (!err) {
996 report->expected = get_unaligned_be16(resp + 2);
997 report->received = get_unaligned_be16(resp + 4);
998 report->uncorrectable = get_unaligned_be16(resp + 6);
999 }
1000
1001 return err;
1002}
1003EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
1004
1005int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
1006 enum si476x_phase_diversity_mode mode)
1007{
1008 u8 resp[CMD_FM_PHASE_DIVERSITY_NRESP];
1009 const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
1010 mode & 0x07,
1011 };
1012
1013 return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
1014 args, ARRAY_SIZE(args),
1015 resp, ARRAY_SIZE(resp),
1016 SI476X_DEFAULT_TIMEOUT);
1017}
1018EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
1019/**
1020 * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
1021 * status
1022 *
1023 * @core: si476x device
1024 *
1025 * NOTE caller must hold core lock
1026 *
1027 * Function returns the value of the status bit in case of success and
1028 * negative error code in case of failre.
1029 */
1030int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
1031{
1032 int err;
1033 u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
1034
1035 err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
1036 NULL, 0,
1037 resp, ARRAY_SIZE(resp),
1038 SI476X_DEFAULT_TIMEOUT);
1039
1040 return (err < 0) ? err : resp[1];
1041}
1042EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
1043
1044
1045/**
1046 * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the
1047 * device
1048 * @core - device to send the command to
1049 * @seekup - if set the direction of the search is 'up'
1050 * @wrap - if set seek wraps when hitting band limit
1051 *
1052 * This function begins search for a valid station. The station is
1053 * considered valid when 'FM_VALID_SNR_THRESHOLD' and
1054 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
1055 * are met.
1056 *
1057 * Function returns 0 on success and negative error code on failure
1058 */
1059int si476x_core_cmd_am_seek_start(struct si476x_core *core,
1060 bool seekup, bool wrap)
1061{
1062 u8 resp[CMD_AM_SEEK_START_NRESP];
1063 const u8 args[CMD_AM_SEEK_START_NARGS] = {
1064 seekup << 3 | wrap << 2,
1065 };
1066
1067 return si476x_cmd_tune_seek_freq(core, CMD_AM_SEEK_START,
1068 args, sizeof(args),
1069 resp, sizeof(resp));
1070}
1071EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1072
1073
1074
1075static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
1076 struct si476x_power_up_args *puargs)
1077{
1078 u8 resp[CMD_POWER_UP_A10_NRESP];
1079 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1080 const bool ctsen = (core->client->irq != 0);
1081 const u8 args[CMD_POWER_UP_A10_NARGS] = {
1082 0xF7, /* Reserved, always 0xF7 */
1083 0x3F & puargs->xcload, /* First two bits are reserved to be
1084 * zeros */
1085 ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
1086 * are reserved to
1087 * be written as 0x7 */
1088 puargs->func << 4 | puargs->freq,
1089 0x11, /* Reserved, always 0x11 */
1090 };
1091
1092 return si476x_core_send_command(core, CMD_POWER_UP,
1093 args, ARRAY_SIZE(args),
1094 resp, ARRAY_SIZE(resp),
1095 SI476X_TIMEOUT_POWER_UP);
1096}
1097
1098static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
1099 struct si476x_power_up_args *puargs)
1100{
1101 u8 resp[CMD_POWER_UP_A20_NRESP];
1102 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1103 const bool ctsen = (core->client->irq != 0);
1104 const u8 args[CMD_POWER_UP_A20_NARGS] = {
1105 puargs->ibias6x << 7 | puargs->xstart,
1106 0x3F & puargs->xcload, /* First two bits are reserved to be
1107 * zeros */
1108 ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
1109 puargs->xbiashc << 3 | puargs->xbias,
1110 puargs->func << 4 | puargs->freq,
1111 0x10 | puargs->xmode,
1112 };
1113
1114 return si476x_core_send_command(core, CMD_POWER_UP,
1115 args, ARRAY_SIZE(args),
1116 resp, ARRAY_SIZE(resp),
1117 SI476X_TIMEOUT_POWER_UP);
1118}
1119
1120static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
1121 struct si476x_power_down_args *pdargs)
1122{
1123 u8 resp[CMD_POWER_DOWN_A10_NRESP];
1124
1125 return si476x_core_send_command(core, CMD_POWER_DOWN,
1126 NULL, 0,
1127 resp, ARRAY_SIZE(resp),
1128 SI476X_DEFAULT_TIMEOUT);
1129}
1130
1131static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
1132 struct si476x_power_down_args *pdargs)
1133{
1134 u8 resp[CMD_POWER_DOWN_A20_NRESP];
1135 const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
1136 pdargs->xosc,
1137 };
1138 return si476x_core_send_command(core, CMD_POWER_DOWN,
1139 args, ARRAY_SIZE(args),
1140 resp, ARRAY_SIZE(resp),
1141 SI476X_DEFAULT_TIMEOUT);
1142}
1143
1144static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
1145 struct si476x_tune_freq_args *tuneargs)
1146{
1147
1148 const int am_freq = tuneargs->freq;
1149 u8 resp[CMD_AM_TUNE_FREQ_NRESP];
1150 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1151 (tuneargs->hd << 6),
1152 msb(am_freq),
1153 lsb(am_freq),
1154 };
1155
1156 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
1157 sizeof(args),
1158 resp, sizeof(resp));
1159}
1160
1161static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
1162 struct si476x_tune_freq_args *tuneargs)
1163{
1164 const int am_freq = tuneargs->freq;
1165 u8 resp[CMD_AM_TUNE_FREQ_NRESP];
1166 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1167 (tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
1168 msb(am_freq),
1169 lsb(am_freq),
1170 };
1171
1172 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
1173 args, sizeof(args),
1174 resp, sizeof(resp));
1175}
1176
1177static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
1178 struct si476x_rsq_status_args *rsqargs,
1179 struct si476x_rsq_status_report *report)
1180{
1181 int err;
1182 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1183 const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
1184 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
1185 rsqargs->cancel << 1 | rsqargs->stcack,
1186 };
1187
1188 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1189 args, ARRAY_SIZE(args),
1190 resp, ARRAY_SIZE(resp),
1191 SI476X_DEFAULT_TIMEOUT);
1192 /*
1193 * Besides getting received signal quality information this
1194 * command can be used to just acknowledge different interrupt
1195 * flags in those cases it is useless to copy and parse
1196 * received data so user can pass NULL, and thus avoid
1197 * unnecessary copying.
1198 */
1199 if (err < 0 || report == NULL)
1200 return err;
1201
1202 report->multhint = 0x80 & resp[1];
1203 report->multlint = 0x40 & resp[1];
1204 report->snrhint = 0x08 & resp[1];
1205 report->snrlint = 0x04 & resp[1];
1206 report->rssihint = 0x02 & resp[1];
1207 report->rssilint = 0x01 & resp[1];
1208
1209 report->bltf = 0x80 & resp[2];
1210 report->snr_ready = 0x20 & resp[2];
1211 report->rssiready = 0x08 & resp[2];
1212 report->afcrl = 0x02 & resp[2];
1213 report->valid = 0x01 & resp[2];
1214
1215 report->readfreq = get_unaligned_be16(resp + 3);
1216 report->freqoff = resp[5];
1217 report->rssi = resp[6];
1218 report->snr = resp[7];
1219 report->lassi = resp[9];
1220 report->hassi = resp[10];
1221 report->mult = resp[11];
1222 report->dev = resp[12];
1223 report->readantcap = get_unaligned_be16(resp + 13);
1224 report->assi = resp[15];
1225 report->usn = resp[16];
1226
1227 return err;
1228}
1229
1230static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
1231 struct si476x_rsq_status_args *rsqargs,
1232 struct si476x_rsq_status_report *report)
1233{
1234 int err;
1235 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1236 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1237 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1238 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1239 rsqargs->stcack,
1240 };
1241
1242 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1243 args, ARRAY_SIZE(args),
1244 resp, ARRAY_SIZE(resp),
1245 SI476X_DEFAULT_TIMEOUT);
1246 /*
1247 * Besides getting received signal quality information this
1248 * command can be used to just acknowledge different interrupt
1249 * flags in those cases it is useless to copy and parse
1250 * received data so user can pass NULL, and thus avoid
1251 * unnecessary copying.
1252 */
1253 if (err < 0 || report == NULL)
1254 return err;
1255
1256 report->multhint = 0x80 & resp[1];
1257 report->multlint = 0x40 & resp[1];
1258 report->snrhint = 0x08 & resp[1];
1259 report->snrlint = 0x04 & resp[1];
1260 report->rssihint = 0x02 & resp[1];
1261 report->rssilint = 0x01 & resp[1];
1262
1263 report->bltf = 0x80 & resp[2];
1264 report->snr_ready = 0x20 & resp[2];
1265 report->rssiready = 0x08 & resp[2];
1266 report->afcrl = 0x02 & resp[2];
1267 report->valid = 0x01 & resp[2];
1268
1269 report->readfreq = get_unaligned_be16(resp + 3);
1270 report->freqoff = resp[5];
1271 report->rssi = resp[6];
1272 report->snr = resp[7];
1273 report->lassi = resp[9];
1274 report->hassi = resp[10];
1275 report->mult = resp[11];
1276 report->dev = resp[12];
1277 report->readantcap = get_unaligned_be16(resp + 13);
1278 report->assi = resp[15];
1279 report->usn = resp[16];
1280
1281 return err;
1282}
1283
1284
1285static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
1286 struct si476x_rsq_status_args *rsqargs,
1287 struct si476x_rsq_status_report *report)
1288{
1289 int err;
1290 u8 resp[CMD_FM_RSQ_STATUS_A30_NRESP];
1291 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1292 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1293 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1294 rsqargs->stcack,
1295 };
1296
1297 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1298 args, ARRAY_SIZE(args),
1299 resp, ARRAY_SIZE(resp),
1300 SI476X_DEFAULT_TIMEOUT);
1301 /*
1302 * Besides getting received signal quality information this
1303 * command can be used to just acknowledge different interrupt
1304 * flags in those cases it is useless to copy and parse
1305 * received data so user can pass NULL, and thus avoid
1306 * unnecessary copying.
1307 */
1308 if (err < 0 || report == NULL)
1309 return err;
1310
1311 report->multhint = 0x80 & resp[1];
1312 report->multlint = 0x40 & resp[1];
1313 report->snrhint = 0x08 & resp[1];
1314 report->snrlint = 0x04 & resp[1];
1315 report->rssihint = 0x02 & resp[1];
1316 report->rssilint = 0x01 & resp[1];
1317
1318 report->bltf = 0x80 & resp[2];
1319 report->snr_ready = 0x20 & resp[2];
1320 report->rssiready = 0x08 & resp[2];
1321 report->injside = 0x04 & resp[2];
1322 report->afcrl = 0x02 & resp[2];
1323 report->valid = 0x01 & resp[2];
1324
1325 report->readfreq = get_unaligned_be16(resp + 3);
1326 report->freqoff = resp[5];
1327 report->rssi = resp[6];
1328 report->snr = resp[7];
1329 report->issi = resp[8];
1330 report->lassi = resp[9];
1331 report->hassi = resp[10];
1332 report->mult = resp[11];
1333 report->dev = resp[12];
1334 report->readantcap = get_unaligned_be16(resp + 13);
1335 report->assi = resp[15];
1336 report->usn = resp[16];
1337
1338 report->pilotdev = resp[17];
1339 report->rdsdev = resp[18];
1340 report->assidev = resp[19];
1341 report->strongdev = resp[20];
1342 report->rdspi = get_unaligned_be16(resp + 21);
1343
1344 return err;
1345}
1346
1347static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
1348 struct si476x_tune_freq_args *tuneargs)
1349{
1350 u8 resp[CMD_FM_TUNE_FREQ_NRESP];
1351 const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
1352 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1353 | (tuneargs->smoothmetrics << 2),
1354 msb(tuneargs->freq),
1355 lsb(tuneargs->freq),
1356 msb(tuneargs->antcap),
1357 lsb(tuneargs->antcap)
1358 };
1359
1360 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1361 args, sizeof(args),
1362 resp, sizeof(resp));
1363}
1364
1365static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
1366 struct si476x_tune_freq_args *tuneargs)
1367{
1368 u8 resp[CMD_FM_TUNE_FREQ_NRESP];
1369 const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
1370 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1371 | (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
1372 msb(tuneargs->freq),
1373 lsb(tuneargs->freq),
1374 };
1375
1376 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1377 args, sizeof(args),
1378 resp, sizeof(resp));
1379}
1380
1381static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
1382 struct si476x_agc_status_report *report)
1383{
1384 int err;
1385 u8 resp[CMD_AGC_STATUS_NRESP_A20];
1386
1387 if (!report)
1388 return -EINVAL;
1389
1390 err = si476x_core_send_command(core, CMD_AGC_STATUS,
1391 NULL, 0,
1392 resp, ARRAY_SIZE(resp),
1393 SI476X_DEFAULT_TIMEOUT);
1394 if (err < 0)
1395 return err;
1396
1397 report->mxhi = resp[1] & SI476X_AGC_MXHI;
1398 report->mxlo = resp[1] & SI476X_AGC_MXLO;
1399 report->lnahi = resp[1] & SI476X_AGC_LNAHI;
1400 report->lnalo = resp[1] & SI476X_AGC_LNALO;
1401 report->fmagc1 = resp[2];
1402 report->fmagc2 = resp[3];
1403 report->pgagain = resp[4];
1404 report->fmwblang = resp[5];
1405
1406 return err;
1407}
1408
1409static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
1410 struct si476x_agc_status_report *report)
1411{
1412 int err;
1413 u8 resp[CMD_AGC_STATUS_NRESP_A10];
1414
1415 if (!report)
1416 return -EINVAL;
1417
1418 err = si476x_core_send_command(core, CMD_AGC_STATUS,
1419 NULL, 0,
1420 resp, ARRAY_SIZE(resp),
1421 SI476X_DEFAULT_TIMEOUT);
1422 if (err < 0)
1423 return err;
1424
1425 report->mxhi = resp[1] & SI476X_AGC_MXHI;
1426 report->mxlo = resp[1] & SI476X_AGC_MXLO;
1427 report->lnahi = resp[1] & SI476X_AGC_LNAHI;
1428 report->lnalo = resp[1] & SI476X_AGC_LNALO;
1429
1430 return err;
1431}
1432
1433typedef int (*tune_freq_func_t) (struct si476x_core *core,
1434 struct si476x_tune_freq_args *tuneargs);
1435
1436static struct {
1437 int (*power_up) (struct si476x_core *,
1438 struct si476x_power_up_args *);
1439 int (*power_down) (struct si476x_core *,
1440 struct si476x_power_down_args *);
1441
1442 tune_freq_func_t fm_tune_freq;
1443 tune_freq_func_t am_tune_freq;
1444
1445 int (*fm_rsq_status)(struct si476x_core *,
1446 struct si476x_rsq_status_args *,
1447 struct si476x_rsq_status_report *);
1448
1449 int (*agc_status)(struct si476x_core *,
1450 struct si476x_agc_status_report *);
1451 int (*intb_pin_cfg)(struct si476x_core *core,
1452 enum si476x_intb_config intb,
1453 enum si476x_a1_config a1);
1454} si476x_cmds_vtable[] = {
1455 [SI476X_REVISION_A10] = {
1456 .power_up = si476x_core_cmd_power_up_a10,
1457 .power_down = si476x_core_cmd_power_down_a10,
1458 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a10,
1459 .am_tune_freq = si476x_core_cmd_am_tune_freq_a10,
1460 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a10,
1461 .agc_status = si476x_core_cmd_agc_status_a10,
1462 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a10,
1463 },
1464 [SI476X_REVISION_A20] = {
1465 .power_up = si476x_core_cmd_power_up_a20,
1466 .power_down = si476x_core_cmd_power_down_a20,
1467 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
1468 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
1469 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a20,
1470 .agc_status = si476x_core_cmd_agc_status_a20,
1471 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
1472 },
1473 [SI476X_REVISION_A30] = {
1474 .power_up = si476x_core_cmd_power_up_a20,
1475 .power_down = si476x_core_cmd_power_down_a20,
1476 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
1477 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
1478 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a30,
1479 .agc_status = si476x_core_cmd_agc_status_a20,
1480 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
1481 },
1482};
1483
1484int si476x_core_cmd_power_up(struct si476x_core *core,
1485 struct si476x_power_up_args *args)
1486{
1487 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1488 core->revision == -1);
1489 return si476x_cmds_vtable[core->revision].power_up(core, args);
1490}
1491EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1492
1493int si476x_core_cmd_power_down(struct si476x_core *core,
1494 struct si476x_power_down_args *args)
1495{
1496 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1497 core->revision == -1);
1498 return si476x_cmds_vtable[core->revision].power_down(core, args);
1499}
1500EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1501
1502int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
1503 struct si476x_tune_freq_args *args)
1504{
1505 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1506 core->revision == -1);
1507 return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
1508}
1509EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1510
1511int si476x_core_cmd_am_tune_freq(struct si476x_core *core,
1512 struct si476x_tune_freq_args *args)
1513{
1514 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1515 core->revision == -1);
1516 return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
1517}
1518EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1519
1520int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
1521 struct si476x_rsq_status_args *args,
1522 struct si476x_rsq_status_report *report)
1523
1524{
1525 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1526 core->revision == -1);
1527 return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
1528 report);
1529}
1530EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1531
1532int si476x_core_cmd_agc_status(struct si476x_core *core,
1533 struct si476x_agc_status_report *report)
1534
1535{
1536 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1537 core->revision == -1);
1538 return si476x_cmds_vtable[core->revision].agc_status(core, report);
1539}
1540EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1541
1542int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
1543 enum si476x_intb_config intb,
1544 enum si476x_a1_config a1)
1545{
1546 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1547 core->revision == -1);
1548
1549 return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
1550}
1551EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1552
1553MODULE_LICENSE("GPL");
1554MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1555MODULE_DESCRIPTION("API for command exchange for si476x");