// SPDX-License-Identifier: GPL-2.0-or-later
/*
* TAS571x amplifier audio driver
*
* Copyright (C) 2015 Google, Inc.
* Copyright (c) 2013 Daniel Mack <zonque@gmail.com>
*
* TAS5721 support:
* Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com>
*
* TAS5707 support:
* Copyright (C) 2018 Jerome Brunet, Baylibre SAS <jbrunet@baylibre.com>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/stddef.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <asm/unaligned.h>
#include "tas571x.h"
#define TAS571X_MAX_SUPPLIES 6
struct tas571x_chip {
const char *const *supply_names;
int num_supply_names;
const struct snd_kcontrol_new *controls;
int num_controls;
const struct regmap_config *regmap_config;
int vol_reg_size;
};
struct tas571x_private {
const struct tas571x_chip *chip;
struct regmap *regmap;
struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES];
struct clk *mclk;
unsigned int format;
struct gpio_desc *reset_gpio;
struct gpio_desc *pdn_gpio;
struct snd_soc_component_driver component_driver;
};
static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
{
switch (reg) {
case TAS571X_MVOL_REG:
case TAS571X_CH1_VOL_REG:
case TAS571X_CH2_VOL_REG:
return priv->chip->vol_reg_size;
case TAS571X_INPUT_MUX_REG:
case TAS571X_CH4_SRC_SELECT_REG:
case TAS571X_PWM_MUX_REG:
case TAS5717_CH1_RIGHT_CH_MIX_REG:
case TAS5717_CH1_LEFT_CH_MIX_REG:
case TAS5717_CH2_LEFT_CH_MIX_REG:
case TAS5717_CH2_RIGHT_CH_MIX_REG:
return 4;
default:
return 1;
}
}
static int tas571x_reg_write(void *context, unsigned int reg,
unsigned int value)
{
struct i2c_client *client = context;
struct tas571x_private *priv = i2c_get_clientdata(client);
unsigned int i, size;
uint8_t buf[5];
int ret;
size = tas571x_register_size(priv, reg);
buf[0] = reg;
for (i = size; i >= 1; --i) {
buf[i] = value;
value >>= 8;
}
ret = i2c_master_send(client, buf, size + 1);
if (ret == size + 1)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static int tas571x_reg_read(void *context, unsigned int reg,
unsigned int *value)
{
struct i2c_client *client = context;
struct tas571x_private *priv = i2c_get_clientdata(client);
uint8_t send_buf, recv_buf[4];
struct i2c_msg msgs[2];
unsigned int size;
unsigned int i;
int ret;
size = tas571x_register_size(priv, reg);
send_buf = reg;
msgs[0].addr = client->addr;
msgs[0].len = sizeof(send_buf);
msgs[0].buf = &send_buf;
msgs[0].flags = 0;
msgs[1].addr = client->addr;
msgs[1].len = size;
msgs[1].buf = recv_buf;
msgs[1].flags = I2C_M_RD;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
return ret;
else if (ret != ARRAY_SIZE(msgs))
return -EIO;
*value = 0;
for (i = 0; i < size; i++) {
*value <<= 8;
*value |= recv_buf[i];
}
return 0;
}
/*
* register write for 8- and 20-byte registers
*/
static int tas571x_reg_write_multiword(struct i2c_client *client,
unsigned int reg, const long values[], size_t len)
{
size_t i;
uint8_t *buf, *p;
int ret;
size_t send_size = 1 + len * sizeof(uint32_t);
buf = kzalloc(send_size, GFP_KERNEL | GFP_DMA);
if (!buf)
return -ENOMEM;
buf[0] = reg;
for (i = 0, p = buf