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