xref: /openbmc/linux/sound/soc/codecs/rl6347a.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2bc08f96bSOder Chiou /*
3bc08f96bSOder Chiou  * rl6347a.c - RL6347A class device shared support
4bc08f96bSOder Chiou  *
5bc08f96bSOder Chiou  * Copyright 2015 Realtek Semiconductor Corp.
6bc08f96bSOder Chiou  *
7bc08f96bSOder Chiou  * Author: Oder Chiou <oder_chiou@realtek.com>
8bc08f96bSOder Chiou  */
9bc08f96bSOder Chiou 
10bc08f96bSOder Chiou #include <linux/module.h>
11bc08f96bSOder Chiou #include <linux/i2c.h>
120f7e1774SAxel Lin #include <linux/regmap.h>
13bc08f96bSOder Chiou 
14bc08f96bSOder Chiou #include "rl6347a.h"
15bc08f96bSOder Chiou 
rl6347a_hw_write(void * context,unsigned int reg,unsigned int value)16bc08f96bSOder Chiou int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value)
17bc08f96bSOder Chiou {
18bc08f96bSOder Chiou 	struct i2c_client *client = context;
19bc08f96bSOder Chiou 	struct rl6347a_priv *rl6347a = i2c_get_clientdata(client);
20bc08f96bSOder Chiou 	u8 data[4];
21bc08f96bSOder Chiou 	int ret, i;
22bc08f96bSOder Chiou 
23bc08f96bSOder Chiou 	/* handle index registers */
24bc08f96bSOder Chiou 	if (reg <= 0xff) {
25bc08f96bSOder Chiou 		rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
26bc08f96bSOder Chiou 		for (i = 0; i < rl6347a->index_cache_size; i++) {
27bc08f96bSOder Chiou 			if (reg == rl6347a->index_cache[i].reg) {
28bc08f96bSOder Chiou 				rl6347a->index_cache[i].def = value;
29bc08f96bSOder Chiou 				break;
30bc08f96bSOder Chiou 			}
31bc08f96bSOder Chiou 
32bc08f96bSOder Chiou 		}
33bc08f96bSOder Chiou 		reg = RL6347A_PROC_COEF;
34bc08f96bSOder Chiou 	}
35bc08f96bSOder Chiou 
36bc08f96bSOder Chiou 	data[0] = (reg >> 24) & 0xff;
37bc08f96bSOder Chiou 	data[1] = (reg >> 16) & 0xff;
38bc08f96bSOder Chiou 	/*
39bc08f96bSOder Chiou 	 * 4 bit VID: reg should be 0
40bc08f96bSOder Chiou 	 * 12 bit VID: value should be 0
41bc08f96bSOder Chiou 	 * So we use an OR operator to handle it rather than use if condition.
42bc08f96bSOder Chiou 	 */
43bc08f96bSOder Chiou 	data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
44bc08f96bSOder Chiou 	data[3] = value & 0xff;
45bc08f96bSOder Chiou 
46bc08f96bSOder Chiou 	ret = i2c_master_send(client, data, 4);
47bc08f96bSOder Chiou 
48bc08f96bSOder Chiou 	if (ret == 4)
49bc08f96bSOder Chiou 		return 0;
50bc08f96bSOder Chiou 	else
51b058b176SJarkko Nikula 		dev_err(&client->dev, "I2C error %d\n", ret);
52bc08f96bSOder Chiou 	if (ret < 0)
53bc08f96bSOder Chiou 		return ret;
54bc08f96bSOder Chiou 	else
55bc08f96bSOder Chiou 		return -EIO;
56bc08f96bSOder Chiou }
57bc08f96bSOder Chiou EXPORT_SYMBOL_GPL(rl6347a_hw_write);
58bc08f96bSOder Chiou 
rl6347a_hw_read(void * context,unsigned int reg,unsigned int * value)59bc08f96bSOder Chiou int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value)
60bc08f96bSOder Chiou {
61bc08f96bSOder Chiou 	struct i2c_client *client = context;
62bc08f96bSOder Chiou 	struct i2c_msg xfer[2];
63bc08f96bSOder Chiou 	int ret;
64b8e022e8SBard liao 	__be32 be_reg, buf = 0x0;
65b8e022e8SBard liao 	unsigned int index, vid;
66bc08f96bSOder Chiou 
67bc08f96bSOder Chiou 	/* handle index registers */
68bc08f96bSOder Chiou 	if (reg <= 0xff) {
69bc08f96bSOder Chiou 		rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
70bc08f96bSOder Chiou 		reg = RL6347A_PROC_COEF;
71bc08f96bSOder Chiou 	}
72bc08f96bSOder Chiou 
73bc08f96bSOder Chiou 	reg = reg | 0x80000;
74bc08f96bSOder Chiou 	vid = (reg >> 8) & 0xfff;
75bc08f96bSOder Chiou 
76bc08f96bSOder Chiou 	if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
77bc08f96bSOder Chiou 		index = (reg >> 8) & 0xf;
78bc08f96bSOder Chiou 		reg = (reg & ~0xf0f) | index;
79bc08f96bSOder Chiou 	}
80bc08f96bSOder Chiou 	be_reg = cpu_to_be32(reg);
81bc08f96bSOder Chiou 
82bc08f96bSOder Chiou 	/* Write register */
83bc08f96bSOder Chiou 	xfer[0].addr = client->addr;
84bc08f96bSOder Chiou 	xfer[0].flags = 0;
85bc08f96bSOder Chiou 	xfer[0].len = 4;
86bc08f96bSOder Chiou 	xfer[0].buf = (u8 *)&be_reg;
87bc08f96bSOder Chiou 
88bc08f96bSOder Chiou 	/* Read data */
89bc08f96bSOder Chiou 	xfer[1].addr = client->addr;
90bc08f96bSOder Chiou 	xfer[1].flags = I2C_M_RD;
91bc08f96bSOder Chiou 	xfer[1].len = 4;
92bc08f96bSOder Chiou 	xfer[1].buf = (u8 *)&buf;
93bc08f96bSOder Chiou 
94bc08f96bSOder Chiou 	ret = i2c_transfer(client->adapter, xfer, 2);
95bc08f96bSOder Chiou 	if (ret < 0)
96bc08f96bSOder Chiou 		return ret;
97bc08f96bSOder Chiou 	else if (ret != 2)
98bc08f96bSOder Chiou 		return -EIO;
99bc08f96bSOder Chiou 
100bc08f96bSOder Chiou 	*value = be32_to_cpu(buf);
101bc08f96bSOder Chiou 
102bc08f96bSOder Chiou 	return 0;
103bc08f96bSOder Chiou }
104bc08f96bSOder Chiou EXPORT_SYMBOL_GPL(rl6347a_hw_read);
105bc08f96bSOder Chiou 
106bc08f96bSOder Chiou MODULE_DESCRIPTION("RL6347A class device shared support");
107bc08f96bSOder Chiou MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
108bc08f96bSOder Chiou MODULE_LICENSE("GPL v2");
109