Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
kernel_linux
提交
0ca0c2d8
K
kernel_linux
项目概览
OpenHarmony
/
kernel_linux
上一次同步 4 年多
通知
15
Star
8
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
kernel_linux
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
0ca0c2d8
编写于
7月 16, 2014
作者:
M
Mark Brown
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'asoc/topic/rt286' into HEAD
上级
249adddb
23c4fd5c
变更
5
显示空白变更内容
内联
并排
Showing
5 changed file
with
1447 addition
and
0 deletion
+1447
-0
include/sound/rt286.h
include/sound/rt286.h
+19
-0
sound/soc/codecs/Kconfig
sound/soc/codecs/Kconfig
+4
-0
sound/soc/codecs/Makefile
sound/soc/codecs/Makefile
+2
-0
sound/soc/codecs/rt286.c
sound/soc/codecs/rt286.c
+1224
-0
sound/soc/codecs/rt286.h
sound/soc/codecs/rt286.h
+198
-0
未找到文件。
include/sound/rt286.h
0 → 100644
浏览文件 @
0ca0c2d8
/*
* linux/sound/rt286.h -- Platform data for RT286
*
* Copyright 2013 Realtek Microelectronics
*
* 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 __LINUX_SND_RT286_H
#define __LINUX_SND_RT286_H
struct
rt286_platform_data
{
bool
cbj_en
;
/*combo jack enable*/
bool
gpio2_en
;
/*GPIO2 enable*/
};
#endif
sound/soc/codecs/Kconfig
浏览文件 @
0ca0c2d8
...
...
@@ -74,6 +74,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM3008
select SND_SOC_PCM512x_I2C if I2C
select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
select SND_SOC_RT5645 if I2C
...
...
@@ -449,6 +450,9 @@ config SND_SOC_RL6231
default m if SND_SOC_RT5645=m
default m if SND_SOC_RT5651=m
config SND_SOC_RT286
tristate
config SND_SOC_RT5631
tristate
...
...
sound/soc/codecs/Makefile
浏览文件 @
0ca0c2d8
...
...
@@ -68,6 +68,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-rt286-objs
:=
rt286.o
snd-soc-rt5631-objs
:=
rt5631.o
snd-soc-rt5640-objs
:=
rt5640.o
snd-soc-rt5645-objs
:=
rt5645.o
...
...
@@ -233,6 +234,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_RT286)
+=
snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT5631)
+=
snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640)
+=
snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_RT5645)
+=
snd-soc-rt5645.o
...
...
sound/soc/codecs/rt286.c
0 → 100644
浏览文件 @
0ca0c2d8
/*
* rt286.c -- RT286 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@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/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/rt286.h>
#include <sound/hda_verbs.h>
#include "rt286.h"
#define RT286_VENDOR_ID 0x10ec0286
struct
rt286_priv
{
struct
regmap
*
regmap
;
struct
snd_soc_codec
*
codec
;
struct
rt286_platform_data
pdata
;
struct
i2c_client
*
i2c
;
struct
snd_soc_jack
*
jack
;
struct
delayed_work
jack_detect_work
;
int
sys_clk
;
struct
reg_default
*
index_cache
;
};
static
struct
reg_default
rt286_index_def
[]
=
{
{
0x01
,
0xaaaa
},
{
0x02
,
0x8aaa
},
{
0x03
,
0x0002
},
{
0x04
,
0xaf01
},
{
0x08
,
0x000d
},
{
0x09
,
0xd810
},
{
0x0a
,
0x0060
},
{
0x0b
,
0x0000
},
{
0x0d
,
0x2800
},
{
0x0f
,
0x0000
},
{
0x19
,
0x0a17
},
{
0x20
,
0x0020
},
{
0x33
,
0x0208
},
{
0x49
,
0x0004
},
{
0x4f
,
0x50e9
},
{
0x50
,
0x2c00
},
{
0x63
,
0x2902
},
{
0x67
,
0x1111
},
{
0x68
,
0x1016
},
{
0x69
,
0x273f
},
};
#define INDEX_CACHE_SIZE ARRAY_SIZE(rt286_index_def)
static
const
struct
reg_default
rt286_reg
[]
=
{
{
0x00170500
,
0x00000400
},
{
0x00220000
,
0x00000031
},
{
0x00239000
,
0x0000007f
},
{
0x0023a000
,
0x0000007f
},
{
0x00270500
,
0x00000400
},
{
0x00370500
,
0x00000400
},
{
0x00870500
,
0x00000400
},
{
0x00920000
,
0x00000031
},
{
0x00935000
,
0x000000c3
},
{
0x00936000
,
0x000000c3
},
{
0x00970500
,
0x00000400
},
{
0x00b37000
,
0x00000097
},
{
0x00b37200
,
0x00000097
},
{
0x00b37300
,
0x00000097
},
{
0x00c37000
,
0x00000000
},
{
0x00c37100
,
0x00000080
},
{
0x01270500
,
0x00000400
},
{
0x01370500
,
0x00000400
},
{
0x01371f00
,
0x411111f0
},
{
0x01439000
,
0x00000080
},
{
0x0143a000
,
0x00000080
},
{
0x01470700
,
0x00000000
},
{
0x01470500
,
0x00000400
},
{
0x01470c00
,
0x00000000
},
{
0x01470100
,
0x00000000
},
{
0x01837000
,
0x00000000
},
{
0x01870500
,
0x00000400
},
{
0x02050000
,
0x00000000
},
{
0x02139000
,
0x00000080
},
{
0x0213a000
,
0x00000080
},
{
0x02170100
,
0x00000000
},
{
0x02170500
,
0x00000400
},
{
0x02170700
,
0x00000000
},
{
0x02270100
,
0x00000000
},
{
0x02370100
,
0x00000000
},
{
0x02040000
,
0x00004002
},
{
0x01870700
,
0x00000020
},
{
0x00830000
,
0x000000c3
},
{
0x00930000
,
0x000000c3
},
{
0x01270700
,
0x00000000
},
};
static
bool
rt286_volatile_register
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
0
...
0xff
:
case
RT286_GET_PARAM
(
AC_NODE_ROOT
,
AC_PAR_VENDOR_ID
):
case
RT286_GET_HP_SENSE
:
case
RT286_GET_MIC1_SENSE
:
case
RT286_PROC_COEF
:
return
true
;
default:
return
false
;
}
}
static
bool
rt286_readable_register
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
0
...
0xff
:
case
RT286_GET_PARAM
(
AC_NODE_ROOT
,
AC_PAR_VENDOR_ID
):
case
RT286_GET_HP_SENSE
:
case
RT286_GET_MIC1_SENSE
:
case
RT286_SET_AUDIO_POWER
:
case
RT286_SET_HPO_POWER
:
case
RT286_SET_SPK_POWER
:
case
RT286_SET_DMIC1_POWER
:
case
RT286_SPK_MUX
:
case
RT286_HPO_MUX
:
case
RT286_ADC0_MUX
:
case
RT286_ADC1_MUX
:
case
RT286_SET_MIC1
:
case
RT286_SET_PIN_HPO
:
case
RT286_SET_PIN_SPK
:
case
RT286_SET_PIN_DMIC1
:
case
RT286_SPK_EAPD
:
case
RT286_SET_AMP_GAIN_HPO
:
case
RT286_SET_DMIC2_DEFAULT
:
case
RT286_DACL_GAIN
:
case
RT286_DACR_GAIN
:
case
RT286_ADCL_GAIN
:
case
RT286_ADCR_GAIN
:
case
RT286_MIC_GAIN
:
case
RT286_SPOL_GAIN
:
case
RT286_SPOR_GAIN
:
case
RT286_HPOL_GAIN
:
case
RT286_HPOR_GAIN
:
case
RT286_F_DAC_SWITCH
:
case
RT286_F_RECMIX_SWITCH
:
case
RT286_REC_MIC_SWITCH
:
case
RT286_REC_I2S_SWITCH
:
case
RT286_REC_LINE_SWITCH
:
case
RT286_REC_BEEP_SWITCH
:
case
RT286_DAC_FORMAT
:
case
RT286_ADC_FORMAT
:
case
RT286_COEF_INDEX
:
case
RT286_PROC_COEF
:
case
RT286_SET_AMP_GAIN_ADC_IN1
:
case
RT286_SET_AMP_GAIN_ADC_IN2
:
case
RT286_SET_POWER
(
RT286_DAC_OUT1
):
case
RT286_SET_POWER
(
RT286_DAC_OUT2
):
case
RT286_SET_POWER
(
RT286_ADC_IN1
):
case
RT286_SET_POWER
(
RT286_ADC_IN2
):
case
RT286_SET_POWER
(
RT286_DMIC2
):
case
RT286_SET_POWER
(
RT286_MIC1
):
return
true
;
default:
return
false
;
}
}
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
);
reg
=
RT286_PROC_COEF
;
for
(
i
=
0
;
i
<
INDEX_CACHE_SIZE
;
i
++
)
{
if
(
reg
==
rt286
->
index_cache
[
i
].
reg
)
{
rt286
->
index_cache
[
i
].
def
=
value
;
break
;
}
}
}
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
;
}
static
void
rt286_index_sync
(
struct
snd_soc_codec
*
codec
)
{
struct
rt286_priv
*
rt286
=
snd_soc_codec_get_drvdata
(
codec
);
int
i
;
for
(
i
=
0
;
i
<
INDEX_CACHE_SIZE
;
i
++
)
{
snd_soc_write
(
codec
,
rt286
->
index_cache
[
i
].
reg
,
rt286
->
index_cache
[
i
].
def
);
}
}
static
int
rt286_support_power_controls
[]
=
{
RT286_DAC_OUT1
,
RT286_DAC_OUT2
,
RT286_ADC_IN1
,
RT286_ADC_IN2
,
RT286_MIC1
,
RT286_DMIC1
,
RT286_DMIC2
,
RT286_SPK_OUT
,
RT286_HP_OUT
,
};
#define RT286_POWER_REG_LEN ARRAY_SIZE(rt286_support_power_controls)
static
int
rt286_jack_detect
(
struct
snd_soc_codec
*
codec
,
bool
*
hp
,
bool
*
mic
)
{
struct
rt286_priv
*
rt286
=
snd_soc_codec_get_drvdata
(
codec
);
unsigned
int
val
,
buf
;
int
i
;
*
hp
=
false
;
*
mic
=
false
;
if
(
rt286
->
pdata
.
cbj_en
)
{
buf
=
snd_soc_read
(
codec
,
RT286_GET_HP_SENSE
);
*
hp
=
buf
&
0x80000000
;
if
(
*
hp
)
{
/* power on HV,VERF */
snd_soc_update_bits
(
codec
,
RT286_POWER_CTRL1
,
0x1001
,
0x0
);
/* power LDO1 */
snd_soc_update_bits
(
codec
,
RT286_POWER_CTRL2
,
0x4
,
0x4
);
snd_soc_write
(
codec
,
RT286_SET_MIC1
,
0x24
);
val
=
snd_soc_read
(
codec
,
RT286_CBJ_CTRL2
);
msleep
(
200
);
i
=
40
;
while
(((
val
&
0x0800
)
==
0
)
&&
(
i
>
0
))
{
val
=
snd_soc_read
(
codec
,
RT286_CBJ_CTRL2
);
i
--
;
msleep
(
20
);
}
if
(
0x0400
==
(
val
&
0x0700
))
{
*
mic
=
false
;
snd_soc_write
(
codec
,
RT286_SET_MIC1
,
0x20
);
/* power off HV,VERF */
snd_soc_update_bits
(
codec
,
RT286_POWER_CTRL1
,
0x1001
,
0x1001
);
snd_soc_update_bits
(
codec
,
RT286_A_BIAS_CTRL3
,
0xc000
,
0x0000
);
snd_soc_update_bits
(
codec
,
RT286_CBJ_CTRL1
,
0x0030
,
0x0000
);
snd_soc_update_bits
(
codec
,
RT286_A_BIAS_CTRL2
,
0xc000
,
0x0000
);
}
else
if
((
0x0200
==
(
val
&
0x0700
))
||
(
0x0100
==
(
val
&
0x0700
)))
{
*
mic
=
true
;
snd_soc_update_bits
(
codec
,
RT286_A_BIAS_CTRL3
,
0xc000
,
0x8000
);
snd_soc_update_bits
(
codec
,
RT286_CBJ_CTRL1
,
0x0030
,
0x0020
);
snd_soc_update_bits
(
codec
,
RT286_A_BIAS_CTRL2
,
0xc000
,
0x8000
);
}
else
{
*
mic
=
false
;
}
snd_soc_update_bits
(
codec
,
RT286_MISC_CTRL1
,
0x0060
,
0x0000
);
}
else
{
snd_soc_update_bits
(
codec
,
RT286_MISC_CTRL1
,
0x0060
,
0x0020
);
snd_soc_update_bits
(
codec
,
RT286_A_BIAS_CTRL3
,
0xc000
,
0x8000
);
snd_soc_update_bits
(
codec
,
RT286_CBJ_CTRL1
,
0x0030
,
0x0020
);
snd_soc_update_bits
(
codec
,
RT286_A_BIAS_CTRL2
,
0xc000
,
0x8000
);
*
mic
=
false
;
}
}
else
{
buf
=
snd_soc_read
(
codec
,
RT286_GET_HP_SENSE
);
*
hp
=
buf
&
0x80000000
;
buf
=
snd_soc_read
(
codec
,
RT286_GET_MIC1_SENSE
);
*
mic
=
buf
&
0x80000000
;
}
return
0
;
}
static
void
rt286_jack_detect_work
(
struct
work_struct
*
work
)
{
struct
rt286_priv
*
rt286
=
container_of
(
work
,
struct
rt286_priv
,
jack_detect_work
.
work
);
int
status
=
0
;
bool
hp
=
false
;
bool
mic
=
false
;
rt286_jack_detect
(
rt286
->
codec
,
&
hp
,
&
mic
);
if
(
hp
==
true
)
status
|=
SND_JACK_HEADPHONE
;
if
(
mic
==
true
)
status
|=
SND_JACK_MICROPHONE
;
snd_soc_jack_report
(
rt286
->
jack
,
status
,
SND_JACK_MICROPHONE
|
SND_JACK_HEADPHONE
);
}
int
rt286_mic_detect
(
struct
snd_soc_codec
*
codec
,
struct
snd_soc_jack
*
jack
)
{
struct
rt286_priv
*
rt286
=
snd_soc_codec_get_drvdata
(
codec
);
rt286
->
jack
=
jack
;
/* Send an initial empty report */
snd_soc_jack_report
(
rt286
->
jack
,
0
,
SND_JACK_MICROPHONE
|
SND_JACK_HEADPHONE
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
rt286_mic_detect
);
static
const
DECLARE_TLV_DB_SCALE
(
out_vol_tlv
,
-
6350
,
50
,
0
);
static
const
DECLARE_TLV_DB_SCALE
(
mic_vol_tlv
,
0
,
1000
,
0
);
static
const
struct
snd_kcontrol_new
rt286_snd_controls
[]
=
{
SOC_DOUBLE_R_TLV
(
"DAC0 Playback Volume"
,
RT286_DACL_GAIN
,
RT286_DACR_GAIN
,
0
,
0x7f
,
0
,
out_vol_tlv
),
SOC_DOUBLE_R_TLV
(
"ADC0 Capture Volume"
,
RT286_ADCL_GAIN
,
RT286_ADCR_GAIN
,
0
,
0x7f
,
0
,
out_vol_tlv
),
SOC_SINGLE_TLV
(
"AMIC Volume"
,
RT286_MIC_GAIN
,
0
,
0x3
,
0
,
mic_vol_tlv
),
SOC_DOUBLE_R
(
"Speaker Playback Switch"
,
RT286_SPOL_GAIN
,
RT286_SPOR_GAIN
,
RT286_MUTE_SFT
,
1
,
1
),
};
/* Digital Mixer */
static
const
struct
snd_kcontrol_new
rt286_front_mix
[]
=
{
SOC_DAPM_SINGLE
(
"DAC Switch"
,
RT286_F_DAC_SWITCH
,
RT286_MUTE_SFT
,
1
,
1
),
SOC_DAPM_SINGLE
(
"RECMIX Switch"
,
RT286_F_RECMIX_SWITCH
,
RT286_MUTE_SFT
,
1
,
1
),
};
/* Analog Input Mixer */
static
const
struct
snd_kcontrol_new
rt286_rec_mix
[]
=
{
SOC_DAPM_SINGLE
(
"Mic1 Switch"
,
RT286_REC_MIC_SWITCH
,
RT286_MUTE_SFT
,
1
,
1
),
SOC_DAPM_SINGLE
(
"I2S Switch"
,
RT286_REC_I2S_SWITCH
,
RT286_MUTE_SFT
,
1
,
1
),
SOC_DAPM_SINGLE
(
"Line1 Switch"
,
RT286_REC_LINE_SWITCH
,
RT286_MUTE_SFT
,
1
,
1
),
SOC_DAPM_SINGLE
(
"Beep Switch"
,
RT286_REC_BEEP_SWITCH
,
RT286_MUTE_SFT
,
1
,
1
),
};
static
const
struct
snd_kcontrol_new
spo_enable_control
=
SOC_DAPM_SINGLE
(
"Switch"
,
RT286_SET_PIN_SPK
,
RT286_SET_PIN_SFT
,
1
,
0
);
static
const
struct
snd_kcontrol_new
hpol_enable_control
=
SOC_DAPM_SINGLE_AUTODISABLE
(
"Switch"
,
RT286_HPOL_GAIN
,
RT286_MUTE_SFT
,
1
,
1
);
static
const
struct
snd_kcontrol_new
hpor_enable_control
=
SOC_DAPM_SINGLE_AUTODISABLE
(
"Switch"
,
RT286_HPOR_GAIN
,
RT286_MUTE_SFT
,
1
,
1
);
/* ADC0 source */
static
const
char
*
const
rt286_adc_src
[]
=
{
"Mic"
,
"RECMIX"
,
"Dmic"
};
static
const
int
rt286_adc_values
[]
=
{
0
,
4
,
5
,
};
static
SOC_VALUE_ENUM_SINGLE_DECL
(
rt286_adc0_enum
,
RT286_ADC0_MUX
,
RT286_ADC_SEL_SFT
,
RT286_ADC_SEL_MASK
,
rt286_adc_src
,
rt286_adc_values
);
static
const
struct
snd_kcontrol_new
rt286_adc0_mux
=
SOC_DAPM_ENUM
(
"ADC 0 source"
,
rt286_adc0_enum
);
static
SOC_VALUE_ENUM_SINGLE_DECL
(
rt286_adc1_enum
,
RT286_ADC1_MUX
,
RT286_ADC_SEL_SFT
,
RT286_ADC_SEL_MASK
,
rt286_adc_src
,
rt286_adc_values
);
static
const
struct
snd_kcontrol_new
rt286_adc1_mux
=
SOC_DAPM_ENUM
(
"ADC 1 source"
,
rt286_adc1_enum
);
static
const
char
*
const
rt286_dac_src
[]
=
{
"Front"
,
"Surround"
};
/* HP-OUT source */
static
SOC_ENUM_SINGLE_DECL
(
rt286_hpo_enum
,
RT286_HPO_MUX
,
0
,
rt286_dac_src
);
static
const
struct
snd_kcontrol_new
rt286_hpo_mux
=
SOC_DAPM_ENUM
(
"HPO source"
,
rt286_hpo_enum
);
/* SPK-OUT source */
static
SOC_ENUM_SINGLE_DECL
(
rt286_spo_enum
,
RT286_SPK_MUX
,
0
,
rt286_dac_src
);
static
const
struct
snd_kcontrol_new
rt286_spo_mux
=
SOC_DAPM_ENUM
(
"SPO source"
,
rt286_spo_enum
);
static
int
rt286_spk_event
(
struct
snd_soc_dapm_widget
*
w
,
struct
snd_kcontrol
*
kcontrol
,
int
event
)
{
struct
snd_soc_codec
*
codec
=
w
->
codec
;
switch
(
event
)
{
case
SND_SOC_DAPM_POST_PMU
:
snd_soc_write
(
codec
,
RT286_SPK_EAPD
,
RT286_SET_EAPD_HIGH
);
break
;
case
SND_SOC_DAPM_PRE_PMD
:
snd_soc_write
(
codec
,
RT286_SPK_EAPD
,
RT286_SET_EAPD_LOW
);
break
;
default:
return
0
;
}
return
0
;
}
static
int
rt286_set_dmic1_event
(
struct
snd_soc_dapm_widget
*
w
,
struct
snd_kcontrol
*
kcontrol
,
int
event
)
{
struct
snd_soc_codec
*
codec
=
w
->
codec
;
switch
(
event
)
{
case
SND_SOC_DAPM_POST_PMU
:
snd_soc_write
(
codec
,
RT286_SET_PIN_DMIC1
,
0x20
);
break
;
case
SND_SOC_DAPM_PRE_PMD
:
snd_soc_write
(
codec
,
RT286_SET_PIN_DMIC1
,
0
);
break
;
default:
return
0
;
}
return
0
;
}
static
int
rt286_adc_event
(
struct
snd_soc_dapm_widget
*
w
,
struct
snd_kcontrol
*
kcontrol
,
int
event
)
{
struct
snd_soc_codec
*
codec
=
w
->
codec
;
unsigned
int
nid
;
nid
=
(
w
->
reg
>>
20
)
&
0xff
;
switch
(
event
)
{
case
SND_SOC_DAPM_POST_PMU
:
snd_soc_update_bits
(
codec
,
VERB_CMD
(
AC_VERB_SET_AMP_GAIN_MUTE
,
nid
,
0
),
0x7080
,
0x7000
);
break
;
case
SND_SOC_DAPM_PRE_PMD
:
snd_soc_update_bits
(
codec
,
VERB_CMD
(
AC_VERB_SET_AMP_GAIN_MUTE
,
nid
,
0
),
0x7080
,
0x7080
);
break
;
default:
return
0
;
}
return
0
;
}
static
const
struct
snd_soc_dapm_widget
rt286_dapm_widgets
[]
=
{
/* Input Lines */
SND_SOC_DAPM_INPUT
(
"DMIC1 Pin"
),
SND_SOC_DAPM_INPUT
(
"DMIC2 Pin"
),
SND_SOC_DAPM_INPUT
(
"MIC1"
),
SND_SOC_DAPM_INPUT
(
"LINE1"
),
SND_SOC_DAPM_INPUT
(
"Beep"
),
/* DMIC */
SND_SOC_DAPM_PGA_E
(
"DMIC1"
,
RT286_SET_POWER
(
RT286_DMIC1
),
0
,
1
,
NULL
,
0
,
rt286_set_dmic1_event
,
SND_SOC_DAPM_PRE_PMD
|
SND_SOC_DAPM_POST_PMU
),
SND_SOC_DAPM_PGA
(
"DMIC2"
,
RT286_SET_POWER
(
RT286_DMIC2
),
0
,
1
,
NULL
,
0
),
SND_SOC_DAPM_SUPPLY
(
"DMIC Receiver"
,
SND_SOC_NOPM
,
0
,
0
,
NULL
,
0
),
/* REC Mixer */
SND_SOC_DAPM_MIXER
(
"RECMIX"
,
SND_SOC_NOPM
,
0
,
0
,
rt286_rec_mix
,
ARRAY_SIZE
(
rt286_rec_mix
)),
/* ADCs */
SND_SOC_DAPM_ADC
(
"ADC 0"
,
NULL
,
SND_SOC_NOPM
,
0
,
0
),
SND_SOC_DAPM_ADC
(
"ADC 1"
,
NULL
,
SND_SOC_NOPM
,
0
,
0
),
/* ADC Mux */
SND_SOC_DAPM_MUX_E
(
"ADC 0 Mux"
,
RT286_SET_POWER
(
RT286_ADC_IN1
),
0
,
1
,
&
rt286_adc0_mux
,
rt286_adc_event
,
SND_SOC_DAPM_PRE_PMD
|
SND_SOC_DAPM_POST_PMU
),
SND_SOC_DAPM_MUX_E
(
"ADC 1 Mux"
,
RT286_SET_POWER
(
RT286_ADC_IN2
),
0
,
1
,
&
rt286_adc1_mux
,
rt286_adc_event
,
SND_SOC_DAPM_PRE_PMD
|
SND_SOC_DAPM_POST_PMU
),
/* Audio Interface */
SND_SOC_DAPM_AIF_IN
(
"AIF1RX"
,
"AIF1 Playback"
,
0
,
SND_SOC_NOPM
,
0
,
0
),
SND_SOC_DAPM_AIF_OUT
(
"AIF1TX"
,
"AIF1 Capture"
,
0
,
SND_SOC_NOPM
,
0
,
0
),
SND_SOC_DAPM_AIF_IN
(
"AIF2RX"
,
"AIF2 Playback"
,
0
,
SND_SOC_NOPM
,
0
,
0
),
SND_SOC_DAPM_AIF_OUT
(
"AIF2TX"
,
"AIF2 Capture"
,
0
,
SND_SOC_NOPM
,
0
,
0
),
/* Output Side */
/* DACs */
SND_SOC_DAPM_DAC
(
"DAC 0"
,
NULL
,
SND_SOC_NOPM
,
0
,
0
),
SND_SOC_DAPM_DAC
(
"DAC 1"
,
NULL
,
SND_SOC_NOPM
,
0
,
0
),
/* Output Mux */
SND_SOC_DAPM_MUX
(
"SPK Mux"
,
SND_SOC_NOPM
,
0
,
0
,
&
rt286_spo_mux
),
SND_SOC_DAPM_MUX
(
"HPO Mux"
,
SND_SOC_NOPM
,
0
,
0
,
&
rt286_hpo_mux
),
SND_SOC_DAPM_SUPPLY
(
"HP Power"
,
RT286_SET_PIN_HPO
,
RT286_SET_PIN_SFT
,
0
,
NULL
,
0
),
/* Output Mixer */
SND_SOC_DAPM_MIXER
(
"Front"
,
RT286_SET_POWER
(
RT286_DAC_OUT1
),
0
,
1
,
rt286_front_mix
,
ARRAY_SIZE
(
rt286_front_mix
)),
SND_SOC_DAPM_PGA
(
"Surround"
,
RT286_SET_POWER
(
RT286_DAC_OUT2
),
0
,
1
,
NULL
,
0
),
/* Output Pga */
SND_SOC_DAPM_SWITCH_E
(
"SPO"
,
SND_SOC_NOPM
,
0
,
0
,
&
spo_enable_control
,
rt286_spk_event
,
SND_SOC_DAPM_PRE_PMD
|
SND_SOC_DAPM_POST_PMU
),
SND_SOC_DAPM_SWITCH
(
"HPO L"
,
SND_SOC_NOPM
,
0
,
0
,
&
hpol_enable_control
),
SND_SOC_DAPM_SWITCH
(
"HPO R"
,
SND_SOC_NOPM
,
0
,
0
,
&
hpor_enable_control
),
/* Output Lines */
SND_SOC_DAPM_OUTPUT
(
"SPOL"
),
SND_SOC_DAPM_OUTPUT
(
"SPOR"
),
SND_SOC_DAPM_OUTPUT
(
"HPO Pin"
),
SND_SOC_DAPM_OUTPUT
(
"SPDIF"
),
};
static
const
struct
snd_soc_dapm_route
rt286_dapm_routes
[]
=
{
{
"DMIC1"
,
NULL
,
"DMIC1 Pin"
},
{
"DMIC2"
,
NULL
,
"DMIC2 Pin"
},
{
"DMIC1"
,
NULL
,
"DMIC Receiver"
},
{
"DMIC2"
,
NULL
,
"DMIC Receiver"
},
{
"RECMIX"
,
"Beep Switch"
,
"Beep"
},
{
"RECMIX"
,
"Line1 Switch"
,
"LINE1"
},
{
"RECMIX"
,
"Mic1 Switch"
,
"MIC1"
},
{
"ADC 0 Mux"
,
"Dmic"
,
"DMIC1"
},
{
"ADC 0 Mux"
,
"RECMIX"
,
"RECMIX"
},
{
"ADC 0 Mux"
,
"Mic"
,
"MIC1"
},
{
"ADC 1 Mux"
,
"Dmic"
,
"DMIC2"
},
{
"ADC 1 Mux"
,
"RECMIX"
,
"RECMIX"
},
{
"ADC 1 Mux"
,
"Mic"
,
"MIC1"
},
{
"ADC 0"
,
NULL
,
"ADC 0 Mux"
},
{
"ADC 1"
,
NULL
,
"ADC 1 Mux"
},
{
"AIF1TX"
,
NULL
,
"ADC 0"
},
{
"AIF2TX"
,
NULL
,
"ADC 1"
},
{
"DAC 0"
,
NULL
,
"AIF1RX"
},
{
"DAC 1"
,
NULL
,
"AIF2RX"
},
{
"Front"
,
"DAC Switch"
,
"DAC 0"
},
{
"Front"
,
"RECMIX Switch"
,
"RECMIX"
},
{
"Surround"
,
NULL
,
"DAC 1"
},
{
"SPK Mux"
,
"Front"
,
"Front"
},
{
"SPK Mux"
,
"Surround"
,
"Surround"
},
{
"HPO Mux"
,
"Front"
,
"Front"
},
{
"HPO Mux"
,
"Surround"
,
"Surround"
},
{
"SPO"
,
"Switch"
,
"SPK Mux"
},
{
"HPO L"
,
"Switch"
,
"HPO Mux"
},
{
"HPO R"
,
"Switch"
,
"HPO Mux"
},
{
"HPO L"
,
NULL
,
"HP Power"
},
{
"HPO R"
,
NULL
,
"HP Power"
},
{
"SPOL"
,
NULL
,
"SPO"
},
{
"SPOR"
,
NULL
,
"SPO"
},
{
"HPO Pin"
,
NULL
,
"HPO L"
},
{
"HPO Pin"
,
NULL
,
"HPO R"
},
};
static
int
rt286_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
,
struct
snd_soc_dai
*
dai
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
struct
rt286_priv
*
rt286
=
snd_soc_codec_get_drvdata
(
codec
);
unsigned
int
val
=
0
;
int
d_len_code
;
switch
(
params_rate
(
params
))
{
/* bit 14 0:48K 1:44.1K */
case
44100
:
val
|=
0x4000
;
break
;
case
48000
:
break
;
default:
dev_err
(
codec
->
dev
,
"Unsupported sample rate %d
\n
"
,
params_rate
(
params
));
return
-
EINVAL
;
}
switch
(
rt286
->
sys_clk
)
{
case
12288000
:
case
24576000
:
if
(
params_rate
(
params
)
!=
48000
)
{
dev_err
(
codec
->
dev
,
"Sys_clk is not matched (%d %d)
\n
"
,
params_rate
(
params
),
rt286
->
sys_clk
);
return
-
EINVAL
;
}
break
;
case
11289600
:
case
22579200
:
if
(
params_rate
(
params
)
!=
44100
)
{
dev_err
(
codec
->
dev
,
"Sys_clk is not matched (%d %d)
\n
"
,
params_rate
(
params
),
rt286
->
sys_clk
);
return
-
EINVAL
;
}
break
;
}
if
(
params_channels
(
params
)
<=
16
)
{
/* bit 3:0 Number of Channel */
val
|=
(
params_channels
(
params
)
-
1
);
}
else
{
dev_err
(
codec
->
dev
,
"Unsupported channels %d
\n
"
,
params_channels
(
params
));
return
-
EINVAL
;
}
d_len_code
=
0
;
switch
(
params_width
(
params
))
{
/* bit 6:4 Bits per Sample */
case
16
:
d_len_code
=
0
;
val
|=
(
0x1
<<
4
);
break
;
case
32
:
d_len_code
=
2
;
val
|=
(
0x4
<<
4
);
break
;
case
20
:
d_len_code
=
1
;
val
|=
(
0x2
<<
4
);
break
;
case
24
:
d_len_code
=
2
;
val
|=
(
0x3
<<
4
);
break
;
case
8
:
d_len_code
=
3
;
break
;
default:
return
-
EINVAL
;
}
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL1
,
0x0018
,
d_len_code
<<
3
);
dev_dbg
(
codec
->
dev
,
"format val = 0x%x
\n
"
,
val
);
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
snd_soc_update_bits
(
codec
,
RT286_DAC_FORMAT
,
0x407f
,
val
);
else
snd_soc_update_bits
(
codec
,
RT286_ADC_FORMAT
,
0x407f
,
val
);
return
0
;
}
static
int
rt286_set_dai_fmt
(
struct
snd_soc_dai
*
dai
,
unsigned
int
fmt
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
switch
(
fmt
&
SND_SOC_DAIFMT_MASTER_MASK
)
{
case
SND_SOC_DAIFMT_CBM_CFM
:
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL1
,
0x800
,
0x800
);
break
;
case
SND_SOC_DAIFMT_CBS_CFS
:
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL1
,
0x800
,
0x0
);
break
;
default:
return
-
EINVAL
;
}
switch
(
fmt
&
SND_SOC_DAIFMT_FORMAT_MASK
)
{
case
SND_SOC_DAIFMT_I2S
:
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL1
,
0x300
,
0x0
);
break
;
case
SND_SOC_DAIFMT_LEFT_J
:
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL1
,
0x300
,
0x1
<<
8
);
break
;
case
SND_SOC_DAIFMT_DSP_A
:
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL1
,
0x300
,
0x2
<<
8
);
break
;
case
SND_SOC_DAIFMT_DSP_B
:
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL1
,
0x300
,
0x3
<<
8
);
break
;
default:
return
-
EINVAL
;
}
/* bit 15 Stream Type 0:PCM 1:Non-PCM */
snd_soc_update_bits
(
codec
,
RT286_DAC_FORMAT
,
0x8000
,
0
);
snd_soc_update_bits
(
codec
,
RT286_ADC_FORMAT
,
0x8000
,
0
);
return
0
;
}
static
int
rt286_set_dai_sysclk
(
struct
snd_soc_dai
*
dai
,
int
clk_id
,
unsigned
int
freq
,
int
dir
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
struct
rt286_priv
*
rt286
=
snd_soc_codec_get_drvdata
(
codec
);
dev_dbg
(
codec
->
dev
,
"%s freq=%d
\n
"
,
__func__
,
freq
);
if
(
RT286_SCLK_S_MCLK
==
clk_id
)
{
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL2
,
0x0100
,
0x0
);
snd_soc_update_bits
(
codec
,
RT286_PLL_CTRL1
,
0x20
,
0x20
);
}
else
{
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL2
,
0x0100
,
0x0100
);
snd_soc_update_bits
(
codec
,
RT286_PLL_CTRL
,
0x4
,
0x4
);
snd_soc_update_bits
(
codec
,
RT286_PLL_CTRL1
,
0x20
,
0x0
);
}
switch
(
freq
)
{
case
19200000
:
if
(
RT286_SCLK_S_MCLK
==
clk_id
)
{
dev_err
(
codec
->
dev
,
"Should not use MCLK
\n
"
);
return
-
EINVAL
;
}
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL2
,
0x40
,
0x40
);
break
;
case
24000000
:
if
(
RT286_SCLK_S_MCLK
==
clk_id
)
{
dev_err
(
codec
->
dev
,
"Should not use MCLK
\n
"
);
return
-
EINVAL
;
}
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL2
,
0x40
,
0x0
);
break
;
case
12288000
:
case
11289600
:
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL2
,
0x8
,
0x0
);
snd_soc_update_bits
(
codec
,
RT286_CLK_DIV
,
0xfc1e
,
0x0004
);
break
;
case
24576000
:
case
22579200
:
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL2
,
0x8
,
0x8
);
snd_soc_update_bits
(
codec
,
RT286_CLK_DIV
,
0xfc1e
,
0x5406
);
break
;
default:
dev_err
(
codec
->
dev
,
"Unsupported system clock
\n
"
);
return
-
EINVAL
;
}
rt286
->
sys_clk
=
freq
;
return
0
;
}
static
int
rt286_set_bclk_ratio
(
struct
snd_soc_dai
*
dai
,
unsigned
int
ratio
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
dev_dbg
(
codec
->
dev
,
"%s ratio=%d
\n
"
,
__func__
,
ratio
);
if
(
50
==
ratio
)
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL1
,
0x1000
,
0x1000
);
else
snd_soc_update_bits
(
codec
,
RT286_I2S_CTRL1
,
0x1000
,
0x0
);
return
0
;
}
static
int
rt286_set_bias_level
(
struct
snd_soc_codec
*
codec
,
enum
snd_soc_bias_level
level
)
{
switch
(
level
)
{
case
SND_SOC_BIAS_PREPARE
:
if
(
SND_SOC_BIAS_STANDBY
==
codec
->
dapm
.
bias_level
)
{
snd_soc_write
(
codec
,
RT286_SET_AUDIO_POWER
,
AC_PWRST_D0
);
snd_soc_update_bits
(
codec
,
RT286_DC_GAIN
,
0x200
,
0x200
);
}
break
;
case
SND_SOC_BIAS_ON
:
mdelay
(
10
);
break
;
case
SND_SOC_BIAS_STANDBY
:
snd_soc_write
(
codec
,
RT286_SET_AUDIO_POWER
,
AC_PWRST_D3
);
snd_soc_update_bits
(
codec
,
RT286_DC_GAIN
,
0x200
,
0x0
);
break
;
default:
break
;
}
codec
->
dapm
.
bias_level
=
level
;
return
0
;
}
static
irqreturn_t
rt286_irq
(
int
irq
,
void
*
data
)
{
struct
rt286_priv
*
rt286
=
data
;
bool
hp
=
false
;
bool
mic
=
false
;
int
status
=
0
;
rt286_jack_detect
(
rt286
->
codec
,
&
hp
,
&
mic
);
/* Clear IRQ */
snd_soc_update_bits
(
rt286
->
codec
,
RT286_IRQ_CTRL
,
0x1
,
0x1
);
if
(
hp
==
true
)
status
|=
SND_JACK_HEADPHONE
;
if
(
mic
==
true
)
status
|=
SND_JACK_MICROPHONE
;
snd_soc_jack_report
(
rt286
->
jack
,
status
,
SND_JACK_MICROPHONE
|
SND_JACK_HEADPHONE
);
pm_wakeup_event
(
&
rt286
->
i2c
->
dev
,
300
);
return
IRQ_HANDLED
;
}
static
int
rt286_probe
(
struct
snd_soc_codec
*
codec
)
{
struct
rt286_priv
*
rt286
=
snd_soc_codec_get_drvdata
(
codec
);
codec
->
dapm
.
bias_level
=
SND_SOC_BIAS_OFF
;
rt286
->
codec
=
codec
;
return
0
;
}
static
int
rt286_remove
(
struct
snd_soc_codec
*
codec
)
{
struct
rt286_priv
*
rt286
=
snd_soc_codec_get_drvdata
(
codec
);
cancel_delayed_work_sync
(
&
rt286
->
jack_detect_work
);
return
0
;
}
#ifdef CONFIG_PM
static
int
rt286_suspend
(
struct
snd_soc_codec
*
codec
)
{
struct
rt286_priv
*
rt286
=
snd_soc_codec_get_drvdata
(
codec
);
regcache_cache_only
(
rt286
->
regmap
,
true
);
regcache_mark_dirty
(
rt286
->
regmap
);
return
0
;
}
static
int
rt286_resume
(
struct
snd_soc_codec
*
codec
)
{
struct
rt286_priv
*
rt286
=
snd_soc_codec_get_drvdata
(
codec
);
regcache_cache_only
(
rt286
->
regmap
,
false
);
rt286_index_sync
(
codec
);
regcache_sync
(
rt286
->
regmap
);
return
0
;
}
#else
#define rt286_suspend NULL
#define rt286_resume NULL
#endif
#define RT286_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
#define RT286_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
static
const
struct
snd_soc_dai_ops
rt286_aif_dai_ops
=
{
.
hw_params
=
rt286_hw_params
,
.
set_fmt
=
rt286_set_dai_fmt
,
.
set_sysclk
=
rt286_set_dai_sysclk
,
.
set_bclk_ratio
=
rt286_set_bclk_ratio
,
};
static
struct
snd_soc_dai_driver
rt286_dai
[]
=
{
{
.
name
=
"rt286-aif1"
,
.
id
=
RT286_AIF1
,
.
playback
=
{
.
stream_name
=
"AIF1 Playback"
,
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rates
=
RT286_STEREO_RATES
,
.
formats
=
RT286_FORMATS
,
},
.
capture
=
{
.
stream_name
=
"AIF1 Capture"
,
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rates
=
RT286_STEREO_RATES
,
.
formats
=
RT286_FORMATS
,
},
.
ops
=
&
rt286_aif_dai_ops
,
.
symmetric_rates
=
1
,
},
{
.
name
=
"rt286-aif2"
,
.
id
=
RT286_AIF2
,
.
playback
=
{
.
stream_name
=
"AIF2 Playback"
,
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rates
=
RT286_STEREO_RATES
,
.
formats
=
RT286_FORMATS
,
},
.
capture
=
{
.
stream_name
=
"AIF2 Capture"
,
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rates
=
RT286_STEREO_RATES
,
.
formats
=
RT286_FORMATS
,
},
.
ops
=
&
rt286_aif_dai_ops
,
.
symmetric_rates
=
1
,
},
};
static
struct
snd_soc_codec_driver
soc_codec_dev_rt286
=
{
.
probe
=
rt286_probe
,
.
remove
=
rt286_remove
,
.
suspend
=
rt286_suspend
,
.
resume
=
rt286_resume
,
.
set_bias_level
=
rt286_set_bias_level
,
.
idle_bias_off
=
true
,
.
controls
=
rt286_snd_controls
,
.
num_controls
=
ARRAY_SIZE
(
rt286_snd_controls
),
.
dapm_widgets
=
rt286_dapm_widgets
,
.
num_dapm_widgets
=
ARRAY_SIZE
(
rt286_dapm_widgets
),
.
dapm_routes
=
rt286_dapm_routes
,
.
num_dapm_routes
=
ARRAY_SIZE
(
rt286_dapm_routes
),
};
static
const
struct
regmap_config
rt286_regmap
=
{
.
reg_bits
=
32
,
.
val_bits
=
32
,
.
max_register
=
0x02370100
,
.
volatile_reg
=
rt286_volatile_register
,
.
readable_reg
=
rt286_readable_register
,
.
reg_write
=
rt286_hw_write
,
.
reg_read
=
rt286_hw_read
,
.
cache_type
=
REGCACHE_RBTREE
,
.
reg_defaults
=
rt286_reg
,
.
num_reg_defaults
=
ARRAY_SIZE
(
rt286_reg
),
};
static
const
struct
i2c_device_id
rt286_i2c_id
[]
=
{
{
"rt286"
,
0
},
{}
};
MODULE_DEVICE_TABLE
(
i2c
,
rt286_i2c_id
);
static
const
struct
acpi_device_id
rt286_acpi_match
[]
=
{
{
"INT343A"
,
0
},
{},
};
MODULE_DEVICE_TABLE
(
acpi
,
rt286_acpi_match
);
static
int
rt286_i2c_probe
(
struct
i2c_client
*
i2c
,
const
struct
i2c_device_id
*
id
)
{
struct
rt286_platform_data
*
pdata
=
dev_get_platdata
(
&
i2c
->
dev
);
struct
rt286_priv
*
rt286
;
int
i
,
ret
;
rt286
=
devm_kzalloc
(
&
i2c
->
dev
,
sizeof
(
*
rt286
),
GFP_KERNEL
);
if
(
NULL
==
rt286
)
return
-
ENOMEM
;
rt286
->
regmap
=
devm_regmap_init
(
&
i2c
->
dev
,
NULL
,
i2c
,
&
rt286_regmap
);
if
(
IS_ERR
(
rt286
->
regmap
))
{
ret
=
PTR_ERR
(
rt286
->
regmap
);
dev_err
(
&
i2c
->
dev
,
"Failed to allocate register map: %d
\n
"
,
ret
);
return
ret
;
}
regmap_read
(
rt286
->
regmap
,
RT286_GET_PARAM
(
AC_NODE_ROOT
,
AC_PAR_VENDOR_ID
),
&
ret
);
if
(
ret
!=
RT286_VENDOR_ID
)
{
dev_err
(
&
i2c
->
dev
,
"Device with ID register %x is not rt286
\n
"
,
ret
);
return
-
ENODEV
;
}
rt286
->
index_cache
=
rt286_index_def
;
rt286
->
i2c
=
i2c
;
i2c_set_clientdata
(
i2c
,
rt286
);
if
(
pdata
)
rt286
->
pdata
=
*
pdata
;
regmap_write
(
rt286
->
regmap
,
RT286_SET_AUDIO_POWER
,
AC_PWRST_D3
);
for
(
i
=
0
;
i
<
RT286_POWER_REG_LEN
;
i
++
)
regmap_write
(
rt286
->
regmap
,
RT286_SET_POWER
(
rt286_support_power_controls
[
i
]),
AC_PWRST_D1
);
if
(
!
rt286
->
pdata
.
cbj_en
)
{
regmap_write
(
rt286
->
regmap
,
RT286_CBJ_CTRL2
,
0x0000
);
regmap_write
(
rt286
->
regmap
,
RT286_MIC1_DET_CTRL
,
0x0816
);
regmap_write
(
rt286
->
regmap
,
RT286_MISC_CTRL1
,
0x0000
);
regmap_update_bits
(
rt286
->
regmap
,
RT286_CBJ_CTRL1
,
0xf000
,
0xb000
);
}
else
{
regmap_update_bits
(
rt286
->
regmap
,
RT286_CBJ_CTRL1
,
0xf000
,
0x5000
);
}
mdelay
(
10
);
if
(
!
rt286
->
pdata
.
gpio2_en
)
regmap_write
(
rt286
->
regmap
,
RT286_SET_DMIC2_DEFAULT
,
0x4000
);
else
regmap_write
(
rt286
->
regmap
,
RT286_SET_DMIC2_DEFAULT
,
0
);
mdelay
(
10
);
/*Power down LDO2*/
regmap_update_bits
(
rt286
->
regmap
,
RT286_POWER_CTRL2
,
0x8
,
0x0
);
/*Set depop parameter*/
regmap_update_bits
(
rt286
->
regmap
,
RT286_DEPOP_CTRL2
,
0x403a
,
0x401a
);
regmap_update_bits
(
rt286
->
regmap
,
RT286_DEPOP_CTRL3
,
0xf777
,
0x4737
);
regmap_update_bits
(
rt286
->
regmap
,
RT286_DEPOP_CTRL4
,
0x00ff
,
0x003f
);
if
(
rt286
->
i2c
->
irq
)
{
regmap_update_bits
(
rt286
->
regmap
,
RT286_IRQ_CTRL
,
0x2
,
0x2
);
INIT_DELAYED_WORK
(
&
rt286
->
jack_detect_work
,
rt286_jack_detect_work
);
schedule_delayed_work
(
&
rt286
->
jack_detect_work
,
msecs_to_jiffies
(
1250
));
ret
=
request_threaded_irq
(
rt286
->
i2c
->
irq
,
NULL
,
rt286_irq
,
IRQF_TRIGGER_HIGH
|
IRQF_ONESHOT
,
"rt286"
,
rt286
);
if
(
ret
!=
0
)
{
dev_err
(
&
i2c
->
dev
,
"Failed to reguest IRQ: %d
\n
"
,
ret
);
return
ret
;
}
}
ret
=
snd_soc_register_codec
(
&
i2c
->
dev
,
&
soc_codec_dev_rt286
,
rt286_dai
,
ARRAY_SIZE
(
rt286_dai
));
return
ret
;
}
static
int
rt286_i2c_remove
(
struct
i2c_client
*
i2c
)
{
struct
rt286_priv
*
rt286
=
i2c_get_clientdata
(
i2c
);
if
(
i2c
->
irq
)
free_irq
(
i2c
->
irq
,
rt286
);
snd_soc_unregister_codec
(
&
i2c
->
dev
);
return
0
;
}
static
struct
i2c_driver
rt286_i2c_driver
=
{
.
driver
=
{
.
name
=
"rt286"
,
.
owner
=
THIS_MODULE
,
.
acpi_match_table
=
ACPI_PTR
(
rt286_acpi_match
),
},
.
probe
=
rt286_i2c_probe
,
.
remove
=
rt286_i2c_remove
,
.
id_table
=
rt286_i2c_id
,
};
module_i2c_driver
(
rt286_i2c_driver
);
MODULE_DESCRIPTION
(
"ASoC RT286 driver"
);
MODULE_AUTHOR
(
"Bard Liao <bardliao@realtek.com>"
);
MODULE_LICENSE
(
"GPL"
);
sound/soc/codecs/rt286.h
0 → 100644
浏览文件 @
0ca0c2d8
/*
* rt286.h -- RT286 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@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 __RT286_H__
#define __RT286_H__
#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
#define RT286_AUDIO_FUNCTION_GROUP 0x01
#define RT286_DAC_OUT1 0x02
#define RT286_DAC_OUT2 0x03
#define RT286_ADC_IN1 0x09
#define RT286_ADC_IN2 0x08
#define RT286_MIXER_IN 0x0b
#define RT286_MIXER_OUT1 0x0c
#define RT286_MIXER_OUT2 0x0d
#define RT286_DMIC1 0x12
#define RT286_DMIC2 0x13
#define RT286_SPK_OUT 0x14
#define RT286_MIC1 0x18
#define RT286_LINE1 0x1a
#define RT286_BEEP 0x1d
#define RT286_SPDIF 0x1e
#define RT286_VENDOR_REGISTERS 0x20
#define RT286_HP_OUT 0x21
#define RT286_MIXER_IN1 0x22
#define RT286_MIXER_IN2 0x23
#define RT286_SET_PIN_SFT 6
#define RT286_SET_PIN_ENABLE 0x40
#define RT286_SET_PIN_DISABLE 0
#define RT286_SET_EAPD_HIGH 0x2
#define RT286_SET_EAPD_LOW 0
#define RT286_MUTE_SFT 7
/* Verb commands */
#define RT286_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM)
#define RT286_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0)
#define RT286_SET_AUDIO_POWER RT286_SET_POWER(RT286_AUDIO_FUNCTION_GROUP)
#define RT286_SET_HPO_POWER RT286_SET_POWER(RT286_HP_OUT)
#define RT286_SET_SPK_POWER RT286_SET_POWER(RT286_SPK_OUT)
#define RT286_SET_DMIC1_POWER RT286_SET_POWER(RT286_DMIC1)
#define RT286_SPK_MUX\
VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_SPK_OUT, 0)
#define RT286_HPO_MUX\
VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_HP_OUT, 0)
#define RT286_ADC0_MUX\
VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN1, 0)
#define RT286_ADC1_MUX\
VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN2, 0)
#define RT286_SET_MIC1\
VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_MIC1, 0)
#define RT286_SET_PIN_HPO\
VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_HP_OUT, 0)
#define RT286_SET_PIN_SPK\
VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_SPK_OUT, 0)
#define RT286_SET_PIN_DMIC1\
VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_DMIC1, 0)
#define RT286_SPK_EAPD\
VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT286_SPK_OUT, 0)
#define RT286_SET_AMP_GAIN_HPO\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0)
#define RT286_SET_AMP_GAIN_ADC_IN1\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0)
#define RT286_SET_AMP_GAIN_ADC_IN2\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN2, 0)
#define RT286_GET_HP_SENSE\
VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_HP_OUT, 0)
#define RT286_GET_MIC1_SENSE\
VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_MIC1, 0)
#define RT286_SET_DMIC2_DEFAULT\
VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT286_DMIC2, 0)
#define RT286_DACL_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0xa000)
#define RT286_DACR_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0x9000)
#define RT286_ADCL_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x6000)
#define RT286_ADCR_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x5000)
#define RT286_MIC_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIC1, 0x7000)
#define RT286_SPOL_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0xa000)
#define RT286_SPOR_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0x9000)
#define RT286_HPOL_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0xa000)
#define RT286_HPOR_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0x9000)
#define RT286_F_DAC_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7000)
#define RT286_F_RECMIX_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7100)
#define RT286_REC_MIC_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7000)
#define RT286_REC_I2S_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7100)
#define RT286_REC_LINE_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7200)
#define RT286_REC_BEEP_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7300)
#define RT286_DAC_FORMAT\
VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_DAC_OUT1, 0)
#define RT286_ADC_FORMAT\
VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_ADC_IN1, 0)
#define RT286_COEF_INDEX\
VERB_CMD(AC_VERB_SET_COEF_INDEX, RT286_VENDOR_REGISTERS, 0)
#define RT286_PROC_COEF\
VERB_CMD(AC_VERB_SET_PROC_COEF, RT286_VENDOR_REGISTERS, 0)
/* Index registers */
#define RT286_A_BIAS_CTRL1 0x01
#define RT286_A_BIAS_CTRL2 0x02
#define RT286_POWER_CTRL1 0x03
#define RT286_A_BIAS_CTRL3 0x04
#define RT286_POWER_CTRL2 0x08
#define RT286_I2S_CTRL1 0x09
#define RT286_I2S_CTRL2 0x0a
#define RT286_CLK_DIV 0x0b
#define RT286_DC_GAIN 0x0d
#define RT286_POWER_CTRL3 0x0f
#define RT286_MIC1_DET_CTRL 0x19
#define RT286_MISC_CTRL1 0x20
#define RT286_IRQ_CTRL 0x33
#define RT286_PLL_CTRL1 0x49
#define RT286_CBJ_CTRL1 0x4f
#define RT286_CBJ_CTRL2 0x50
#define RT286_PLL_CTRL 0x63
#define RT286_DEPOP_CTRL1 0x66
#define RT286_DEPOP_CTRL2 0x67
#define RT286_DEPOP_CTRL3 0x68
#define RT286_DEPOP_CTRL4 0x69
/* SPDIF (0x06) */
#define RT286_SPDIF_SEL_SFT 0
#define RT286_SPDIF_SEL_PCM0 0
#define RT286_SPDIF_SEL_PCM1 1
#define RT286_SPDIF_SEL_SPOUT 2
#define RT286_SPDIF_SEL_PP 3
/* RECMIX (0x0b) */
#define RT286_M_REC_BEEP_SFT 0
#define RT286_M_REC_LINE1_SFT 1
#define RT286_M_REC_MIC1_SFT 2
#define RT286_M_REC_I2S_SFT 3
/* Front (0x0c) */
#define RT286_M_FRONT_DAC_SFT 0
#define RT286_M_FRONT_REC_SFT 1
/* SPK-OUT (0x14) */
#define RT286_M_SPK_MUX_SFT 14
#define RT286_SPK_SEL_MASK 0x1
#define RT286_SPK_SEL_SFT 0
#define RT286_SPK_SEL_F 0
#define RT286_SPK_SEL_S 1
/* HP-OUT (0x21) */
#define RT286_M_HP_MUX_SFT 14
#define RT286_HP_SEL_MASK 0x1
#define RT286_HP_SEL_SFT 0
#define RT286_HP_SEL_F 0
#define RT286_HP_SEL_S 1
/* ADC (0x22) (0x23) */
#define RT286_ADC_SEL_MASK 0x7
#define RT286_ADC_SEL_SFT 0
#define RT286_ADC_SEL_SURR 0
#define RT286_ADC_SEL_FRONT 1
#define RT286_ADC_SEL_DMIC 2
#define RT286_ADC_SEL_BEEP 4
#define RT286_ADC_SEL_LINE1 5
#define RT286_ADC_SEL_I2S 6
#define RT286_ADC_SEL_MIC1 7
#define RT286_SCLK_S_MCLK 0
#define RT286_SCLK_S_PLL 1
enum
{
RT286_AIF1
,
RT286_AIF2
,
RT286_AIFS
,
};
int
rt286_mic_detect
(
struct
snd_soc_codec
*
codec
,
struct
snd_soc_jack
*
jack
);
#endif
/* __RT286_H__ */
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录