18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * rl6347a.c - RL6347A class device shared support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2015 Realtek Semiconductor Corp.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Oder Chiou <oder_chiou@realtek.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/regmap.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "rl6347a.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciint rl6347a_hw_write(void *context, unsigned int reg, unsigned int value)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	struct i2c_client *client = context;
198c2ecf20Sopenharmony_ci	struct rl6347a_priv *rl6347a = i2c_get_clientdata(client);
208c2ecf20Sopenharmony_ci	u8 data[4];
218c2ecf20Sopenharmony_ci	int ret, i;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	/* handle index registers */
248c2ecf20Sopenharmony_ci	if (reg <= 0xff) {
258c2ecf20Sopenharmony_ci		rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
268c2ecf20Sopenharmony_ci		for (i = 0; i < rl6347a->index_cache_size; i++) {
278c2ecf20Sopenharmony_ci			if (reg == rl6347a->index_cache[i].reg) {
288c2ecf20Sopenharmony_ci				rl6347a->index_cache[i].def = value;
298c2ecf20Sopenharmony_ci				break;
308c2ecf20Sopenharmony_ci			}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci		}
338c2ecf20Sopenharmony_ci		reg = RL6347A_PROC_COEF;
348c2ecf20Sopenharmony_ci	}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	data[0] = (reg >> 24) & 0xff;
378c2ecf20Sopenharmony_ci	data[1] = (reg >> 16) & 0xff;
388c2ecf20Sopenharmony_ci	/*
398c2ecf20Sopenharmony_ci	 * 4 bit VID: reg should be 0
408c2ecf20Sopenharmony_ci	 * 12 bit VID: value should be 0
418c2ecf20Sopenharmony_ci	 * So we use an OR operator to handle it rather than use if condition.
428c2ecf20Sopenharmony_ci	 */
438c2ecf20Sopenharmony_ci	data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
448c2ecf20Sopenharmony_ci	data[3] = value & 0xff;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	ret = i2c_master_send(client, data, 4);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (ret == 4)
498c2ecf20Sopenharmony_ci		return 0;
508c2ecf20Sopenharmony_ci	else
518c2ecf20Sopenharmony_ci		dev_err(&client->dev, "I2C error %d\n", ret);
528c2ecf20Sopenharmony_ci	if (ret < 0)
538c2ecf20Sopenharmony_ci		return ret;
548c2ecf20Sopenharmony_ci	else
558c2ecf20Sopenharmony_ci		return -EIO;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rl6347a_hw_write);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ciint rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct i2c_client *client = context;
628c2ecf20Sopenharmony_ci	struct i2c_msg xfer[2];
638c2ecf20Sopenharmony_ci	int ret;
648c2ecf20Sopenharmony_ci	__be32 be_reg, buf = 0x0;
658c2ecf20Sopenharmony_ci	unsigned int index, vid;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	/* handle index registers */
688c2ecf20Sopenharmony_ci	if (reg <= 0xff) {
698c2ecf20Sopenharmony_ci		rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
708c2ecf20Sopenharmony_ci		reg = RL6347A_PROC_COEF;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	reg = reg | 0x80000;
748c2ecf20Sopenharmony_ci	vid = (reg >> 8) & 0xfff;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
778c2ecf20Sopenharmony_ci		index = (reg >> 8) & 0xf;
788c2ecf20Sopenharmony_ci		reg = (reg & ~0xf0f) | index;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci	be_reg = cpu_to_be32(reg);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* Write register */
838c2ecf20Sopenharmony_ci	xfer[0].addr = client->addr;
848c2ecf20Sopenharmony_ci	xfer[0].flags = 0;
858c2ecf20Sopenharmony_ci	xfer[0].len = 4;
868c2ecf20Sopenharmony_ci	xfer[0].buf = (u8 *)&be_reg;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* Read data */
898c2ecf20Sopenharmony_ci	xfer[1].addr = client->addr;
908c2ecf20Sopenharmony_ci	xfer[1].flags = I2C_M_RD;
918c2ecf20Sopenharmony_ci	xfer[1].len = 4;
928c2ecf20Sopenharmony_ci	xfer[1].buf = (u8 *)&buf;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	ret = i2c_transfer(client->adapter, xfer, 2);
958c2ecf20Sopenharmony_ci	if (ret < 0)
968c2ecf20Sopenharmony_ci		return ret;
978c2ecf20Sopenharmony_ci	else if (ret != 2)
988c2ecf20Sopenharmony_ci		return -EIO;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	*value = be32_to_cpu(buf);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rl6347a_hw_read);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RL6347A class device shared support");
1078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
1088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
109