]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/mfd/si476x-cmd.c
KARO: cleanup after merge of Freescale 3.10.17 stuff
[karo-tx-linux.git] / drivers / mfd / si476x-cmd.c
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
147 enum 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
160 enum 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
167 enum 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
180 static 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  */
266 static 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;
352 exit:
353         return err;
354 }
355
356 static 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
381 static 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  */
413 int 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 }
433 EXPORT_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  */
444 int 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 }
461 EXPORT_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  */
471 int 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 }
490 EXPORT_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  */
529 int 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 }
548 EXPORT_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  */
578 int 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 }
597 EXPORT_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  */
635 int 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 }
654 EXPORT_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  */
669 int 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 }
682 EXPORT_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  */
703 static 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
719 static 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  */
752 int 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 }
799 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
800
801 int 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 }
835 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
836
837 int 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 }
868 EXPORT_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  */
885 int 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 }
897 EXPORT_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  */
911 int 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 }
975 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
976
977 int 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 }
1003 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
1004
1005 int 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 }
1018 EXPORT_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  */
1030 int 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 }
1042 EXPORT_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  */
1059 int 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 }
1071 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1072
1073
1074
1075 static 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
1098 static 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
1120 static 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
1131 static 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
1144 static 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
1161 static 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
1177 static 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
1230 static 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
1285 static 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
1347 static 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
1365 static 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
1381 static 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
1409 static 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
1433 typedef int (*tune_freq_func_t) (struct si476x_core *core,
1434                                  struct si476x_tune_freq_args *tuneargs);
1435
1436 static 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
1484 int 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 }
1491 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1492
1493 int 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 }
1500 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1501
1502 int 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 }
1509 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1510
1511 int 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 }
1518 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1519
1520 int 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 }
1530 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1531
1532 int 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 }
1540 EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1541
1542 int 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 }
1551 EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1552
1553 MODULE_LICENSE("GPL");
1554 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1555 MODULE_DESCRIPTION("API for command exchange for si476x");