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