Linux Audio

Check our new training course

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