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