blob: e66c97c999b34fa29a1448d4ca6f9423f2e0a878 [file] [log] [blame]
/*
* audiocodec.c - setup comet audio codec, DAC & ADC.
*
* Copyright (C) 2010 Imagination Technologies
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <sound/tansen.h>
#include <asm/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/soc-tz1090/defs.h>
#include <asm/soc-tz1090/audiocodec.h>
#include <asm/soc-tz1090/hdmi-audio.h>
static struct kobject *ac_kobj;
/*
* TM_PWM*_S1 registers
* Apparently not where indicated in the register list, but in 'reserved' regions?!?
*/
static unsigned long pwms1_regs[AUDIOCODEC_NUM_STEREOPAIRS] = {
0x826, /* TW_PWM1_S1 */
0x8AA, /* TW_PWM2_S1 */
0x926, /* TW_PWM3_S1 */
};
struct str_int_map_t {
char *name;
int val;
};
static struct str_int_map_t map_bool[] = {
{ "false", 0 },
{ "true", 1 },
{ NULL, -1 },
};
static struct str_int_map_t map_samplewidth[] = {
{ "16b", AUDIOCODEC_SAMPLEWIDTH_16 },
{ "20b", AUDIOCODEC_SAMPLEWIDTH_20 },
{ "24b", AUDIOCODEC_SAMPLEWIDTH_24 },
{ "32b", AUDIOCODEC_SAMPLEWIDTH_32 },
{ NULL, -1 },
};
static struct str_int_map_t map_input[] = {
{ "mic", AUDIOCODEC_INPUT_MIC },
{ "line", AUDIOCODEC_INPUT_LINE },
{ "ipod", AUDIOCODEC_INPUT_IPOD },
{ "mic_differential", AUDIOCODEC_INPUT_MIC_DIFFERENTIAL },
{ NULL, -1 },
};
static struct str_int_map_t map_mute[] = {
{ "none", AUDIOCODEC_MUTE_NONE },
{ "hard", AUDIOCODEC_MUTE_HARD },
{ "90db", AUDIOCODEC_MUTE_90DB },
{ "squarewave", AUDIOCODEC_MUTE_SQUAREWAVE },
{ NULL, -1 },
};
static struct str_int_map_t map_frame[] = {
{ "1616", AUDIOCODEC_FRAME_1616 },
{ "3232", AUDIOCODEC_FRAME_3232 },
};
static struct str_int_map_t map_clock[] = {
{ "256fs", AUDIOCODEC_CLOCK_256FS },
{ "384fs", AUDIOCODEC_CLOCK_384FS },
};
static struct str_int_map_t map_i2sclock[] = {
{ "disabled", AUDIOCODEC_I2SCLOCK_DISABLED },
{ "xtal1", AUDIOCODEC_I2SCLOCK_XTAL1 },
{ "xtal2", AUDIOCODEC_I2SCLOCK_XTAL2 },
{ "sys_undeleted", AUDIOCODEC_I2SCLOCK_SYS_UNDELETED },
{ "adc_pll", AUDIOCODEC_I2SCLOCK_ADC_PLL },
{ NULL, -1 },
};
static struct str_int_map_t map_preset[] = {
{ "none", AUDIOCODEC_PRESET_NONE },
{ "phono", AUDIOCODEC_PRESET_PHONO },
#ifdef CONFIG_TZ1090_01XX_HDMI_AUDIO
{ "hdmi", AUDIOCODEC_PRESET_HDMI },
{ "default", AUDIOCODEC_PRESET_PHONO | AUDIOCODEC_PRESET_HDMI },
#else
{ "default", AUDIOCODEC_PRESET_PHONO },
#endif
{ NULL, -1 },
};
static int map_lookup(struct str_int_map_t *map, const char *str)
{
size_t len = strlen(str);
size_t cmplen = len;
int i;
if (len == 0)
return -1;
if (str[len-1] == '\n')
cmplen--;
for (i = 0; map[i].name; i++) {
if (!strncmp(map[i].name, str, cmplen))
return map[i].val;
}
return -1;
}
static int map_lookup_flags(struct str_int_map_t *map, const char *str)
{
size_t len;
int i, val = 0;
bool change;
do {
change = false;
for (i = 0; map[i].name; i++) {
len = strlen(map[i].name);
if (strncmp(map[i].name, str, len))
continue;
if (str[len] == ',' || str[len] == '\n' ||
str[len] == 0) {
val |= map[i].val;
change = true;
str += len + (str[len] == ',' ? 1 : 0);
}
}
} while (change);
return val;
}
static ssize_t map_string(struct str_int_map_t *map, int val, char *buf,
size_t sz)
{
int i;
for (i = 0; map[i].name; i++) {
if (map[i].val == val)
return snprintf(buf, sz, "%s\n", map[i].name);
}
return snprintf(buf, sz, "err\n");
}
static ssize_t map_string_flags(struct str_int_map_t *map, int val, char *buf,
size_t sz)
{
int i;
char *str = buf;
size_t written, rem = sz - 1;
for (i = 0; map[i].name; i++) {
if (!map[i].val)
continue;
if ((map[i].val & val) == map[i].val) {
if (buf < str) {
*str++ = ',';
*str = 0;
}
written = snprintf(str, rem, "%s", map[i].name);
str += written;
rem -= written;
val &= ~map[i].val;
}
}
if (str == buf) {
/* outputted nothing */
/* look for a zero mapping */
for (i = 0; map[i].name; i++) {
if (!map[i].val)
return snprintf(buf, sz, "%s\n", map[i].name);
}
}
*str++ = '\n';
*str = 0;
return (ssize_t)(str - buf);
}
static void ac_reset(void)
{
writel(1, AUDIO_OUT_SOFT_RESET);
udelay(1000);
writel(0, AUDIO_OUT_SOFT_RESET);
udelay(1000);
}
static int ac_dac_power_get(bool *pwr)
{
uint32_t hpctrl = readl(CR_AUDIO_HP_CTRL);
*pwr = !(hpctrl & AUDIO_PWDN_BG_OP);
return 0;
}
static int ac_dac_power_set(bool pwr)
{
uint32_t hpctrl = readl(CR_AUDIO_HP_CTRL);
uint32_t adcctrl = readl(CR_AUDIO_ADC_CTRL);
/* GTI port must be in reset for power up to work */
gti_reset((void __iomem *)CR_TOP_AUDGTI_CTRL, 1);
if (pwr) {
/* Power up bandgap */
hpctrl &= ~(AUDIO_PWDN_BG_OP | AUDIO_PWDN_BG_IP);
writel(hpctrl, CR_AUDIO_HP_CTRL);
udelay(100);
/* Reset bandgap filter */
hpctrl |= (AUDIO_RST_BG_OP | AUDIO_RST_BG_IP);
writel(hpctrl, CR_AUDIO_HP_CTRL);
udelay(250);
hpctrl &= ~(AUDIO_RST_BG_OP | AUDIO_RST_BG_IP);
writel(hpctrl, CR_AUDIO_HP_CTRL);
udelay(100);
/* Power up PLL */
hpctrl &= ~AUDIO_PWDN_PLL;
writel(hpctrl, CR_AUDIO_HP_CTRL);
udelay(100);
/* Take digital blocks out of reset */
hpctrl &= ~(AUDIO_RSTB_DIG_OP | AUDIO_RSTB_DIG_IP);
writel(hpctrl, CR_AUDIO_HP_CTRL);
udelay(100);
/* Power up outputs */
hpctrl |= (AUDIO_PSCNT_PWM_F | AUDIO_PSCNT_PWM_E |
AUDIO_PSCNT_PWM_D | AUDIO_PSCNT_PWM_C |
AUDIO_PSCNT_PWM_B | AUDIO_PSCNT_PWM_A);
hpctrl |= (AUDIO_PSCNTHP_R | AUDIO_PSCNTHP_L);
writel(hpctrl, CR_AUDIO_HP_CTRL);
adcctrl |= (AUDIO_PSCNTADC_R | AUDIO_PSCNTADC_L);
writel(adcctrl, CR_AUDIO_ADC_CTRL);
udelay(100);
/* Reset analogue filters */
hpctrl |= (AUDIO_RSTB_ANA_OP | AUDIO_RSTB_ANA_IP);
writel(hpctrl, CR_AUDIO_HP_CTRL);
udelay(250);
hpctrl &= ~(AUDIO_RSTB_ANA_OP | AUDIO_RSTB_ANA_IP);
writel(hpctrl, CR_AUDIO_HP_CTRL);
udelay(250);
} else {
hpctrl |= (AUDIO_PWDN_BG_OP | AUDIO_PWDN_BG_IP);
hpctrl |= (AUDIO_RSTB_DIG_OP | AUDIO_RSTB_DIG_IP);
writel(hpctrl, CR_AUDIO_HP_CTRL);
}
/* Take the GTI port out of reset */
gti_reset((void __iomem *)CR_TOP_AUDGTI_CTRL, 0);
return 0;
}
static int ac_i2s_out_clock_get(int *clksrc)
{
uint32_t val = readl(CR_TOP_CLKENAB);
bool sw0, sw1, sw2;
if (!(val & (1 << CR_TOP_I2S_1_EN_BIT))) {
*clksrc = AUDIOCODEC_I2SCLOCK_DISABLED;
} else {
val = readl(CR_TOP_CLKSWITCH);
sw0 = !!(val & (1 << CR_TOP_I2S_0_SW_BIT));
sw1 = !!(val & (1 << CR_TOP_I2S_1_SW_BIT));
sw2 = !!(val & (1 << CR_TOP_I2S_2_SW_BIT));
if (!sw1) {
if (sw2)
*clksrc = AUDIOCODEC_I2SCLOCK_SYS_UNDELETED;
else
*clksrc = AUDIOCODEC_I2SCLOCK_XTAL1;
} else if (!sw0)
*clksrc = AUDIOCODEC_I2SCLOCK_XTAL2;
else
*clksrc = AUDIOCODEC_I2SCLOCK_ADC_PLL;
}
return 0;
}
static int ac_i2s_out_clock_set(int clksrc)
{
uint32_t val = readl(CR_TOP_CLKENAB);
if (clksrc == AUDIOCODEC_I2SCLOCK_DISABLED) {
val &= ~(1 << CR_TOP_I2S_1_EN_BIT);
writel(val, CR_TOP_CLKENAB);
return 0;
}
val |= (1 << CR_TOP_I2S_1_EN_BIT);
writel(val, CR_TOP_CLKENAB);
val = readl(CR_TOP_CLKSWITCH);
val &= ~(1 << CR_TOP_I2S_0_SW_BIT);
val &= ~(1 << CR_TOP_I2S_1_SW_BIT);
val &= ~(1 << CR_TOP_I2S_2_SW_BIT);
switch (clksrc) {
case AUDIOCODEC_I2SCLOCK_XTAL1:
break;
case AUDIOCODEC_I2SCLOCK_XTAL2:
val |= (1 << CR_TOP_I2S_1_SW_BIT);
break;
case AUDIOCODEC_I2SCLOCK_SYS_UNDELETED:
val |= (1 << CR_TOP_I2S_2_SW_BIT);
break;
case AUDIOCODEC_I2SCLOCK_ADC_PLL:
val |= (1 << CR_TOP_I2S_0_SW_BIT);
val |= (1 << CR_TOP_I2S_1_SW_BIT);
break;
default:
return -EINVAL;
}
writel(val, CR_TOP_CLKSWITCH);
return 0;
}
static int ac_i2s_out_clockdiv_get(int *div)
{
*div = readl(CR_TOP_I2SCLK_DIV) & 0xff;
return 0;
}
static int ac_i2s_out_clockdiv_set(int div)
{
writel(div & 0xff, CR_TOP_I2SCLK_DIV);
return 0;
}
static int ac_i2s_out_clockdiv2_get(int *div)
{
*div = readl(CR_TOP_I2S_DIV2) & 0x3;
return 0;
}
static int ac_i2s_out_clockdiv2_set(int div)
{
writel(div & 0x3, CR_TOP_I2S_DIV2);
return 0;
}
static int ac_i2s_out_active_channels_get(int *chans)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
*chans = (val >> AUDIO_OUT_CM_ACTIVE_CHAN_SHIFT) + 1;
return 0;
}
static int ac_i2s_out_active_channels_set(int chans)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
val &= ~0xffffc000;
val |= (chans - 1) << AUDIO_OUT_CM_ACTIVE_CHAN_SHIFT;
writel(val, AUDIO_OUT_CONTROL_MAIN);
return 0;
}
static int ac_i2s_out_frame_get(int *frame)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
*frame = (val >> AUDIO_OUT_CM_FRAME_SHIFT) & AUDIO_OUT_CM_FRAME_MASK;
return 0;
}
static int ac_i2s_out_frame_set(int frame)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
val &= ~AUDIO_OUT_CM_FRAME_MASK;
val |= frame << AUDIO_OUT_CM_FRAME_SHIFT;
writel(val, AUDIO_OUT_CONTROL_MAIN);
return 0;
}
static int ac_i2s_out_master_get(bool *en)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
*en = !!(val & AUDIO_OUT_CM_MASTER);
return 0;
}
static int ac_i2s_out_master_set(bool en)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
val &= ~AUDIO_OUT_CM_MASTER;
if (en)
val |= AUDIO_OUT_CM_MASTER;
writel(val, AUDIO_OUT_CONTROL_MAIN);
return 0;
}
static int ac_i2s_out_aclk_get(int *aclk)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
*aclk = (val >> AUDIO_OUT_CM_ACLK_SHIFT) & AUDIO_OUT_CM_ACLK_MASK;
return 0;
}
static int ac_i2s_out_aclk_set(int aclk)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
val &= ~AUDIO_OUT_CM_ACLK_MASK;
val |= aclk << AUDIO_OUT_CM_ACLK_SHIFT;
writel(val, AUDIO_OUT_CONTROL_MAIN);
return 0;
}
static int ac_i2s_out_blrclk_get(bool *en)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
*en = !!(val & AUDIO_OUT_CM_BLRCLK_EN);
return 0;
}
static int ac_i2s_out_blrclk_set(bool en)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
val &= ~AUDIO_OUT_CM_BLRCLK_EN;
if (en)
val |= AUDIO_OUT_CM_BLRCLK_EN;
writel(val, AUDIO_OUT_CONTROL_MAIN);
return 0;
}
static int ac_i2s_out_enable_get(bool *en)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
*en = !!(val & AUDIO_OUT_CM_ME);
return 0;
}
static int ac_i2s_out_enable_set(bool en)
{
uint32_t val = readl(AUDIO_OUT_CONTROL_MAIN);
val &= ~AUDIO_OUT_CM_ME;
if (en)
val |= AUDIO_OUT_CM_ME;
writel(val, AUDIO_OUT_CONTROL_MAIN);
return 0;
}
static int ac_i2swidth_dac_get(int *sw)
{
uint32_t val = gti_read((void __iomem *)CR_TOP_AUDGTI_CTRL, 0xe85);
*sw = val & 0x3;
return 0;
}
static int ac_i2swidth_dac_set(int sw)
{
int i;
uint32_t addr;
uint32_t val = gti_read((void __iomem *)CR_TOP_AUDGTI_CTRL, 0xe85);
val &= ~0x3; /* Clear TX width field */
val |= sw;
gti_write((void __iomem *)CR_TOP_AUDGTI_CTRL, 0xe85, val);
for (i = 0; i < AUDIOCODEC_NUM_STEREOPAIRS; i++) {
addr = AUDIO_OUT_BASE_ADDR + (0x20 * AUDIOCODEC_NUM_STEREOPAIRS)
+ (0x20 * i) + 0x04;
val = readl(addr);
val &= ~AUDIO_OUT_CC_FORMAT_MASK;
val |= 4 << AUDIO_OUT_CC_FORMAT_SHIFT;
val |= AUDIO_OUT_CC_LEFT_JUST;
writel(val, addr);
}
return 0;
}
static int ac_i2swidth_adc_get(int *sw)
{
uint32_t val = gti_read((void __iomem *)CR_TOP_AUDGTI_CTRL, 0xe86);
*sw = val & 0x3;
return 0;
}
static int ac_i2swidth_adc_set(int sw)
{
uint32_t val = gti_read((void __iomem *)CR_TOP_AUDGTI_CTRL, 0xe86);
val &= ~0x3; /* Clear RX width field */
val |= sw;
gti_write((void __iomem *)CR_TOP_AUDGTI_CTRL, 0xe86, val);
return 0;
}
static int ac_bypass_adc_get(bool *bypass)
{
uint32_t val = readl(CR_AUDIO_HP_CTRL);
*bypass = !(val & AUDIO_I2S_EXT);
return 0;
}
static int ac_bypass_adc_set(bool bypass)
{
uint32_t val = readl(CR_AUDIO_HP_CTRL);
val &= ~AUDIO_I2S_EXT;
if (!bypass)
val |= AUDIO_I2S_EXT;
writel(val, CR_AUDIO_HP_CTRL);
return 0;
}
static int ac_input_route_get(int *route)
{
uint32_t val = readl(CR_AUDIO_HP_CTRL);
*route = (val & AUDIO_PGA_MODE_MASK) >> AUDIO_PGA_MODE_SHIFT;
return 0;
}
static int ac_input_route_set(int route)
{
uint32_t val = readl(CR_AUDIO_HP_CTRL);
val &= ~AUDIO_PGA_MODE_MASK;
val |= AUDIO_PGA_MODE(route);
writel(val, CR_AUDIO_HP_CTRL);
return 0;
}
static int ac_chan_i2sinput_get(int chan, int *in)
{
uint32_t val = gti_read((void __iomem *)CR_TOP_AUDGTI_CTRL,
pwms1_regs[chan]);
*in = (val & 0x3) - 1;
return 0;
}
static int ac_chan_i2sinput_set(int chan, int in)
{
uint32_t val = gti_read((void __iomem *)CR_TOP_AUDGTI_CTRL,
pwms1_regs[chan]);
val &= ~0x3; /* Clear routing */
val |= (in + 1) & 0x3;
gti_write((void __iomem *)CR_TOP_AUDGTI_CTRL, pwms1_regs[chan], val);
return 0;
}
static int ac_chan_i2swidth_get(int chan, int *sw)
{
uint32_t val = gti_read((void __iomem *)CR_TOP_AUDGTI_CTRL,
pwms1_regs[chan]);
*sw = (val >> 6) & 0x3;
return 0;
}
static int ac_chan_i2swidth_set(int chan, int sw)
{
uint32_t val = gti_read((void __iomem *)CR_TOP_AUDGTI_CTRL,
pwms1_regs[chan]);
val &= ~0xc; /* Clear width */
val |= sw << 6;
gti_write((void __iomem *)CR_TOP_AUDGTI_CTRL, pwms1_regs[chan], val);
return 0;
}
static int ac_chan_vol_get(int pair, int lr, uint8_t *db)
{
uint32_t reg_addr, value, shift = 32;
if (lr & AUDIOCODEC_LEFT)
shift = ((pair % 2) * 16);
else if (lr & AUDIOCODEC_RIGHT)
shift = (((pair % 2) * 16) + 8);
/* Each 32 bit GAIN register represents 2 channel pairs */
reg_addr = CR_AUDIO_GAIN0 + ((pair / 2) * sizeof(uint32_t));
value = readl(reg_addr);
*db = (value >> shift) & 0xFF;
return 0;
}
static int ac_chan_vol_set(int pair, int lr, uint8_t db)
{
uint32_t reg_addr, value, clear_mask = 0, set_mask = 0;
if (lr & AUDIOCODEC_LEFT) {
clear_mask += 0xFF << ((pair % 2) * 16);
set_mask += db << ((pair % 2) * 16);
}
if (lr & AUDIOCODEC_RIGHT) {
clear_mask += 0xFF << (((pair % 2) * 16) + 8);
set_mask += db << (((pair % 2) * 16) + 8);
}
/* Each 32 bit GAIN register represents 2 channel pairs */
reg_addr = CR_AUDIO_GAIN0 + ((pair / 2) * sizeof(uint32_t));
value = readl(reg_addr);
value &= ~clear_mask;
value |= set_mask;
writel(value, reg_addr);
return 0;
}
static int ac_chan_mute_get(int pair, int lr, int *mute)
{
uint32_t val = readl(CR_AUDIO_MUTE);
*mute = AUDIOCODEC_MUTE_NONE;
if (val & (lr << (16 + (pair * 2))))
*mute |= AUDIOCODEC_MUTE_HARD;
if (val & (lr << (8 + (pair * 2))))
*mute |= AUDIOCODEC_MUTE_90DB;
if (val & (lr << (0 + (pair * 2))))
*mute |= AUDIOCODEC_MUTE_SQUAREWAVE;
return 0;
}
static int ac_chan_mute_set(int pair, int lr, int mute)
{
uint32_t val = readl(CR_AUDIO_MUTE);
val &= ~(lr << (16 + (pair * 2)));
val &= ~(lr << (8 + (pair * 2)));
val &= ~(lr << (0 + (pair * 2)));
if (mute & AUDIOCODEC_MUTE_HARD)
val |= (lr << (16 + (pair * 2)));
if (mute & AUDIOCODEC_MUTE_90DB)
val |= (lr << (8 + (pair * 2)));
if (mute & AUDIOCODEC_MUTE_SQUAREWAVE)
val |= (lr << (0 + (pair * 2)));
writel(val, CR_AUDIO_MUTE);
return 0;
}
static int curr_preset = AUDIOCODEC_PRESET_NONE;
static ssize_t ac_preset_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return map_string_flags(map_preset, curr_preset, buf, PAGE_SIZE);
}
static ssize_t ac_preset_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int preset = map_lookup_flags(map_preset, buf);
if (preset == 0)
return -EINVAL;
curr_preset = preset;
ac_reset();
#ifdef CONFIG_TZ1090_01XX_HDMI_AUDIO
zero1sp_hdmi_audio_set_enabled(!!(preset & AUDIOCODEC_PRESET_HDMI));
#endif
ac_dac_power_set(!!(preset & AUDIOCODEC_PRESET_PHONO));
ac_chan_vol_set(0, AUDIOCODEC_LEFT | AUDIOCODEC_RIGHT, 128);
ac_chan_mute_set(0, AUDIOCODEC_LEFT | AUDIOCODEC_RIGHT,
AUDIOCODEC_MUTE_NONE);
ac_i2s_out_clock_set(AUDIOCODEC_I2SCLOCK_XTAL1);
ac_i2s_out_clockdiv_set(0);
ac_i2s_out_clockdiv2_set(1);
ac_i2s_out_active_channels_set(1);
ac_i2s_out_master_set(true);
ac_i2s_out_frame_set(AUDIOCODEC_FRAME_3232);
ac_i2s_out_aclk_set(AUDIOCODEC_CLOCK_256FS);
ac_i2s_out_blrclk_set(true);
ac_i2s_out_enable_set(true);
ac_i2swidth_dac_set(AUDIOCODEC_SAMPLEWIDTH_24);
ac_i2swidth_adc_set(AUDIOCODEC_SAMPLEWIDTH_24);
ac_chan_i2sinput_set(0, 1);
ac_chan_i2swidth_set(0, AUDIOCODEC_SAMPLEWIDTH_24);
ac_chan_i2sinput_set(1, 0);
ac_chan_i2swidth_set(1, AUDIOCODEC_SAMPLEWIDTH_24);
ac_chan_i2sinput_set(2, 2);
ac_chan_i2swidth_set(2, AUDIOCODEC_SAMPLEWIDTH_24);
ac_bypass_adc_set(false);
ac_input_route_set(AUDIOCODEC_INPUT_LINE);
return count;
}
static ssize_t ac_dac_power_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
bool pwr;
if (ac_dac_power_get(&pwr))
return -EINVAL;
return map_string(map_bool, pwr, buf, PAGE_SIZE);
}
static ssize_t ac_dac_power_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int pwr = map_lookup(map_bool, buf);
if (pwr == -1)
return -EINVAL;
if (ac_dac_power_set(!!pwr))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_out_active_channels_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int chans;
if (ac_i2s_out_active_channels_get(&chans))
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%d\n", chans);
}
static ssize_t ac_i2s_out_active_channels_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int chans;
if (sscanf(buf, "%d", &chans) != 1)
return -EINVAL;
if (ac_i2s_out_active_channels_set(chans))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_out_clock_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int clksrc;
if (ac_i2s_out_clock_get(&clksrc))
return -EINVAL;
return map_string(map_i2sclock, clksrc, buf, PAGE_SIZE);
}
static ssize_t ac_i2s_out_clock_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int clksrc = map_lookup(map_i2sclock, buf);
if (clksrc == -1)
return -EINVAL;
if (ac_i2s_out_clock_set(clksrc))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_out_clockdiv_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int chans;
if (ac_i2s_out_clockdiv_get(&chans))
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%d\n", chans);
}
static ssize_t ac_i2s_out_clockdiv_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int chans;
if (sscanf(buf, "%d", &chans) != 1)
return -EINVAL;
if (ac_i2s_out_clockdiv_set(chans))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_out_clockdiv2_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int chans;
if (ac_i2s_out_clockdiv2_get(&chans))
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%d\n", chans);
}
static ssize_t ac_i2s_out_clockdiv2_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int chans;
if (sscanf(buf, "%d", &chans) != 1)
return -EINVAL;
if (ac_i2s_out_clockdiv2_set(chans))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_out_frame_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int frame;
if (ac_i2s_out_frame_get(&frame))
return -EINVAL;
return map_string(map_frame, frame, buf, PAGE_SIZE);
}
static ssize_t ac_i2s_out_frame_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int frame = map_lookup(map_frame, buf);
if (frame == -1)
return -EINVAL;
if (ac_i2s_out_frame_set(frame))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_out_master_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
bool master;
if (ac_i2s_out_master_get(&master))
return -EINVAL;
return map_string(map_bool, master, buf, PAGE_SIZE);
}
static ssize_t ac_i2s_out_master_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int master = map_lookup(map_bool, buf);
if (master == -1)
return -EINVAL;
if (ac_i2s_out_master_set(!!master))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_out_aclk_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int aclk;
if (ac_i2s_out_aclk_get(&aclk))
return -EINVAL;
return map_string(map_clock, aclk, buf, PAGE_SIZE);
}
static ssize_t ac_i2s_out_aclk_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int aclk = map_lookup(map_clock, buf);
if (aclk == -1)
return -EINVAL;
if (ac_i2s_out_aclk_set(aclk))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_out_blrclk_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
bool blrclk;
if (ac_i2s_out_blrclk_get(&blrclk))
return -EINVAL;
return map_string(map_bool, blrclk, buf, PAGE_SIZE);
}
static ssize_t ac_i2s_out_blrclk_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int blrclk = map_lookup(map_bool, buf);
if (blrclk == -1)
return -EINVAL;
if (ac_i2s_out_blrclk_set(!!blrclk))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_out_enable_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
bool enable;
if (ac_i2s_out_enable_get(&enable))
return -EINVAL;
return map_string(map_bool, enable, buf, PAGE_SIZE);
}
static ssize_t ac_i2s_out_enable_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int enable = map_lookup(map_bool, buf);
if (enable == -1)
return -EINVAL;
if (ac_i2s_out_enable_set(!!enable))
return -EINVAL;
return count;
}
static ssize_t ac_bypass_adc_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
bool bypass;
if (ac_bypass_adc_get(&bypass))
return -EINVAL;
return map_string(map_bool, bypass, buf, PAGE_SIZE);
}
static ssize_t ac_bypass_adc_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int bypass = map_lookup(map_bool, buf);
if (bypass == -1)
return -EINVAL;
if (ac_bypass_adc_set(!!bypass))
return -EINVAL;
return count;
}
static ssize_t ac_input_route_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int in;
if (ac_input_route_get(&in))
return -EINVAL;
return map_string(map_input, in, buf, PAGE_SIZE);
}
static ssize_t ac_input_route_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int in = map_lookup(map_input, buf);
if (in == -1)
return -EINVAL;
if (ac_input_route_set(in))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_samplewidth_dac_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int sw;
if (ac_i2swidth_dac_get(&sw))
return -EINVAL;
return map_string(map_samplewidth, sw, buf, PAGE_SIZE);
}
static ssize_t ac_i2s_samplewidth_dac_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int sw = map_lookup(map_samplewidth, buf);
if (sw == -1)
return -EINVAL;
if (ac_i2swidth_dac_set(sw))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_samplewidth_adc_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int sw;
if (ac_i2swidth_adc_get(&sw))
return -EINVAL;
return map_string(map_samplewidth, sw, buf, PAGE_SIZE);
}
static ssize_t ac_i2s_samplewidth_adc_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int sw = map_lookup(map_samplewidth, buf);
if (sw == -1)
return -EINVAL;
if (ac_i2swidth_adc_set(sw))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_input_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int pair, in;
if (sscanf(attr->attr.name, "i2s_input_%d", &pair) != 1)
return -EINVAL;
if (ac_chan_i2sinput_get(pair, &in))
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%d\n", in);
}
static ssize_t ac_i2s_input_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int pair, in;
if (sscanf(attr->attr.name, "i2s_input_%d", &pair) != 1)
return -EINVAL;
if (sscanf(buf, "%d", &in) != 1)
return -EINVAL;
if (ac_chan_i2sinput_set(pair, in))
return -EINVAL;
return count;
}
static ssize_t ac_i2s_samplewidth_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int pair, sw;
if (sscanf(attr->attr.name, "i2s_samplewidth_%d", &pair) != 1)
return -EINVAL;
if (ac_chan_i2swidth_get(pair, &sw))
return -EINVAL;
return map_string(map_samplewidth, sw, buf, PAGE_SIZE);
}
static ssize_t ac_i2s_samplewidth_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int pair, sw;
if (sscanf(attr->attr.name, "i2s_samplewidth_%d", &pair) != 1)
return -EINVAL;
sw = map_lookup(map_samplewidth, buf);
if (sw == -1)
return -EINVAL;
if (ac_chan_i2swidth_set(pair, sw))
return -EINVAL;
return count;
}
static int ac_lr_char(char lrchar)
{
switch (lrchar) {
case 'l': return AUDIOCODEC_LEFT;
case 'r': return AUDIOCODEC_RIGHT;
case 'b': return AUDIOCODEC_LEFT | AUDIOCODEC_RIGHT;
default: return -EINVAL;
}
}
static ssize_t ac_vol_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int pair, lr;
uint8_t vol;
char lrchar;
if (sscanf(attr->attr.name, "vol_%d%c", &pair, &lrchar) != 2)
return -EINVAL;
lr = ac_lr_char(lrchar);
if (ac_chan_vol_get(pair, lr, &vol))
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%d\n", (int)vol);
}
static ssize_t ac_vol_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int pair, lr, vol;
char lrchar;
if (sscanf(attr->attr.name, "vol_%d%c", &pair, &lrchar) != 2)
return -EINVAL;
if (sscanf(buf, "%d", &vol) != 1)
return -EINVAL;
lr = ac_lr_char(lrchar);
if (ac_chan_vol_set(pair, lr, (uint8_t)vol))
return -EINVAL;
return count;
}
static ssize_t ac_mute_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int pair, lr, mute;
char lrchar;
if (sscanf(attr->attr.name, "mute_%d%c", &pair, &lrchar) != 2)
return -EINVAL;
lr = ac_lr_char(lrchar);
if (ac_chan_mute_get(pair, lr, &mute))
return -EINVAL;
return map_string_flags(map_mute, mute, buf, PAGE_SIZE);
}
static ssize_t ac_mute_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int pair, lr, mute;
char lrchar;
if (sscanf(attr->attr.name, "mute_%d%c", &pair, &lrchar) != 2)
return -EINVAL;
lr = ac_lr_char(lrchar);
mute = map_lookup_flags(map_mute, buf);
if (ac_chan_mute_set(pair, lr, mute))
return -EINVAL;
return count;
}
#ifdef CONFIG_TZ1090_01XX_HDMI_AUDIO
static ssize_t ac_hdmi_audio_enable_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
bool en = zero1sp_hdmi_audio_get_enabled();
return map_string(map_bool, en, buf, PAGE_SIZE);
}
static ssize_t ac_hdmi_audio_enable_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int en = map_lookup(map_bool, buf);
if (en == -1)
return -EINVAL;
zero1sp_hdmi_audio_set_enabled(!!en);
return count;
}
#endif
#define AC_ATTR(name, fnname) \
__ATTR(name, 0666, ac_##fnname##_show, ac_##fnname##_store)
#define AC_ATTR_PERPAIR(name, fnname) \
AC_ATTR(name##_0, fnname), \
AC_ATTR(name##_1, fnname), \
AC_ATTR(name##_2, fnname)
#define AC_ATTR_PERCHAN(name, fnname) \
AC_ATTR(name##_0l, fnname), \
AC_ATTR(name##_0r, fnname), \
AC_ATTR(name##_1l, fnname), \
AC_ATTR(name##_1r, fnname), \
AC_ATTR(name##_2l, fnname), \
AC_ATTR(name##_2r, fnname)
static struct kobj_attribute ac_kobj_attributes[] = {
AC_ATTR(preset, preset),
AC_ATTR(dac_power, dac_power),
AC_ATTR(bypass_adc, bypass_adc),
AC_ATTR(input_route, input_route),
AC_ATTR(i2s_out_clock, i2s_out_clock),
AC_ATTR(i2s_out_clockdiv, i2s_out_clockdiv),
AC_ATTR(i2s_out_clockdiv2, i2s_out_clockdiv2),
AC_ATTR(i2s_out_active_channels, i2s_out_active_channels),
AC_ATTR(i2s_out_frame, i2s_out_frame),
AC_ATTR(i2s_out_master, i2s_out_master),
AC_ATTR(i2s_out_aclk, i2s_out_aclk),
AC_ATTR(i2s_out_blrclk, i2s_out_blrclk),
AC_ATTR(i2s_out_enable, i2s_out_enable),
AC_ATTR(i2s_samplewidth_dac, i2s_samplewidth_dac),
AC_ATTR(i2s_samplewidth_adc, i2s_samplewidth_adc),
AC_ATTR_PERPAIR(i2s_input, i2s_input),
AC_ATTR_PERPAIR(i2s_samplewidth, i2s_samplewidth),
AC_ATTR_PERCHAN(vol, vol),
AC_ATTR_PERCHAN(mute, mute),
#ifdef CONFIG_TZ1090_01XX_HDMI_AUDIO
AC_ATTR(hdmi_audio_enable, hdmi_audio_enable),
#endif
};
static struct attribute *ac_attributes[ARRAY_SIZE(ac_kobj_attributes)+1];
static struct attribute_group ac_attr_group = {
.attrs = ac_attributes,
};
static int __init audiocodec_init(void)
{
int i, ret;
ac_kobj = kobject_create_and_add("audiocodec", kernel_kobj);
if (!ac_kobj) {
ret = -ENOMEM;
goto err0;
}
for (i = 0; i < ARRAY_SIZE(ac_kobj_attributes); i++)
ac_attributes[i] = &ac_kobj_attributes[i].attr;
ac_attributes[i] = NULL;
ret = sysfs_create_group(ac_kobj, &ac_attr_group);
if (ret)
goto err1;
return 0;
err1:
kobject_put(ac_kobj);
err0:
return ret;
}
static void audiocodec_exit(void)
{
kobject_put(ac_kobj);
}
module_init(audiocodec_init);
module_exit(audiocodec_exit);