Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-2.6-microblaze.git] / drivers / mfd / si476x-cmd.c
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
138 enum 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
151 enum 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
158 enum 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
171 static 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  * @response: buffer to place the expected response from the device
250  * @respn:    actual size of @response
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  */
257 static 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;
343 exit:
344         return err;
345 }
346
347 static 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
372 static 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  */
404 int 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 }
424 EXPORT_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  */
435 int 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 }
452 EXPORT_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  */
462 int 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 }
481 EXPORT_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  */
520 int 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 }
539 EXPORT_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  */
569 int 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 }
588 EXPORT_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  */
626 int 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 }
645 EXPORT_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  */
660 int 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 }
673 EXPORT_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  */
694 static 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
710 static 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  * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT,
733  *           RSSSILINT, BLENDINT, MULTHINT and MULTLINT
734  * @attune - when set the values in the status report are the values
735  *           that were calculated at tune
736  * @cancel - abort ongoing seek/tune opertation
737  * @stcack - clear the STCINT bin in status register
738  * @report - all signal quality information retured by the command
739  *           (if NULL then the output of the command is ignored)
740  *
741  * Function returns 0 on success and negative error code on failure
742  */
743 int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
744                                   struct si476x_rsq_status_args *rsqargs,
745                                   struct si476x_rsq_status_report *report)
746 {
747         int err;
748         u8       resp[CMD_AM_RSQ_STATUS_NRESP];
749         const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
750                 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
751                 rsqargs->cancel << 1 | rsqargs->stcack,
752         };
753
754         err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
755                                        args, ARRAY_SIZE(args),
756                                        resp, ARRAY_SIZE(resp),
757                                        SI476X_DEFAULT_TIMEOUT);
758         /*
759          * Besides getting received signal quality information this
760          * command can be used to just acknowledge different interrupt
761          * flags in those cases it is useless to copy and parse
762          * received data so user can pass NULL, and thus avoid
763          * unnecessary copying.
764          */
765         if (!report)
766                 return err;
767
768         report->snrhint         = 0x08 & resp[1];
769         report->snrlint         = 0x04 & resp[1];
770         report->rssihint        = 0x02 & resp[1];
771         report->rssilint        = 0x01 & resp[1];
772
773         report->bltf            = 0x80 & resp[2];
774         report->snr_ready       = 0x20 & resp[2];
775         report->rssiready       = 0x08 & resp[2];
776         report->afcrl           = 0x02 & resp[2];
777         report->valid           = 0x01 & resp[2];
778
779         report->readfreq        = get_unaligned_be16(resp + 3);
780         report->freqoff         = resp[5];
781         report->rssi            = resp[6];
782         report->snr             = resp[7];
783         report->lassi           = resp[9];
784         report->hassi           = resp[10];
785         report->mult            = resp[11];
786         report->dev             = resp[12];
787
788         return err;
789 }
790 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
791
792 int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
793                              struct si476x_acf_status_report *report)
794 {
795         int err;
796         u8       resp[CMD_FM_ACF_STATUS_NRESP];
797         const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
798                 0x0,
799         };
800
801         if (!report)
802                 return -EINVAL;
803
804         err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
805                                        args, ARRAY_SIZE(args),
806                                        resp, ARRAY_SIZE(resp),
807                                        SI476X_DEFAULT_TIMEOUT);
808         if (err < 0)
809                 return err;
810
811         report->blend_int       = resp[1] & SI476X_ACF_BLEND_INT;
812         report->hblend_int      = resp[1] & SI476X_ACF_HIBLEND_INT;
813         report->hicut_int       = resp[1] & SI476X_ACF_HICUT_INT;
814         report->chbw_int        = resp[1] & SI476X_ACF_CHBW_INT;
815         report->softmute_int    = resp[1] & SI476X_ACF_SOFTMUTE_INT;
816         report->smute           = resp[2] & SI476X_ACF_SMUTE;
817         report->smattn          = resp[3] & SI476X_ACF_SMATTN;
818         report->chbw            = resp[4];
819         report->hicut           = resp[5];
820         report->hiblend         = resp[6];
821         report->pilot           = resp[7] & SI476X_ACF_PILOT;
822         report->stblend         = resp[7] & SI476X_ACF_STBLEND;
823
824         return err;
825 }
826 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
827
828 int si476x_core_cmd_am_acf_status(struct si476x_core *core,
829                                   struct si476x_acf_status_report *report)
830 {
831         int err;
832         u8       resp[CMD_AM_ACF_STATUS_NRESP];
833         const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
834                 0x0,
835         };
836
837         if (!report)
838                 return -EINVAL;
839
840         err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
841                                        args, ARRAY_SIZE(args),
842                                        resp, ARRAY_SIZE(resp),
843                                        SI476X_DEFAULT_TIMEOUT);
844         if (err < 0)
845                 return err;
846
847         report->blend_int       = resp[1] & SI476X_ACF_BLEND_INT;
848         report->hblend_int      = resp[1] & SI476X_ACF_HIBLEND_INT;
849         report->hicut_int       = resp[1] & SI476X_ACF_HICUT_INT;
850         report->chbw_int        = resp[1] & SI476X_ACF_CHBW_INT;
851         report->softmute_int    = resp[1] & SI476X_ACF_SOFTMUTE_INT;
852         report->smute           = resp[2] & SI476X_ACF_SMUTE;
853         report->smattn          = resp[3] & SI476X_ACF_SMATTN;
854         report->chbw            = resp[4];
855         report->hicut           = resp[5];
856
857         return err;
858 }
859 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
860
861
862 /**
863  * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
864  * device
865  * @core  - device to send the command to
866  * @seekup - if set the direction of the search is 'up'
867  * @wrap   - if set seek wraps when hitting band limit
868  *
869  * This function begins search for a valid station. The station is
870  * considered valid when 'FM_VALID_SNR_THRESHOLD' and
871  * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
872  * are met.
873 } *
874  * Function returns 0 on success and negative error code on failure
875  */
876 int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
877                                   bool seekup, bool wrap)
878 {
879         u8       resp[CMD_FM_SEEK_START_NRESP];
880         const u8 args[CMD_FM_SEEK_START_NARGS] = {
881                 seekup << 3 | wrap << 2,
882         };
883
884         return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
885                                          args, sizeof(args),
886                                          resp, sizeof(resp));
887 }
888 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
889
890 /**
891  * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
892  * device
893  * @core - device to send the command to
894  * @status_only - if set the data is not removed from RDSFIFO,
895  *                RDSFIFOUSED is not decremented and data in all the
896  *                rest RDS data contains the last valid info received
897  * @mtfifo if set the command clears RDS receive FIFO
898  * @intack if set the command clards the RDSINT bit.
899  *
900  * Function returns 0 on success and negative error code on failure
901  */
902 int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
903                                   bool status_only,
904                                   bool mtfifo,
905                                   bool intack,
906                                   struct si476x_rds_status_report *report)
907 {
908         int err;
909         u8       resp[CMD_FM_RDS_STATUS_NRESP];
910         const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
911                 status_only << 2 | mtfifo << 1 | intack,
912         };
913
914         err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
915                                        args, ARRAY_SIZE(args),
916                                        resp, ARRAY_SIZE(resp),
917                                        SI476X_DEFAULT_TIMEOUT);
918         /*
919          * Besides getting RDS status information this command can be
920          * used to just acknowledge different interrupt flags in those
921          * cases it is useless to copy and parse received data so user
922          * can pass NULL, and thus avoid unnecessary copying.
923          */
924         if (err < 0 || report == NULL)
925                 return err;
926
927         report->rdstpptyint     = 0x10 & resp[1];
928         report->rdspiint        = 0x08 & resp[1];
929         report->rdssyncint      = 0x02 & resp[1];
930         report->rdsfifoint      = 0x01 & resp[1];
931
932         report->tpptyvalid      = 0x10 & resp[2];
933         report->pivalid         = 0x08 & resp[2];
934         report->rdssync         = 0x02 & resp[2];
935         report->rdsfifolost     = 0x01 & resp[2];
936
937         report->tp              = 0x20 & resp[3];
938         report->pty             = 0x1f & resp[3];
939
940         report->pi              = get_unaligned_be16(resp + 4);
941         report->rdsfifoused     = resp[6];
942
943         report->ble[V4L2_RDS_BLOCK_A]   = 0xc0 & resp[7];
944         report->ble[V4L2_RDS_BLOCK_B]   = 0x30 & resp[7];
945         report->ble[V4L2_RDS_BLOCK_C]   = 0x0c & resp[7];
946         report->ble[V4L2_RDS_BLOCK_D]   = 0x03 & resp[7];
947
948         report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
949         report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
950         report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
951
952         report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
953         report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
954         report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
955
956         report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
957         report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
958         report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
959
960         report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
961         report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
962         report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
963
964         return err;
965 }
966 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
967
968 int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
969                                 bool clear,
970                                 struct si476x_rds_blockcount_report *report)
971 {
972         int err;
973         u8       resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
974         const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
975                 clear,
976         };
977
978         if (!report)
979                 return -EINVAL;
980
981         err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
982                                        args, ARRAY_SIZE(args),
983                                        resp, ARRAY_SIZE(resp),
984                                        SI476X_DEFAULT_TIMEOUT);
985
986         if (!err) {
987                 report->expected        = get_unaligned_be16(resp + 2);
988                 report->received        = get_unaligned_be16(resp + 4);
989                 report->uncorrectable   = get_unaligned_be16(resp + 6);
990         }
991
992         return err;
993 }
994 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
995
996 int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
997                                        enum si476x_phase_diversity_mode mode)
998 {
999         u8       resp[CMD_FM_PHASE_DIVERSITY_NRESP];
1000         const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
1001                 mode & 0x07,
1002         };
1003
1004         return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
1005                                         args, ARRAY_SIZE(args),
1006                                         resp, ARRAY_SIZE(resp),
1007                                         SI476X_DEFAULT_TIMEOUT);
1008 }
1009 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
1010 /**
1011  * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
1012  * status
1013  *
1014  * @core: si476x device
1015  *
1016  * NOTE caller must hold core lock
1017  *
1018  * Function returns the value of the status bit in case of success and
1019  * negative error code in case of failre.
1020  */
1021 int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
1022 {
1023         int err;
1024         u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
1025
1026         err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
1027                                        NULL, 0,
1028                                        resp, ARRAY_SIZE(resp),
1029                                        SI476X_DEFAULT_TIMEOUT);
1030
1031         return (err < 0) ? err : resp[1];
1032 }
1033 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
1034
1035
1036 /**
1037  * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the
1038  * device
1039  * @core  - device to send the command to
1040  * @seekup - if set the direction of the search is 'up'
1041  * @wrap   - if set seek wraps when hitting band limit
1042  *
1043  * This function begins search for a valid station. The station is
1044  * considered valid when 'FM_VALID_SNR_THRESHOLD' and
1045  * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
1046  * are met.
1047  *
1048  * Function returns 0 on success and negative error code on failure
1049  */
1050 int si476x_core_cmd_am_seek_start(struct si476x_core *core,
1051                                   bool seekup, bool wrap)
1052 {
1053         u8       resp[CMD_AM_SEEK_START_NRESP];
1054         const u8 args[CMD_AM_SEEK_START_NARGS] = {
1055                 seekup << 3 | wrap << 2,
1056         };
1057
1058         return si476x_cmd_tune_seek_freq(core,  CMD_AM_SEEK_START,
1059                                          args, sizeof(args),
1060                                          resp, sizeof(resp));
1061 }
1062 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1063
1064
1065
1066 static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
1067                                         struct si476x_power_up_args *puargs)
1068 {
1069         u8       resp[CMD_POWER_UP_A10_NRESP];
1070         const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1071         const bool ctsen  = (core->client->irq != 0);
1072         const u8 args[CMD_POWER_UP_A10_NARGS] = {
1073                 0xF7,           /* Reserved, always 0xF7 */
1074                 0x3F & puargs->xcload,  /* First two bits are reserved to be
1075                                  * zeros */
1076                 ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
1077                                                    * are reserved to
1078                                                    * be written as 0x7 */
1079                 puargs->func << 4 | puargs->freq,
1080                 0x11,           /* Reserved, always 0x11 */
1081         };
1082
1083         return si476x_core_send_command(core, CMD_POWER_UP,
1084                                         args, ARRAY_SIZE(args),
1085                                         resp, ARRAY_SIZE(resp),
1086                                         SI476X_TIMEOUT_POWER_UP);
1087 }
1088
1089 static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
1090                                  struct si476x_power_up_args *puargs)
1091 {
1092         u8       resp[CMD_POWER_UP_A20_NRESP];
1093         const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1094         const bool ctsen  = (core->client->irq != 0);
1095         const u8 args[CMD_POWER_UP_A20_NARGS] = {
1096                 puargs->ibias6x << 7 | puargs->xstart,
1097                 0x3F & puargs->xcload,  /* First two bits are reserved to be
1098                                          * zeros */
1099                 ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
1100                 puargs->xbiashc << 3 | puargs->xbias,
1101                 puargs->func << 4 | puargs->freq,
1102                 0x10 | puargs->xmode,
1103         };
1104
1105         return si476x_core_send_command(core, CMD_POWER_UP,
1106                                         args, ARRAY_SIZE(args),
1107                                         resp, ARRAY_SIZE(resp),
1108                                         SI476X_TIMEOUT_POWER_UP);
1109 }
1110
1111 static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
1112                                           struct si476x_power_down_args *pdargs)
1113 {
1114         u8 resp[CMD_POWER_DOWN_A10_NRESP];
1115
1116         return si476x_core_send_command(core, CMD_POWER_DOWN,
1117                                         NULL, 0,
1118                                         resp, ARRAY_SIZE(resp),
1119                                         SI476X_DEFAULT_TIMEOUT);
1120 }
1121
1122 static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
1123                                           struct si476x_power_down_args *pdargs)
1124 {
1125         u8 resp[CMD_POWER_DOWN_A20_NRESP];
1126         const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
1127                 pdargs->xosc,
1128         };
1129         return si476x_core_send_command(core, CMD_POWER_DOWN,
1130                                         args, ARRAY_SIZE(args),
1131                                         resp, ARRAY_SIZE(resp),
1132                                         SI476X_DEFAULT_TIMEOUT);
1133 }
1134
1135 static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
1136                                         struct si476x_tune_freq_args *tuneargs)
1137 {
1138
1139         const int am_freq = tuneargs->freq;
1140         u8       resp[CMD_AM_TUNE_FREQ_NRESP];
1141         const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1142                 (tuneargs->hd << 6),
1143                 msb(am_freq),
1144                 lsb(am_freq),
1145         };
1146
1147         return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
1148                                          sizeof(args),
1149                                          resp, sizeof(resp));
1150 }
1151
1152 static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
1153                                         struct si476x_tune_freq_args *tuneargs)
1154 {
1155         const int am_freq = tuneargs->freq;
1156         u8       resp[CMD_AM_TUNE_FREQ_NRESP];
1157         const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1158                 (tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
1159                 msb(am_freq),
1160                 lsb(am_freq),
1161         };
1162
1163         return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
1164                                          args, sizeof(args),
1165                                          resp, sizeof(resp));
1166 }
1167
1168 static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
1169                                         struct si476x_rsq_status_args *rsqargs,
1170                                         struct si476x_rsq_status_report *report)
1171 {
1172         int err;
1173         u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1174         const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
1175                 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
1176                 rsqargs->cancel << 1 | rsqargs->stcack,
1177         };
1178
1179         err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1180                                        args, ARRAY_SIZE(args),
1181                                        resp, ARRAY_SIZE(resp),
1182                                        SI476X_DEFAULT_TIMEOUT);
1183         /*
1184          * Besides getting received signal quality information this
1185          * command can be used to just acknowledge different interrupt
1186          * flags in those cases it is useless to copy and parse
1187          * received data so user can pass NULL, and thus avoid
1188          * unnecessary copying.
1189          */
1190         if (err < 0 || report == NULL)
1191                 return err;
1192
1193         report->multhint        = 0x80 & resp[1];
1194         report->multlint        = 0x40 & resp[1];
1195         report->snrhint         = 0x08 & resp[1];
1196         report->snrlint         = 0x04 & resp[1];
1197         report->rssihint        = 0x02 & resp[1];
1198         report->rssilint        = 0x01 & resp[1];
1199
1200         report->bltf            = 0x80 & resp[2];
1201         report->snr_ready       = 0x20 & resp[2];
1202         report->rssiready       = 0x08 & resp[2];
1203         report->afcrl           = 0x02 & resp[2];
1204         report->valid           = 0x01 & resp[2];
1205
1206         report->readfreq        = get_unaligned_be16(resp + 3);
1207         report->freqoff         = resp[5];
1208         report->rssi            = resp[6];
1209         report->snr             = resp[7];
1210         report->lassi           = resp[9];
1211         report->hassi           = resp[10];
1212         report->mult            = resp[11];
1213         report->dev             = resp[12];
1214         report->readantcap      = get_unaligned_be16(resp + 13);
1215         report->assi            = resp[15];
1216         report->usn             = resp[16];
1217
1218         return err;
1219 }
1220
1221 static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
1222                                      struct si476x_rsq_status_args *rsqargs,
1223                                      struct si476x_rsq_status_report *report)
1224 {
1225         int err;
1226         u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1227         const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1228                 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1229                 rsqargs->attune  << 2 | rsqargs->cancel << 1 |
1230                 rsqargs->stcack,
1231         };
1232
1233         err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1234                                        args, ARRAY_SIZE(args),
1235                                        resp, ARRAY_SIZE(resp),
1236                                        SI476X_DEFAULT_TIMEOUT);
1237         /*
1238          * Besides getting received signal quality information this
1239          * command can be used to just acknowledge different interrupt
1240          * flags in those cases it is useless to copy and parse
1241          * received data so user can pass NULL, and thus avoid
1242          * unnecessary copying.
1243          */
1244         if (err < 0 || report == NULL)
1245                 return err;
1246
1247         report->multhint        = 0x80 & resp[1];
1248         report->multlint        = 0x40 & resp[1];
1249         report->snrhint         = 0x08 & resp[1];
1250         report->snrlint         = 0x04 & resp[1];
1251         report->rssihint        = 0x02 & resp[1];
1252         report->rssilint        = 0x01 & resp[1];
1253
1254         report->bltf            = 0x80 & resp[2];
1255         report->snr_ready       = 0x20 & resp[2];
1256         report->rssiready       = 0x08 & resp[2];
1257         report->afcrl           = 0x02 & resp[2];
1258         report->valid           = 0x01 & resp[2];
1259
1260         report->readfreq        = get_unaligned_be16(resp + 3);
1261         report->freqoff         = resp[5];
1262         report->rssi            = resp[6];
1263         report->snr             = resp[7];
1264         report->lassi           = resp[9];
1265         report->hassi           = resp[10];
1266         report->mult            = resp[11];
1267         report->dev             = resp[12];
1268         report->readantcap      = get_unaligned_be16(resp + 13);
1269         report->assi            = resp[15];
1270         report->usn             = resp[16];
1271
1272         return err;
1273 }
1274
1275
1276 static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
1277                                         struct si476x_rsq_status_args *rsqargs,
1278                                         struct si476x_rsq_status_report *report)
1279 {
1280         int err;
1281         u8       resp[CMD_FM_RSQ_STATUS_A30_NRESP];
1282         const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1283                 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1284                 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1285                 rsqargs->stcack,
1286         };
1287
1288         err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1289                                        args, ARRAY_SIZE(args),
1290                                        resp, ARRAY_SIZE(resp),
1291                                        SI476X_DEFAULT_TIMEOUT);
1292         /*
1293          * Besides getting received signal quality information this
1294          * command can be used to just acknowledge different interrupt
1295          * flags in those cases it is useless to copy and parse
1296          * received data so user can pass NULL, and thus avoid
1297          * unnecessary copying.
1298          */
1299         if (err < 0 || report == NULL)
1300                 return err;
1301
1302         report->multhint        = 0x80 & resp[1];
1303         report->multlint        = 0x40 & resp[1];
1304         report->snrhint         = 0x08 & resp[1];
1305         report->snrlint         = 0x04 & resp[1];
1306         report->rssihint        = 0x02 & resp[1];
1307         report->rssilint        = 0x01 & resp[1];
1308
1309         report->bltf            = 0x80 & resp[2];
1310         report->snr_ready       = 0x20 & resp[2];
1311         report->rssiready       = 0x08 & resp[2];
1312         report->injside         = 0x04 & resp[2];
1313         report->afcrl           = 0x02 & resp[2];
1314         report->valid           = 0x01 & resp[2];
1315
1316         report->readfreq        = get_unaligned_be16(resp + 3);
1317         report->freqoff         = resp[5];
1318         report->rssi            = resp[6];
1319         report->snr             = resp[7];
1320         report->issi            = resp[8];
1321         report->lassi           = resp[9];
1322         report->hassi           = resp[10];
1323         report->mult            = resp[11];
1324         report->dev             = resp[12];
1325         report->readantcap      = get_unaligned_be16(resp + 13);
1326         report->assi            = resp[15];
1327         report->usn             = resp[16];
1328
1329         report->pilotdev        = resp[17];
1330         report->rdsdev          = resp[18];
1331         report->assidev         = resp[19];
1332         report->strongdev       = resp[20];
1333         report->rdspi           = get_unaligned_be16(resp + 21);
1334
1335         return err;
1336 }
1337
1338 static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
1339                                         struct si476x_tune_freq_args *tuneargs)
1340 {
1341         u8       resp[CMD_FM_TUNE_FREQ_NRESP];
1342         const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
1343                 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1344                 | (tuneargs->smoothmetrics << 2),
1345                 msb(tuneargs->freq),
1346                 lsb(tuneargs->freq),
1347                 msb(tuneargs->antcap),
1348                 lsb(tuneargs->antcap)
1349         };
1350
1351         return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1352                                          args, sizeof(args),
1353                                          resp, sizeof(resp));
1354 }
1355
1356 static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
1357                                         struct si476x_tune_freq_args *tuneargs)
1358 {
1359         u8       resp[CMD_FM_TUNE_FREQ_NRESP];
1360         const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
1361                 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1362                 |  (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
1363                 msb(tuneargs->freq),
1364                 lsb(tuneargs->freq),
1365         };
1366
1367         return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1368                                          args, sizeof(args),
1369                                          resp, sizeof(resp));
1370 }
1371
1372 static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
1373                                         struct si476x_agc_status_report *report)
1374 {
1375         int err;
1376         u8 resp[CMD_AGC_STATUS_NRESP_A20];
1377
1378         if (!report)
1379                 return -EINVAL;
1380
1381         err = si476x_core_send_command(core, CMD_AGC_STATUS,
1382                                        NULL, 0,
1383                                        resp, ARRAY_SIZE(resp),
1384                                        SI476X_DEFAULT_TIMEOUT);
1385         if (err < 0)
1386                 return err;
1387
1388         report->mxhi            = resp[1] & SI476X_AGC_MXHI;
1389         report->mxlo            = resp[1] & SI476X_AGC_MXLO;
1390         report->lnahi           = resp[1] & SI476X_AGC_LNAHI;
1391         report->lnalo           = resp[1] & SI476X_AGC_LNALO;
1392         report->fmagc1          = resp[2];
1393         report->fmagc2          = resp[3];
1394         report->pgagain         = resp[4];
1395         report->fmwblang        = resp[5];
1396
1397         return err;
1398 }
1399
1400 static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
1401                                         struct si476x_agc_status_report *report)
1402 {
1403         int err;
1404         u8 resp[CMD_AGC_STATUS_NRESP_A10];
1405
1406         if (!report)
1407                 return -EINVAL;
1408
1409         err = si476x_core_send_command(core, CMD_AGC_STATUS,
1410                                        NULL, 0,
1411                                        resp, ARRAY_SIZE(resp),
1412                                        SI476X_DEFAULT_TIMEOUT);
1413         if (err < 0)
1414                 return err;
1415
1416         report->mxhi    = resp[1] & SI476X_AGC_MXHI;
1417         report->mxlo    = resp[1] & SI476X_AGC_MXLO;
1418         report->lnahi   = resp[1] & SI476X_AGC_LNAHI;
1419         report->lnalo   = resp[1] & SI476X_AGC_LNALO;
1420
1421         return err;
1422 }
1423
1424 typedef int (*tune_freq_func_t) (struct si476x_core *core,
1425                                  struct si476x_tune_freq_args *tuneargs);
1426
1427 static struct {
1428         int (*power_up)(struct si476x_core *,
1429                         struct si476x_power_up_args *);
1430         int (*power_down)(struct si476x_core *,
1431                           struct si476x_power_down_args *);
1432
1433         tune_freq_func_t fm_tune_freq;
1434         tune_freq_func_t am_tune_freq;
1435
1436         int (*fm_rsq_status)(struct si476x_core *,
1437                              struct si476x_rsq_status_args *,
1438                              struct si476x_rsq_status_report *);
1439
1440         int (*agc_status)(struct si476x_core *,
1441                           struct si476x_agc_status_report *);
1442         int (*intb_pin_cfg)(struct si476x_core *core,
1443                             enum si476x_intb_config intb,
1444                             enum si476x_a1_config a1);
1445 } si476x_cmds_vtable[] = {
1446         [SI476X_REVISION_A10] = {
1447                 .power_up       = si476x_core_cmd_power_up_a10,
1448                 .power_down     = si476x_core_cmd_power_down_a10,
1449                 .fm_tune_freq   = si476x_core_cmd_fm_tune_freq_a10,
1450                 .am_tune_freq   = si476x_core_cmd_am_tune_freq_a10,
1451                 .fm_rsq_status  = si476x_core_cmd_fm_rsq_status_a10,
1452                 .agc_status     = si476x_core_cmd_agc_status_a10,
1453                 .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a10,
1454         },
1455         [SI476X_REVISION_A20] = {
1456                 .power_up       = si476x_core_cmd_power_up_a20,
1457                 .power_down     = si476x_core_cmd_power_down_a20,
1458                 .fm_tune_freq   = si476x_core_cmd_fm_tune_freq_a20,
1459                 .am_tune_freq   = si476x_core_cmd_am_tune_freq_a20,
1460                 .fm_rsq_status  = si476x_core_cmd_fm_rsq_status_a20,
1461                 .agc_status     = si476x_core_cmd_agc_status_a20,
1462                 .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
1463         },
1464         [SI476X_REVISION_A30] = {
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_a30,
1470                 .agc_status     = si476x_core_cmd_agc_status_a20,
1471                 .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
1472         },
1473 };
1474
1475 int si476x_core_cmd_power_up(struct si476x_core *core,
1476                              struct si476x_power_up_args *args)
1477 {
1478         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1479                core->revision == -1);
1480         return si476x_cmds_vtable[core->revision].power_up(core, args);
1481 }
1482 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1483
1484 int si476x_core_cmd_power_down(struct si476x_core *core,
1485                                struct si476x_power_down_args *args)
1486 {
1487         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1488                core->revision == -1);
1489         return si476x_cmds_vtable[core->revision].power_down(core, args);
1490 }
1491 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1492
1493 int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
1494                                  struct si476x_tune_freq_args *args)
1495 {
1496         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1497                core->revision == -1);
1498         return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
1499 }
1500 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1501
1502 int si476x_core_cmd_am_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].am_tune_freq(core, args);
1508 }
1509 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1510
1511 int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
1512                                   struct si476x_rsq_status_args *args,
1513                                   struct si476x_rsq_status_report *report)
1514
1515 {
1516         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1517                core->revision == -1);
1518         return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
1519                                                                 report);
1520 }
1521 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1522
1523 int si476x_core_cmd_agc_status(struct si476x_core *core,
1524                                   struct si476x_agc_status_report *report)
1525
1526 {
1527         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1528                core->revision == -1);
1529         return si476x_cmds_vtable[core->revision].agc_status(core, report);
1530 }
1531 EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1532
1533 int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
1534                             enum si476x_intb_config intb,
1535                             enum si476x_a1_config a1)
1536 {
1537         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1538                core->revision == -1);
1539
1540         return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
1541 }
1542 EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1543
1544 MODULE_LICENSE("GPL");
1545 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1546 MODULE_DESCRIPTION("API for command exchange for si476x");