162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * rl6347a.c - RL6347A class device shared support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2015 Realtek Semiconductor Corp.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Oder Chiou <oder_chiou@realtek.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/i2c.h>
1262306a36Sopenharmony_ci#include <linux/regmap.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "rl6347a.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciint rl6347a_hw_write(void *context, unsigned int reg, unsigned int value)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	struct i2c_client *client = context;
1962306a36Sopenharmony_ci	struct rl6347a_priv *rl6347a = i2c_get_clientdata(client);
2062306a36Sopenharmony_ci	u8 data[4];
2162306a36Sopenharmony_ci	int ret, i;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	/* handle index registers */
2462306a36Sopenharmony_ci	if (reg <= 0xff) {
2562306a36Sopenharmony_ci		rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
2662306a36Sopenharmony_ci		for (i = 0; i < rl6347a->index_cache_size; i++) {
2762306a36Sopenharmony_ci			if (reg == rl6347a->index_cache[i].reg) {
2862306a36Sopenharmony_ci				rl6347a->index_cache[i].def = value;
2962306a36Sopenharmony_ci				break;
3062306a36Sopenharmony_ci			}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci		}
3362306a36Sopenharmony_ci		reg = RL6347A_PROC_COEF;
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	data[0] = (reg >> 24) & 0xff;
3762306a36Sopenharmony_ci	data[1] = (reg >> 16) & 0xff;
3862306a36Sopenharmony_ci	/*
3962306a36Sopenharmony_ci	 * 4 bit VID: reg should be 0
4062306a36Sopenharmony_ci	 * 12 bit VID: value should be 0
4162306a36Sopenharmony_ci	 * So we use an OR operator to handle it rather than use if condition.
4262306a36Sopenharmony_ci	 */
4362306a36Sopenharmony_ci	data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
4462306a36Sopenharmony_ci	data[3] = value & 0xff;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	ret = i2c_master_send(client, data, 4);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (ret == 4)
4962306a36Sopenharmony_ci		return 0;
5062306a36Sopenharmony_ci	else
5162306a36Sopenharmony_ci		dev_err(&client->dev, "I2C error %d\n", ret);
5262306a36Sopenharmony_ci	if (ret < 0)
5362306a36Sopenharmony_ci		return ret;
5462306a36Sopenharmony_ci	else
5562306a36Sopenharmony_ci		return -EIO;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rl6347a_hw_write);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciint rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct i2c_client *client = context;
6262306a36Sopenharmony_ci	struct i2c_msg xfer[2];
6362306a36Sopenharmony_ci	int ret;
6462306a36Sopenharmony_ci	__be32 be_reg, buf = 0x0;
6562306a36Sopenharmony_ci	unsigned int index, vid;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	/* handle index registers */
6862306a36Sopenharmony_ci	if (reg <= 0xff) {
6962306a36Sopenharmony_ci		rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
7062306a36Sopenharmony_ci		reg = RL6347A_PROC_COEF;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	reg = reg | 0x80000;
7462306a36Sopenharmony_ci	vid = (reg >> 8) & 0xfff;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
7762306a36Sopenharmony_ci		index = (reg >> 8) & 0xf;
7862306a36Sopenharmony_ci		reg = (reg & ~0xf0f) | index;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci	be_reg = cpu_to_be32(reg);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Write register */
8362306a36Sopenharmony_ci	xfer[0].addr = client->addr;
8462306a36Sopenharmony_ci	xfer[0].flags = 0;
8562306a36Sopenharmony_ci	xfer[0].len = 4;
8662306a36Sopenharmony_ci	xfer[0].buf = (u8 *)&be_reg;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Read data */
8962306a36Sopenharmony_ci	xfer[1].addr = client->addr;
9062306a36Sopenharmony_ci	xfer[1].flags = I2C_M_RD;
9162306a36Sopenharmony_ci	xfer[1].len = 4;
9262306a36Sopenharmony_ci	xfer[1].buf = (u8 *)&buf;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	ret = i2c_transfer(client->adapter, xfer, 2);
9562306a36Sopenharmony_ci	if (ret < 0)
9662306a36Sopenharmony_ci		return ret;
9762306a36Sopenharmony_ci	else if (ret != 2)
9862306a36Sopenharmony_ci		return -EIO;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	*value = be32_to_cpu(buf);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rl6347a_hw_read);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciMODULE_DESCRIPTION("RL6347A class device shared support");
10762306a36Sopenharmony_ciMODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
10862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
109