ASoC: rt286: Add RL6347A class device shared support for RT286
The patch separates the IO function from the rt286. It is prepared to share for new chips that support the same IO function. Signed-off-by: Oder Chiou <oder_chiou@realtek.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
b787f68c36
commit
bc08f96b5f
@ -507,6 +507,11 @@ config SND_SOC_RL6231
|
||||
default m if SND_SOC_RT5670=m
|
||||
default m if SND_SOC_RT5677=m
|
||||
|
||||
config SND_SOC_RL6347A
|
||||
tristate
|
||||
default y if SND_SOC_RT286=y
|
||||
default m if SND_SOC_RT286=m
|
||||
|
||||
config SND_SOC_RT286
|
||||
tristate
|
||||
depends on I2C
|
||||
|
@ -77,6 +77,7 @@ snd-soc-pcm512x-objs := pcm512x.o
|
||||
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
|
||||
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
|
||||
snd-soc-rl6231-objs := rl6231.o
|
||||
snd-soc-rl6347a-objs := rl6347a.o
|
||||
snd-soc-rt286-objs := rt286.o
|
||||
snd-soc-rt5631-objs := rt5631.o
|
||||
snd-soc-rt5640-objs := rt5640.o
|
||||
@ -262,6 +263,7 @@ obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
|
||||
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
|
||||
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
|
||||
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
|
||||
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
|
||||
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
|
||||
|
128
sound/soc/codecs/rl6347a.c
Normal file
128
sound/soc/codecs/rl6347a.c
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* rl6347a.c - RL6347A class device shared support
|
||||
*
|
||||
* Copyright 2015 Realtek Semiconductor Corp.
|
||||
*
|
||||
* Author: Oder Chiou <oder_chiou@realtek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/jack.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <sound/hda_verbs.h>
|
||||
|
||||
#include "rl6347a.h"
|
||||
|
||||
int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value)
|
||||
{
|
||||
struct i2c_client *client = context;
|
||||
struct rl6347a_priv *rl6347a = i2c_get_clientdata(client);
|
||||
u8 data[4];
|
||||
int ret, i;
|
||||
|
||||
/* handle index registers */
|
||||
if (reg <= 0xff) {
|
||||
rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
|
||||
for (i = 0; i < rl6347a->index_cache_size; i++) {
|
||||
if (reg == rl6347a->index_cache[i].reg) {
|
||||
rl6347a->index_cache[i].def = value;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
reg = RL6347A_PROC_COEF;
|
||||
}
|
||||
|
||||
data[0] = (reg >> 24) & 0xff;
|
||||
data[1] = (reg >> 16) & 0xff;
|
||||
/*
|
||||
* 4 bit VID: reg should be 0
|
||||
* 12 bit VID: value should be 0
|
||||
* So we use an OR operator to handle it rather than use if condition.
|
||||
*/
|
||||
data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
|
||||
data[3] = value & 0xff;
|
||||
|
||||
ret = i2c_master_send(client, data, 4);
|
||||
|
||||
if (ret == 4)
|
||||
return 0;
|
||||
else
|
||||
pr_err("ret=%d\n", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rl6347a_hw_write);
|
||||
|
||||
int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value)
|
||||
{
|
||||
struct i2c_client *client = context;
|
||||
struct i2c_msg xfer[2];
|
||||
int ret;
|
||||
__be32 be_reg;
|
||||
unsigned int index, vid, buf = 0x0;
|
||||
|
||||
/* handle index registers */
|
||||
if (reg <= 0xff) {
|
||||
rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
|
||||
reg = RL6347A_PROC_COEF;
|
||||
}
|
||||
|
||||
reg = reg | 0x80000;
|
||||
vid = (reg >> 8) & 0xfff;
|
||||
|
||||
if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
|
||||
index = (reg >> 8) & 0xf;
|
||||
reg = (reg & ~0xf0f) | index;
|
||||
}
|
||||
be_reg = cpu_to_be32(reg);
|
||||
|
||||
/* Write register */
|
||||
xfer[0].addr = client->addr;
|
||||
xfer[0].flags = 0;
|
||||
xfer[0].len = 4;
|
||||
xfer[0].buf = (u8 *)&be_reg;
|
||||
|
||||
/* Read data */
|
||||
xfer[1].addr = client->addr;
|
||||
xfer[1].flags = I2C_M_RD;
|
||||
xfer[1].len = 4;
|
||||
xfer[1].buf = (u8 *)&buf;
|
||||
|
||||
ret = i2c_transfer(client->adapter, xfer, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 2)
|
||||
return -EIO;
|
||||
|
||||
*value = be32_to_cpu(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rl6347a_hw_read);
|
||||
|
||||
MODULE_DESCRIPTION("RL6347A class device shared support");
|
||||
MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
32
sound/soc/codecs/rl6347a.h
Normal file
32
sound/soc/codecs/rl6347a.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* rl6347a.h - RL6347A class device shared support
|
||||
*
|
||||
* Copyright 2015 Realtek Semiconductor Corp.
|
||||
*
|
||||
* Author: Oder Chiou <oder_chiou@realtek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __RL6347A_H__
|
||||
#define __RL6347A_H__
|
||||
|
||||
#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
|
||||
|
||||
#define RL6347A_VENDOR_REGISTERS 0x20
|
||||
|
||||
#define RL6347A_COEF_INDEX\
|
||||
VERB_CMD(AC_VERB_SET_COEF_INDEX, RL6347A_VENDOR_REGISTERS, 0)
|
||||
#define RL6347A_PROC_COEF\
|
||||
VERB_CMD(AC_VERB_SET_PROC_COEF, RL6347A_VENDOR_REGISTERS, 0)
|
||||
|
||||
struct rl6347a_priv {
|
||||
struct reg_default *index_cache;
|
||||
int index_cache_size;
|
||||
};
|
||||
|
||||
int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value);
|
||||
int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value);
|
||||
|
||||
#endif /* __RL6347A_H__ */
|
@ -31,12 +31,15 @@
|
||||
#include <sound/rt286.h>
|
||||
#include <sound/hda_verbs.h>
|
||||
|
||||
#include "rl6347a.h"
|
||||
#include "rt286.h"
|
||||
|
||||
#define RT286_VENDOR_ID 0x10ec0286
|
||||
#define RT288_VENDOR_ID 0x10ec0288
|
||||
|
||||
struct rt286_priv {
|
||||
struct reg_default *index_cache;
|
||||
int index_cache_size;
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_codec *codec;
|
||||
struct rt286_platform_data pdata;
|
||||
@ -45,7 +48,6 @@ struct rt286_priv {
|
||||
struct delayed_work jack_detect_work;
|
||||
int sys_clk;
|
||||
int clk_id;
|
||||
struct reg_default *index_cache;
|
||||
};
|
||||
|
||||
static struct reg_default rt286_index_def[] = {
|
||||
@ -185,94 +187,6 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg)
|
||||
}
|
||||
}
|
||||
|
||||
static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
|
||||
{
|
||||
struct i2c_client *client = context;
|
||||
struct rt286_priv *rt286 = i2c_get_clientdata(client);
|
||||
u8 data[4];
|
||||
int ret, i;
|
||||
|
||||
/* handle index registers */
|
||||
if (reg <= 0xff) {
|
||||
rt286_hw_write(client, RT286_COEF_INDEX, reg);
|
||||
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
|
||||
if (reg == rt286->index_cache[i].reg) {
|
||||
rt286->index_cache[i].def = value;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
reg = RT286_PROC_COEF;
|
||||
}
|
||||
|
||||
data[0] = (reg >> 24) & 0xff;
|
||||
data[1] = (reg >> 16) & 0xff;
|
||||
/*
|
||||
* 4 bit VID: reg should be 0
|
||||
* 12 bit VID: value should be 0
|
||||
* So we use an OR operator to handle it rather than use if condition.
|
||||
*/
|
||||
data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
|
||||
data[3] = value & 0xff;
|
||||
|
||||
ret = i2c_master_send(client, data, 4);
|
||||
|
||||
if (ret == 4)
|
||||
return 0;
|
||||
else
|
||||
pr_err("ret=%d\n", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
|
||||
{
|
||||
struct i2c_client *client = context;
|
||||
struct i2c_msg xfer[2];
|
||||
int ret;
|
||||
__be32 be_reg;
|
||||
unsigned int index, vid, buf = 0x0;
|
||||
|
||||
/* handle index registers */
|
||||
if (reg <= 0xff) {
|
||||
rt286_hw_write(client, RT286_COEF_INDEX, reg);
|
||||
reg = RT286_PROC_COEF;
|
||||
}
|
||||
|
||||
reg = reg | 0x80000;
|
||||
vid = (reg >> 8) & 0xfff;
|
||||
|
||||
if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
|
||||
index = (reg >> 8) & 0xf;
|
||||
reg = (reg & ~0xf0f) | index;
|
||||
}
|
||||
be_reg = cpu_to_be32(reg);
|
||||
|
||||
/* Write register */
|
||||
xfer[0].addr = client->addr;
|
||||
xfer[0].flags = 0;
|
||||
xfer[0].len = 4;
|
||||
xfer[0].buf = (u8 *)&be_reg;
|
||||
|
||||
/* Read data */
|
||||
xfer[1].addr = client->addr;
|
||||
xfer[1].flags = I2C_M_RD;
|
||||
xfer[1].len = 4;
|
||||
xfer[1].buf = (u8 *)&buf;
|
||||
|
||||
ret = i2c_transfer(client->adapter, xfer, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 2)
|
||||
return -EIO;
|
||||
|
||||
*value = be32_to_cpu(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void rt286_index_sync(struct snd_soc_codec *codec)
|
||||
{
|
||||
@ -1173,8 +1087,8 @@ static const struct regmap_config rt286_regmap = {
|
||||
.max_register = 0x02370100,
|
||||
.volatile_reg = rt286_volatile_register,
|
||||
.readable_reg = rt286_readable_register,
|
||||
.reg_write = rt286_hw_write,
|
||||
.reg_read = rt286_hw_read,
|
||||
.reg_write = rl6347a_hw_write,
|
||||
.reg_read = rl6347a_hw_read,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.reg_defaults = rt286_reg,
|
||||
.num_reg_defaults = ARRAY_SIZE(rt286_reg),
|
||||
@ -1247,6 +1161,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
|
||||
}
|
||||
|
||||
rt286->index_cache = rt286_index_def;
|
||||
rt286->index_cache_size = INDEX_CACHE_SIZE;
|
||||
rt286->i2c = i2c;
|
||||
i2c_set_clientdata(i2c, rt286);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user