Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
77c92d2b
Commit
77c92d2b
authored
May 27, 2016
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/sti' into asoc-next
parents
38e3c63d
ee4c879b
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
687 additions
and
136 deletions
+687
-136
Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt
Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt
+41
-7
sound/soc/sti/sti_uniperif.c
sound/soc/sti/sti_uniperif.c
+143
-1
sound/soc/sti/uniperif.h
sound/soc/sti/uniperif.h
+204
-16
sound/soc/sti/uniperif_player.c
sound/soc/sti/uniperif_player.c
+131
-51
sound/soc/sti/uniperif_reader.c
sound/soc/sti/uniperif_reader.c
+168
-61
No files found.
Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt
View file @
77c92d2b
...
...
@@ -37,17 +37,18 @@ Required properties:
- dai-name: DAI name that describes the IP.
- IP mode: IP working mode depending on associated codec.
"HDMI" connected to HDMI codec and support IEC HDMI formats (player only).
"SPDIF" connected to SPDIF codec and support SPDIF formats (player only).
"PCM" PCM standard mode for I2S or TDM bus.
"TDM" TDM mode for TDM bus.
Required properties ("st,sti-uni-player" compatibility only):
- clocks: CPU_DAI IP clock source, listed in the same order than the
CPU_DAI properties.
- uniperiph-id: internal SOC IP instance ID.
- IP mode: IP working mode depending on associated codec.
"HDMI" connected to HDMI codec IP and IEC HDMI formats.
"SPDIF"connected to SPDIF codec and support SPDIF formats.
"PCM" PCM standard mode for I2S or TDM bus.
Optional properties:
- pinctrl-0: defined for CPU_DAI@1 and CPU_DAI@4 to describe I2S PIOs for
external codecs connection.
...
...
@@ -56,6 +57,22 @@ Optional properties:
Example:
sti_uni_player1: sti-uni-player@1 {
compatible = "st,sti-uni-player";
status = "okay";
#sound-dai-cells = <0>;
st,syscfg = <&syscfg_core>;
clocks = <&clk_s_d0_flexgen CLK_PCM_1>;
reg = <0x8D81000 0x158>;
interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>;
dmas = <&fdma0 3 0 1>;
st,dai-name = "Uni Player #1 (I2S)";
dma-names = "tx";
st,uniperiph-id = <1>;
st,version = <5>;
st,mode = "TDM";
};
sti_uni_player2: sti-uni-player@2 {
compatible = "st,sti-uni-player";
status = "okay";
...
...
@@ -65,7 +82,7 @@ Example:
reg = <0x8D82000 0x158>;
interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
dmas = <&fdma0 4 0 1>;
dai-name = "Uni Player #
1
(DAC)";
dai-name = "Uni Player #
2
(DAC)";
dma-names = "tx";
uniperiph-id = <2>;
version = <5>;
...
...
@@ -82,7 +99,7 @@ Example:
interrupts = <GIC_SPI 89 IRQ_TYPE_NONE>;
dmas = <&fdma0 7 0 1>;
dma-names = "tx";
dai-name = "Uni Player #
1 (PIO
)";
dai-name = "Uni Player #
3 (SPDIF
)";
uniperiph-id = <3>;
version = <5>;
mode = "SPDIF";
...
...
@@ -99,6 +116,7 @@ Example:
dma-names = "rx";
dai-name = "Uni Reader #1 (HDMI RX)";
version = <3>;
st,mode = "PCM";
};
2) sti-sas-codec: internal audio codec IPs driver
...
...
@@ -152,4 +170,20 @@ Example of audio card declaration:
sound-dai = <&sti_sasg_codec 0>;
};
};
simple-audio-card,dai-link@2 {
/* TDM playback */
format = "left_j";
frame-inversion = <1>;
cpu {
sound-dai = <&sti_uni_player1>;
dai-tdm-slot-num = <16>;
dai-tdm-slot-width = <16>;
dai-tdm-slot-tx-mask =
<1 1 1 1 0 0 0 0 0 0 1 1 0 0 1 1>;
};
codec {
sound-dai = <&sti_sasg_codec 3>;
};
};
};
sound/soc/sti/sti_uniperif.c
View file @
77c92d2b
...
...
@@ -10,6 +10,142 @@
#include "uniperif.h"
/*
* User frame size shall be 2, 4, 6 or 8 32-bits words length
* (i.e. 8, 16, 24 or 32 bytes)
* This constraint comes from allowed values for
* UNIPERIF_I2S_FMT_NUM_CH register
*/
#define UNIPERIF_MAX_FRAME_SZ 0x20
#define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ)
int
sti_uniperiph_set_tdm_slot
(
struct
snd_soc_dai
*
dai
,
unsigned
int
tx_mask
,
unsigned
int
rx_mask
,
int
slots
,
int
slot_width
)
{
struct
sti_uniperiph_data
*
priv
=
snd_soc_dai_get_drvdata
(
dai
);
struct
uniperif
*
uni
=
priv
->
dai_data
.
uni
;
int
i
,
frame_size
,
avail_slots
;
if
(
!
UNIPERIF_TYPE_IS_TDM
(
uni
))
{
dev_err
(
uni
->
dev
,
"cpu dai not in tdm mode
\n
"
);
return
-
EINVAL
;
}
/* store info in unip context */
uni
->
tdm_slot
.
slots
=
slots
;
uni
->
tdm_slot
.
slot_width
=
slot_width
;
/* unip is unidirectionnal */
uni
->
tdm_slot
.
mask
=
(
tx_mask
!=
0
)
?
tx_mask
:
rx_mask
;
/* number of available timeslots */
for
(
i
=
0
,
avail_slots
=
0
;
i
<
uni
->
tdm_slot
.
slots
;
i
++
)
{
if
((
uni
->
tdm_slot
.
mask
>>
i
)
&
0x01
)
avail_slots
++
;
}
uni
->
tdm_slot
.
avail_slots
=
avail_slots
;
/* frame size in bytes */
frame_size
=
uni
->
tdm_slot
.
avail_slots
*
uni
->
tdm_slot
.
slot_width
/
8
;
/* check frame size is allowed */
if
((
frame_size
>
UNIPERIF_MAX_FRAME_SZ
)
||
(
frame_size
&
~
(
int
)
UNIPERIF_ALLOWED_FRAME_SZ
))
{
dev_err
(
uni
->
dev
,
"frame size not allowed: %d bytes
\n
"
,
frame_size
);
return
-
EINVAL
;
}
return
0
;
}
int
sti_uniperiph_fix_tdm_chan
(
struct
snd_pcm_hw_params
*
params
,
struct
snd_pcm_hw_rule
*
rule
)
{
struct
uniperif
*
uni
=
rule
->
private
;
struct
snd_interval
t
;
t
.
min
=
uni
->
tdm_slot
.
avail_slots
;
t
.
max
=
uni
->
tdm_slot
.
avail_slots
;
t
.
openmin
=
0
;
t
.
openmax
=
0
;
t
.
integer
=
0
;
return
snd_interval_refine
(
hw_param_interval
(
params
,
rule
->
var
),
&
t
);
}
int
sti_uniperiph_fix_tdm_format
(
struct
snd_pcm_hw_params
*
params
,
struct
snd_pcm_hw_rule
*
rule
)
{
struct
uniperif
*
uni
=
rule
->
private
;
struct
snd_mask
*
maskp
=
hw_param_mask
(
params
,
rule
->
var
);
u64
format
;
switch
(
uni
->
tdm_slot
.
slot_width
)
{
case
16
:
format
=
SNDRV_PCM_FMTBIT_S16_LE
;
break
;
case
32
:
format
=
SNDRV_PCM_FMTBIT_S32_LE
;
break
;
default:
dev_err
(
uni
->
dev
,
"format not supported: %d bits
\n
"
,
uni
->
tdm_slot
.
slot_width
);
return
-
EINVAL
;
}
maskp
->
bits
[
0
]
&=
(
u_int32_t
)
format
;
maskp
->
bits
[
1
]
&=
(
u_int32_t
)(
format
>>
32
);
/* clear remaining indexes */
memset
(
maskp
->
bits
+
2
,
0
,
(
SNDRV_MASK_MAX
-
64
)
/
8
);
if
(
!
maskp
->
bits
[
0
]
&&
!
maskp
->
bits
[
1
])
return
-
EINVAL
;
return
0
;
}
int
sti_uniperiph_get_tdm_word_pos
(
struct
uniperif
*
uni
,
unsigned
int
*
word_pos
)
{
int
slot_width
=
uni
->
tdm_slot
.
slot_width
/
8
;
int
slots_num
=
uni
->
tdm_slot
.
slots
;
unsigned
int
slots_mask
=
uni
->
tdm_slot
.
mask
;
int
i
,
j
,
k
;
unsigned
int
word16_pos
[
4
];
/* word16_pos:
* word16_pos[0] = WORDX_LSB
* word16_pos[1] = WORDX_MSB,
* word16_pos[2] = WORDX+1_LSB
* word16_pos[3] = WORDX+1_MSB
*/
/* set unip word position */
for
(
i
=
0
,
j
=
0
,
k
=
0
;
(
i
<
slots_num
)
&&
(
k
<
WORD_MAX
);
i
++
)
{
if
((
slots_mask
>>
i
)
&
0x01
)
{
word16_pos
[
j
]
=
i
*
slot_width
;
if
(
slot_width
==
4
)
{
word16_pos
[
j
+
1
]
=
word16_pos
[
j
]
+
2
;
j
++
;
}
j
++
;
if
(
j
>
3
)
{
word_pos
[
k
]
=
word16_pos
[
1
]
|
(
word16_pos
[
0
]
<<
8
)
|
(
word16_pos
[
3
]
<<
16
)
|
(
word16_pos
[
2
]
<<
24
);
j
=
0
;
k
++
;
}
}
}
return
0
;
}
/*
* sti_uniperiph_dai_create_ctrl
* This function is used to create Ctrl associated to DAI but also pcm device.
...
...
@@ -45,10 +181,16 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
struct
snd_pcm_hw_params
*
params
,
struct
snd_soc_dai
*
dai
)
{
struct
sti_uniperiph_data
*
priv
=
snd_soc_dai_get_drvdata
(
dai
);
struct
uniperif
*
uni
=
priv
->
dai_data
.
uni
;
struct
snd_dmaengine_dai_dma_data
*
dma_data
;
int
transfer_size
;
transfer_size
=
params_channels
(
params
)
*
UNIPERIF_FIFO_FRAMES
;
if
(
uni
->
info
->
type
==
SND_ST_UNIPERIF_TYPE_TDM
)
/* transfer size = user frame size (in 32-bits FIFO cell) */
transfer_size
=
snd_soc_params_to_frame_size
(
params
)
/
32
;
else
transfer_size
=
params_channels
(
params
)
*
UNIPERIF_FIFO_FRAMES
;
dma_data
=
snd_soc_dai_get_dma_data
(
dai
,
substream
);
dma_data
->
maxburst
=
transfer_size
;
...
...
sound/soc/sti/uniperif.h
View file @
77c92d2b
...
...
@@ -25,7 +25,7 @@
writel_relaxed((((value) & mask) << shift), ip->base + offset)
/*
*
AUD_
UNIPERIF_SOFT_RST reg
* UNIPERIF_SOFT_RST reg
*/
#define UNIPERIF_SOFT_RST_OFFSET(ip) 0x0000
...
...
@@ -50,7 +50,7 @@
UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip))
/*
*
AUD_
UNIPERIF_FIFO_DATA reg
* UNIPERIF_FIFO_DATA reg
*/
#define UNIPERIF_FIFO_DATA_OFFSET(ip) 0x0004
...
...
@@ -58,7 +58,7 @@
writel_relaxed(value, ip->base + UNIPERIF_FIFO_DATA_OFFSET(ip))
/*
*
AUD_
UNIPERIF_CHANNEL_STA_REGN reg
* UNIPERIF_CHANNEL_STA_REGN reg
*/
#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n))
...
...
@@ -105,7 +105,7 @@
writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip))
/*
*
AUD_
UNIPERIF_ITS reg
* UNIPERIF_ITS reg
*/
#define UNIPERIF_ITS_OFFSET(ip) 0x000C
...
...
@@ -143,7 +143,7 @@
0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip))))
/*
*
AUD_
UNIPERIF_ITS_BCLR reg
* UNIPERIF_ITS_BCLR reg
*/
/* FIFO_ERROR */
...
...
@@ -160,7 +160,7 @@
writel_relaxed(value, ip->base + UNIPERIF_ITS_BCLR_OFFSET(ip))
/*
*
AUD_
UNIPERIF_ITM reg
* UNIPERIF_ITM reg
*/
#define UNIPERIF_ITM_OFFSET(ip) 0x0018
...
...
@@ -188,7 +188,7 @@
0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip))))
/*
*
AUD_
UNIPERIF_ITM_BCLR reg
* UNIPERIF_ITM_BCLR reg
*/
#define UNIPERIF_ITM_BCLR_OFFSET(ip) 0x001c
...
...
@@ -213,7 +213,7 @@
UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip))
/*
*
AUD_
UNIPERIF_ITM_BSET reg
* UNIPERIF_ITM_BSET reg
*/
#define UNIPERIF_ITM_BSET_OFFSET(ip) 0x0020
...
...
@@ -767,7 +767,7 @@
SET_UNIPERIF_REG(ip, \
UNIPERIF_CTRL_OFFSET(ip), \
UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \
CORAUD_
UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 1)
UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 1)
/* UNDERFLOW_REC_WINDOW */
#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip) 20
...
...
@@ -1046,7 +1046,7 @@
UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip), value)
/*
*
AUD_
UNIPERIF_CHANNEL_STA_REGN reg
* UNIPERIF_CHANNEL_STA_REGN reg
*/
#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n))
...
...
@@ -1057,7 +1057,7 @@
UNIPERIF_CHANNEL_STA_REGN(ip, n))
/*
*
AUD_
UNIPERIF_USER_VALIDITY reg
* UNIPERIF_USER_VALIDITY reg
*/
#define UNIPERIF_USER_VALIDITY_OFFSET(ip) 0x0090
...
...
@@ -1100,6 +1100,118 @@
UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \
UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip), value)
/*
* UNIPERIF_TDM_ENABLE
*/
#define UNIPERIF_TDM_ENABLE_OFFSET(ip) 0x0118
#define GET_UNIPERIF_TDM_ENABLE(ip) \
readl_relaxed(ip->base + UNIPERIF_TDM_ENABLE_OFFSET(ip))
#define SET_UNIPERIF_TDM_ENABLE(ip, value) \
writel_relaxed(value, ip->base + UNIPERIF_TDM_ENABLE_OFFSET(ip))
/* TDM_ENABLE */
#define UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip) 0x0
#define UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip) 0x1
#define GET_UNIPERIF_TDM_ENABLE_EN_TDM(ip) \
GET_UNIPERIF_REG(ip, \
UNIPERIF_TDM_ENABLE_OFFSET(ip), \
UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip), \
UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip))
#define SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE(ip) \
SET_UNIPERIF_REG(ip, \
UNIPERIF_TDM_ENABLE_OFFSET(ip), \
UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip), \
UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip), 1)
#define SET_UNIPERIF_TDM_ENABLE_TDM_DISABLE(ip) \
SET_UNIPERIF_REG(ip, \
UNIPERIF_TDM_ENABLE_OFFSET(ip), \
UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip), \
UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip), 0)
/*
* UNIPERIF_TDM_FS_REF_FREQ
*/
#define UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip) 0x011c
#define GET_UNIPERIF_TDM_FS_REF_FREQ(ip) \
readl_relaxed(ip->base + UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip))
#define SET_UNIPERIF_TDM_FS_REF_FREQ(ip, value) \
writel_relaxed(value, ip->base + \
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip))
/* REF_FREQ */
#define UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip) 0x0
#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_8KHZ(ip) 0
#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_16KHZ(ip) 1
#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_32KHZ(ip) 2
#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_48KHZ(ip) 3
#define UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip) 0x3
#define GET_UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ(ip) \
GET_UNIPERIF_REG(ip, \
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip))
#define SET_UNIPERIF_TDM_FS_REF_FREQ_8KHZ(ip) \
SET_UNIPERIF_REG(ip, \
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
VALUE_UNIPERIF_TDM_FS_REF_FREQ_8KHZ(ip))
#define SET_UNIPERIF_TDM_FS_REF_FREQ_16KHZ(ip) \
SET_UNIPERIF_REG(ip, \
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
VALUE_UNIPERIF_TDM_FS_REF_FREQ_16KHZ(ip))
#define SET_UNIPERIF_TDM_FS_REF_FREQ_32KHZ(ip) \
SET_UNIPERIF_REG(ip, \
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
VALUE_UNIPERIF_TDM_FS_REF_FREQ_32KHZ(ip))
#define SET_UNIPERIF_TDM_FS_REF_FREQ_48KHZ(ip) \
SET_UNIPERIF_REG(ip, \
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
VALUE_UNIPERIF_TDM_FS_REF_FREQ_48KHZ(ip))
/*
* UNIPERIF_TDM_FS_REF_DIV
*/
#define UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip) 0x0120
#define GET_UNIPERIF_TDM_FS_REF_DIV(ip) \
readl_relaxed(ip->base + UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip))
#define SET_UNIPERIF_TDM_FS_REF_DIV(ip, value) \
writel_relaxed(value, ip->base + \
UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip))
/* NUM_TIMESLOT */
#define UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_SHIFT(ip) 0x0
#define UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_MASK(ip) 0xff
#define GET_UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT(ip) \
GET_UNIPERIF_REG(ip, \
UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip), \
UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_SHIFT(ip), \
UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_MASK(ip))
#define SET_UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT(ip, value) \
SET_UNIPERIF_REG(ip, \
UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip), \
UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_SHIFT(ip), \
UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_MASK(ip), value)
/*
* UNIPERIF_TDM_WORD_POS_X_Y
* 32 bits of UNIPERIF_TDM_WORD_POS_X_Y register shall be set in 1 shot
*/
#define UNIPERIF_TDM_WORD_POS_1_2_OFFSET(ip) 0x013c
#define UNIPERIF_TDM_WORD_POS_3_4_OFFSET(ip) 0x0140
#define UNIPERIF_TDM_WORD_POS_5_6_OFFSET(ip) 0x0144
#define UNIPERIF_TDM_WORD_POS_7_8_OFFSET(ip) 0x0148
#define GET_UNIPERIF_TDM_WORD_POS(ip, words) \
readl_relaxed(ip->base + UNIPERIF_TDM_WORD_POS_##words##_OFFSET(ip))
#define SET_UNIPERIF_TDM_WORD_POS(ip, words, value) \
writel_relaxed(value, ip->base + \
UNIPERIF_TDM_WORD_POS_##words##_OFFSET(ip))
/*
* uniperipheral IP capabilities
*/
...
...
@@ -1107,6 +1219,18 @@
#define UNIPERIF_FIFO_SIZE 70
/* FIFO is 70 cells deep */
#define UNIPERIF_FIFO_FRAMES 4
/* FDMA trigger limit in frames */
#define UNIPERIF_TYPE_IS_HDMI(p) \
((p)->info->type == SND_ST_UNIPERIF_TYPE_HDMI)
#define UNIPERIF_TYPE_IS_PCM(p) \
((p)->info->type == SND_ST_UNIPERIF_TYPE_PCM)
#define UNIPERIF_TYPE_IS_SPDIF(p) \
((p)->info->type == SND_ST_UNIPERIF_TYPE_SPDIF)
#define UNIPERIF_TYPE_IS_IEC958(p) \
(UNIPERIF_TYPE_IS_HDMI(p) || \
UNIPERIF_TYPE_IS_SPDIF(p))
#define UNIPERIF_TYPE_IS_TDM(p) \
((p)->info->type == SND_ST_UNIPERIF_TYPE_TDM)
/*
* Uniperipheral IP revisions
*/
...
...
@@ -1125,10 +1249,11 @@ enum uniperif_version {
};
enum
uniperif_type
{
SND_ST_UNIPERIF_PLAYER_TYPE_NONE
,
SND_ST_UNIPERIF_PLAYER_TYPE_HDMI
,
SND_ST_UNIPERIF_PLAYER_TYPE_PCM
,
SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF
SND_ST_UNIPERIF_TYPE_NONE
,
SND_ST_UNIPERIF_TYPE_HDMI
,
SND_ST_UNIPERIF_TYPE_PCM
,
SND_ST_UNIPERIF_TYPE_SPDIF
,
SND_ST_UNIPERIF_TYPE_TDM
};
enum
uniperif_state
{
...
...
@@ -1145,9 +1270,17 @@ enum uniperif_iec958_encoding_mode {
UNIPERIF_IEC958_ENCODING_MODE_ENCODED
};
enum
uniperif_word_pos
{
WORD_1_2
,
WORD_3_4
,
WORD_5_6
,
WORD_7_8
,
WORD_MAX
};
struct
uniperif_info
{
int
id
;
/* instance value of the uniperipheral IP */
enum
uniperif_type
player_
type
;
enum
uniperif_type
type
;
int
underflow_enabled
;
/* Underflow recovery mode */
};
...
...
@@ -1156,12 +1289,20 @@ struct uniperif_iec958_settings {
struct
snd_aes_iec958
iec958
;
};
struct
dai_tdm_slot
{
unsigned
int
mask
;
int
slots
;
int
slot_width
;
unsigned
int
avail_slots
;
};
struct
uniperif
{
/* System information */
struct
uniperif_info
*
info
;
struct
device
*
dev
;
int
ver
;
/* IP version, used by register access macros */
struct
regmap_field
*
clk_sel
;
struct
regmap_field
*
valid_sel
;
/* capabilities */
const
struct
snd_pcm_hardware
*
hw
;
...
...
@@ -1192,6 +1333,7 @@ struct uniperif {
/* dai properties */
unsigned
int
daifmt
;
struct
dai_tdm_slot
tdm_slot
;
/* DAI callbacks */
const
struct
snd_soc_dai_ops
*
dai_ops
;
...
...
@@ -1209,6 +1351,28 @@ struct sti_uniperiph_data {
struct
sti_uniperiph_dai
dai_data
;
};
static
const
struct
snd_pcm_hardware
uni_tdm_hw
=
{
.
info
=
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_BLOCK_TRANSFER
|
SNDRV_PCM_INFO_PAUSE
|
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
,
.
formats
=
SNDRV_PCM_FMTBIT_S32_LE
|
SNDRV_PCM_FMTBIT_S16_LE
,
.
rates
=
SNDRV_PCM_RATE_CONTINUOUS
,
.
rate_min
=
8000
,
.
rate_max
=
48000
,
.
channels_min
=
1
,
.
channels_max
=
32
,
.
periods_min
=
2
,
.
periods_max
=
10
,
.
period_bytes_min
=
128
,
.
period_bytes_max
=
64
*
PAGE_SIZE
,
.
buffer_bytes_max
=
256
*
PAGE_SIZE
};
/* uniperiph player*/
int
uni_player_init
(
struct
platform_device
*
pdev
,
struct
uniperif
*
uni_player
);
...
...
@@ -1226,4 +1390,28 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
struct
snd_pcm_hw_params
*
params
,
struct
snd_soc_dai
*
dai
);
static
inline
int
sti_uniperiph_get_user_frame_size
(
struct
snd_pcm_runtime
*
runtime
)
{
return
(
runtime
->
channels
*
snd_pcm_format_width
(
runtime
->
format
)
/
8
);
}
static
inline
int
sti_uniperiph_get_unip_tdm_frame_size
(
struct
uniperif
*
uni
)
{
return
(
uni
->
tdm_slot
.
slots
*
uni
->
tdm_slot
.
slot_width
/
8
);
}
int
sti_uniperiph_set_tdm_slot
(
struct
snd_soc_dai
*
dai
,
unsigned
int
tx_mask
,
unsigned
int
rx_mask
,
int
slots
,
int
slot_width
);
int
sti_uniperiph_get_tdm_word_pos
(
struct
uniperif
*
uni
,
unsigned
int
*
word_pos
);
int
sti_uniperiph_fix_tdm_chan
(
struct
snd_pcm_hw_params
*
params
,
struct
snd_pcm_hw_rule
*
rule
);
int
sti_uniperiph_fix_tdm_format
(
struct
snd_pcm_hw_params
*
params
,
struct
snd_pcm_hw_rule
*
rule
);
#endif
sound/soc/sti/uniperif_player.c
View file @
77c92d2b
...
...
@@ -21,23 +21,14 @@
/* sys config registers definitions */
#define SYS_CFG_AUDIO_GLUE 0xA4
#define SYS_CFG_AUDI0_GLUE_PCM_CLKX 8
/*
* Driver specific types.
*/
#define UNIPERIF_PLAYER_TYPE_IS_HDMI(p) \
((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_HDMI)
#define UNIPERIF_PLAYER_TYPE_IS_PCM(p) \
((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_PCM)
#define UNIPERIF_PLAYER_TYPE_IS_SPDIF(p) \
((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF)
#define UNIPERIF_PLAYER_TYPE_IS_IEC958(p) \
(UNIPERIF_PLAYER_TYPE_IS_HDMI(p) || \
UNIPERIF_PLAYER_TYPE_IS_SPDIF(p))
#define UNIPERIF_PLAYER_CLK_ADJ_MIN -999999
#define UNIPERIF_PLAYER_CLK_ADJ_MAX 1000000
#define UNIPERIF_PLAYER_I2S_OUT 1
/* player id connected to I2S/TDM TX bus */
/*
* Note: snd_pcm_hardware is linked to DMA controller but is declared here to
...
...
@@ -444,18 +435,11 @@ static int uni_player_prepare_pcm(struct uniperif *player,
/* Force slot width to 32 in I2S mode (HW constraint) */
if
((
player
->
daifmt
&
SND_SOC_DAIFMT_FORMAT_MASK
)
==
SND_SOC_DAIFMT_I2S
)
{
SND_SOC_DAIFMT_I2S
)
slot_width
=
32
;
}
else
{
switch
(
runtime
->
format
)
{
case
SNDRV_PCM_FORMAT_S16_LE
:
slot_width
=
16
;
break
;
default:
slot_width
=
32
;
break
;
}
}
else
slot_width
=
snd_pcm_format_width
(
runtime
->
format
);
output_frame_size
=
slot_width
*
runtime
->
channels
;
clk_div
=
player
->
mclk
/
runtime
->
rate
;
...
...
@@ -530,7 +514,6 @@ static int uni_player_prepare_pcm(struct uniperif *player,
SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE
(
player
);
SET_UNIPERIF_I2S_FMT_ORDER_MSB
(
player
);
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING
(
player
);
/* No iec958 formatting as outputting to DAC */
SET_UNIPERIF_CTRL_SPDIF_FMT_OFF
(
player
);
...
...
@@ -538,6 +521,55 @@ static int uni_player_prepare_pcm(struct uniperif *player,
return
0
;
}
static
int
uni_player_prepare_tdm
(
struct
uniperif
*
player
,
struct
snd_pcm_runtime
*
runtime
)
{
int
tdm_frame_size
;
/* unip tdm frame size in bytes */
int
user_frame_size
;
/* user tdm frame size in bytes */
/* default unip TDM_WORD_POS_X_Y */
unsigned
int
word_pos
[
4
]
=
{
0x04060002
,
0x0C0E080A
,
0x14161012
,
0x1C1E181A
};
int
freq
,
ret
;
tdm_frame_size
=
sti_uniperiph_get_unip_tdm_frame_size
(
player
);
user_frame_size
=
sti_uniperiph_get_user_frame_size
(
runtime
);
/* fix 16/0 format */
SET_UNIPERIF_CONFIG_MEM_FMT_16_0
(
player
);
SET_UNIPERIF_I2S_FMT_DATA_SIZE_32
(
player
);
/* number of words inserted on the TDM line */
SET_UNIPERIF_I2S_FMT_NUM_CH
(
player
,
user_frame_size
/
4
/
2
);
SET_UNIPERIF_I2S_FMT_ORDER_MSB
(
player
);
SET_UNIPERIF_I2S_FMT_ALIGN_LEFT
(
player
);
/* Enable the tdm functionality */
SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE
(
player
);
/* number of 8 bits timeslots avail in unip tdm frame */
SET_UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT
(
player
,
tdm_frame_size
);
/* set the timeslot allocation for words in FIFO */
sti_uniperiph_get_tdm_word_pos
(
player
,
word_pos
);
SET_UNIPERIF_TDM_WORD_POS
(
player
,
1
_2
,
word_pos
[
WORD_1_2
]);
SET_UNIPERIF_TDM_WORD_POS
(
player
,
3
_4
,
word_pos
[
WORD_3_4
]);
SET_UNIPERIF_TDM_WORD_POS
(
player
,
5
_6
,
word_pos
[
WORD_5_6
]);
SET_UNIPERIF_TDM_WORD_POS
(
player
,
7
_8
,
word_pos
[
WORD_7_8
]);
/* set unip clk rate (not done vai set_sysclk ops) */
freq
=
runtime
->
rate
*
tdm_frame_size
*
8
;
mutex_lock
(
&
player
->
ctrl_lock
);
ret
=
uni_player_clk_set_rate
(
player
,
freq
);
if
(
!
ret
)
player
->
mclk
=
freq
;
mutex_unlock
(
&
player
->
ctrl_lock
);
return
0
;
}
/*
* ALSA uniperipheral iec958 controls
*/
...
...
@@ -668,11 +700,29 @@ static int uni_player_startup(struct snd_pcm_substream *substream,
{
struct
sti_uniperiph_data
*
priv
=
snd_soc_dai_get_drvdata
(
dai
);
struct
uniperif
*
player
=
priv
->
dai_data
.
uni
;
int
ret
;
player
->
substream
=
substream
;
player
->
clk_adj
=
0
;
return
0
;
if
(
!
UNIPERIF_TYPE_IS_TDM
(
player
))
return
0
;
/* refine hw constraint in tdm mode */
ret
=
snd_pcm_hw_rule_add
(
substream
->
runtime
,
0
,
SNDRV_PCM_HW_PARAM_CHANNELS
,
sti_uniperiph_fix_tdm_chan
,
player
,
SNDRV_PCM_HW_PARAM_CHANNELS
,
-
1
);
if
(
ret
<
0
)
return
ret
;
return
snd_pcm_hw_rule_add
(
substream
->
runtime
,
0
,
SNDRV_PCM_HW_PARAM_FORMAT
,
sti_uniperiph_fix_tdm_format
,
player
,
SNDRV_PCM_HW_PARAM_FORMAT
,
-
1
);
}
static
int
uni_player_set_sysclk
(
struct
snd_soc_dai
*
dai
,
int
clk_id
,
...
...
@@ -682,7 +732,7 @@ static int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id,
struct
uniperif
*
player
=
priv
->
dai_data
.
uni
;
int
ret
;
if
(
dir
==
SND_SOC_CLOCK_IN
)
if
(
UNIPERIF_TYPE_IS_TDM
(
player
)
||
(
dir
==
SND_SOC_CLOCK_IN
)
)
return
0
;
if
(
clk_id
!=
0
)
...
...
@@ -714,7 +764,13 @@ static int uni_player_prepare(struct snd_pcm_substream *substream,
}
/* Calculate transfer size (in fifo cells and bytes) for frame count */
transfer_size
=
runtime
->
channels
*
UNIPERIF_FIFO_FRAMES
;
if
(
player
->
info
->
type
==
SND_ST_UNIPERIF_TYPE_TDM
)
{
/* transfer size = user frame size (in 32 bits FIFO cell) */
transfer_size
=
sti_uniperiph_get_user_frame_size
(
runtime
)
/
4
;
}
else
{
transfer_size
=
runtime
->
channels
*
UNIPERIF_FIFO_FRAMES
;
}
/* Calculate number of empty cells available before asserting DREQ */
if
(
player
->
ver
<
SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
)
{
...
...
@@ -738,16 +794,19 @@ static int uni_player_prepare(struct snd_pcm_substream *substream,
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT
(
player
,
trigger_limit
);
/* Uniperipheral setup depends on player type */
switch
(
player
->
info
->
player_
type
)
{
case
SND_ST_UNIPERIF_
PLAYER_
TYPE_HDMI
:
switch
(
player
->
info
->
type
)
{
case
SND_ST_UNIPERIF_TYPE_HDMI
:
ret
=
uni_player_prepare_iec958
(
player
,
runtime
);
break
;
case
SND_ST_UNIPERIF_
PLAYER_
TYPE_PCM
:
case
SND_ST_UNIPERIF_TYPE_PCM
:
ret
=
uni_player_prepare_pcm
(
player
,
runtime
);
break
;
case
SND_ST_UNIPERIF_
PLAYER_
TYPE_SPDIF
:
case
SND_ST_UNIPERIF_TYPE_SPDIF
:
ret
=
uni_player_prepare_iec958
(
player
,
runtime
);
break
;
case
SND_ST_UNIPERIF_TYPE_TDM
:
ret
=
uni_player_prepare_tdm
(
player
,
runtime
);
break
;
default:
dev_err
(
player
->
dev
,
"invalid player type"
);
return
-
EINVAL
;
...
...
@@ -852,8 +911,8 @@ static int uni_player_start(struct uniperif *player)
* will not take affect and hang the player.
*/
if
(
player
->
ver
<
SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
)
if
(
UNIPERIF_
PLAYER_
TYPE_IS_IEC958
(
player
))
SET_UNIPERIF_CTRL_SPDIF_FMT_ON
(
player
);
if
(
UNIPERIF_TYPE_IS_IEC958
(
player
))
SET_UNIPERIF_CTRL_SPDIF_FMT_ON
(
player
);
/* Force channel status update (no update if clk disable) */
if
(
player
->
ver
<
SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
)
...
...
@@ -954,27 +1013,30 @@ static void uni_player_shutdown(struct snd_pcm_substream *substream,
player
->
substream
=
NULL
;
}
static
int
uni_player_parse_dt_
clk
_glue
(
struct
platform_device
*
pdev
,
struct
uniperif
*
player
)
static
int
uni_player_parse_dt_
audio
_glue
(
struct
platform_device
*
pdev
,
struct
uniperif
*
player
)
{
int
bit_offset
;
struct
device_node
*
node
=
pdev
->
dev
.
of_node
;
struct
regmap
*
regmap
;
bit_offset
=
SYS_CFG_AUDI0_GLUE_PCM_CLKX
+
player
->
info
->
id
;
struct
reg_field
regfield
[
2
]
=
{
/* PCM_CLK_SEL */
REG_FIELD
(
SYS_CFG_AUDIO_GLUE
,
8
+
player
->
info
->
id
,
8
+
player
->
info
->
id
),
/* PCMP_VALID_SEL */
REG_FIELD
(
SYS_CFG_AUDIO_GLUE
,
0
,
1
)
};
regmap
=
syscon_regmap_lookup_by_phandle
(
node
,
"st,syscfg"
);
if
(
regmap
)
{
struct
reg_field
regfield
=
REG_FIELD
(
SYS_CFG_AUDIO_GLUE
,
bit_offset
,
bit_offset
);
player
->
clk_sel
=
regmap_field_alloc
(
regmap
,
regfield
);
}
else
{
if
(
!
regmap
)
{
dev_err
(
&
pdev
->
dev
,
"sti-audio-clk-glue syscf not found
\n
"
);
return
-
EINVAL
;
}
player
->
clk_sel
=
regmap_field_alloc
(
regmap
,
regfield
[
0
]);
player
->
valid_sel
=
regmap_field_alloc
(
regmap
,
regfield
[
1
]);
return
0
;
}
...
...
@@ -1012,19 +1074,21 @@ static int uni_player_parse_dt(struct platform_device *pdev,
}
if
(
strcasecmp
(
mode
,
"hdmi"
)
==
0
)
info
->
player_type
=
SND_ST_UNIPERIF_PLAYER
_TYPE_HDMI
;
info
->
type
=
SND_ST_UNIPERIF
_TYPE_HDMI
;
else
if
(
strcasecmp
(
mode
,
"pcm"
)
==
0
)
info
->
player_type
=
SND_ST_UNIPERIF_PLAYER
_TYPE_PCM
;
info
->
type
=
SND_ST_UNIPERIF
_TYPE_PCM
;
else
if
(
strcasecmp
(
mode
,
"spdif"
)
==
0
)
info
->
player_type
=
SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF
;
info
->
type
=
SND_ST_UNIPERIF_TYPE_SPDIF
;
else
if
(
strcasecmp
(
mode
,
"tdm"
)
==
0
)
info
->
type
=
SND_ST_UNIPERIF_TYPE_TDM
;
else
info
->
player_type
=
SND_ST_UNIPERIF_PLAYER
_TYPE_NONE
;
info
->
type
=
SND_ST_UNIPERIF
_TYPE_NONE
;
/* Save the info structure */
player
->
info
=
info
;
/* Get
the PCM_CLK_SEL bit from audio-glue-ctrl SoC register
*/
if
(
uni_player_parse_dt_
clk
_glue
(
pdev
,
player
))
/* Get
PCM_CLK_SEL & PCMP_VALID_SEL from audio-glue-ctrl SoC reg
*/
if
(
uni_player_parse_dt_
audio
_glue
(
pdev
,
player
))
return
-
EINVAL
;
return
0
;
...
...
@@ -1037,7 +1101,8 @@ static const struct snd_soc_dai_ops uni_player_dai_ops = {
.
trigger
=
uni_player_trigger
,
.
hw_params
=
sti_uniperiph_dai_hw_params
,
.
set_fmt
=
sti_uniperiph_dai_set_fmt
,
.
set_sysclk
=
uni_player_set_sysclk
.
set_sysclk
=
uni_player_set_sysclk
,
.
set_tdm_slot
=
sti_uniperiph_set_tdm_slot
};
int
uni_player_init
(
struct
platform_device
*
pdev
,
...
...
@@ -1047,7 +1112,6 @@ int uni_player_init(struct platform_device *pdev,
player
->
dev
=
&
pdev
->
dev
;
player
->
state
=
UNIPERIF_STATE_STOPPED
;
player
->
hw
=
&
uni_player_pcm_hw
;
player
->
dai_ops
=
&
uni_player_dai_ops
;
ret
=
uni_player_parse_dt
(
pdev
,
player
);
...
...
@@ -1057,6 +1121,11 @@ int uni_player_init(struct platform_device *pdev,
return
ret
;
}
if
(
UNIPERIF_TYPE_IS_TDM
(
player
))
player
->
hw
=
&
uni_tdm_hw
;
else
player
->
hw
=
&
uni_player_pcm_hw
;
/* Get uniperif resource */
player
->
clk
=
of_clk_get
(
pdev
->
dev
.
of_node
,
0
);
if
(
IS_ERR
(
player
->
clk
))
...
...
@@ -1073,6 +1142,17 @@ int uni_player_init(struct platform_device *pdev,
}
}
/* connect to I2S/TDM TX bus */
if
(
player
->
valid_sel
&&
(
player
->
info
->
id
==
UNIPERIF_PLAYER_I2S_OUT
))
{
ret
=
regmap_field_write
(
player
->
valid_sel
,
player
->
info
->
id
);
if
(
ret
)
{
dev_err
(
player
->
dev
,
"%s: unable to connect to tdm bus"
,
__func__
);
return
ret
;
}
}
ret
=
devm_request_irq
(
&
pdev
->
dev
,
player
->
irq
,
uni_player_irq_handler
,
IRQF_SHARED
,
dev_name
(
&
pdev
->
dev
),
player
);
...
...
@@ -1087,7 +1167,7 @@ int uni_player_init(struct platform_device *pdev,
SET_UNIPERIF_CTRL_SPDIF_LAT_OFF
(
player
);
SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE
(
player
);
if
(
UNIPERIF_
PLAYER_
TYPE_IS_IEC958
(
player
))
{
if
(
UNIPERIF_TYPE_IS_IEC958
(
player
))
{
/* Set default iec958 status bits */
/* Consumer, PCM, copyright, 2ch, mode 0 */
...
...
sound/soc/sti/uniperif_reader.c
View file @
77c92d2b
...
...
@@ -73,55 +73,10 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id)
return
ret
;
}
static
int
uni_reader_prepare
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
static
int
uni_reader_prepare
_pcm
(
struct
snd_pcm_runtime
*
runtime
,
struct
uniperif
*
reader
)
{
struct
sti_uniperiph_data
*
priv
=
snd_soc_dai_get_drvdata
(
dai
);
struct
uniperif
*
reader
=
priv
->
dai_data
.
uni
;
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
int
transfer_size
,
trigger_limit
;
int
slot_width
;
int
count
=
10
;
/* The reader should be stopped */
if
(
reader
->
state
!=
UNIPERIF_STATE_STOPPED
)
{
dev_err
(
reader
->
dev
,
"%s: invalid reader state %d"
,
__func__
,
reader
->
state
);
return
-
EINVAL
;
}
/* Calculate transfer size (in fifo cells and bytes) for frame count */
transfer_size
=
runtime
->
channels
*
UNIPERIF_FIFO_FRAMES
;
/* Calculate number of empty cells available before asserting DREQ */
if
(
reader
->
ver
<
SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
)
trigger_limit
=
UNIPERIF_FIFO_SIZE
-
transfer_size
;
else
/*
* Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
* FDMA_TRIGGER_LIMIT also controls when the state switches
* from OFF or STANDBY to AUDIO DATA.
*/
trigger_limit
=
transfer_size
;
/* Trigger limit must be an even number */
if
((
!
trigger_limit
%
2
)
||
(
trigger_limit
!=
1
&&
transfer_size
%
2
)
||
(
trigger_limit
>
UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK
(
reader
)))
{
dev_err
(
reader
->
dev
,
"invalid trigger limit %d"
,
trigger_limit
);
return
-
EINVAL
;
}
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT
(
reader
,
trigger_limit
);
switch
(
reader
->
daifmt
&
SND_SOC_DAIFMT_INV_MASK
)
{
case
SND_SOC_DAIFMT_IB_IF
:
case
SND_SOC_DAIFMT_NB_IF
:
SET_UNIPERIF_I2S_FMT_LR_POL_HIG
(
reader
);
break
;
default:
SET_UNIPERIF_I2S_FMT_LR_POL_LOW
(
reader
);
}
/* Force slot width to 32 in I2S mode */
if
((
reader
->
daifmt
&
SND_SOC_DAIFMT_FORMAT_MASK
)
...
...
@@ -173,6 +128,109 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
return
-
EINVAL
;
}
/* Number of channels must be even */
if
((
runtime
->
channels
%
2
)
||
(
runtime
->
channels
<
2
)
||
(
runtime
->
channels
>
10
))
{
dev_err
(
reader
->
dev
,
"%s: invalid nb of channels"
,
__func__
);
return
-
EINVAL
;
}
SET_UNIPERIF_I2S_FMT_NUM_CH
(
reader
,
runtime
->
channels
/
2
);
SET_UNIPERIF_I2S_FMT_ORDER_MSB
(
reader
);
return
0
;
}
static
int
uni_reader_prepare_tdm
(
struct
snd_pcm_runtime
*
runtime
,
struct
uniperif
*
reader
)
{
int
frame_size
;
/* user tdm frame size in bytes */
/* default unip TDM_WORD_POS_X_Y */
unsigned
int
word_pos
[
4
]
=
{
0x04060002
,
0x0C0E080A
,
0x14161012
,
0x1C1E181A
};
frame_size
=
sti_uniperiph_get_user_frame_size
(
runtime
);
/* fix 16/0 format */
SET_UNIPERIF_CONFIG_MEM_FMT_16_0
(
reader
);
SET_UNIPERIF_I2S_FMT_DATA_SIZE_32
(
reader
);
/* number of words inserted on the TDM line */
SET_UNIPERIF_I2S_FMT_NUM_CH
(
reader
,
frame_size
/
4
/
2
);
SET_UNIPERIF_I2S_FMT_ORDER_MSB
(
reader
);
SET_UNIPERIF_I2S_FMT_ALIGN_LEFT
(
reader
);
SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE
(
reader
);
/*
* set the timeslots allocation for words in FIFO
*
* HW bug: (LSB word < MSB word) => this config is not possible
* So if we want (LSB word < MSB) word, then it shall be
* handled by user
*/
sti_uniperiph_get_tdm_word_pos
(
reader
,
word_pos
);
SET_UNIPERIF_TDM_WORD_POS
(
reader
,
1
_2
,
word_pos
[
WORD_1_2
]);
SET_UNIPERIF_TDM_WORD_POS
(
reader
,
3
_4
,
word_pos
[
WORD_3_4
]);
SET_UNIPERIF_TDM_WORD_POS
(
reader
,
5
_6
,
word_pos
[
WORD_5_6
]);
SET_UNIPERIF_TDM_WORD_POS
(
reader
,
7
_8
,
word_pos
[
WORD_7_8
]);
return
0
;
}
static
int
uni_reader_prepare
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
struct
sti_uniperiph_data
*
priv
=
snd_soc_dai_get_drvdata
(
dai
);
struct
uniperif
*
reader
=
priv
->
dai_data
.
uni
;
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
int
transfer_size
,
trigger_limit
,
ret
;
int
count
=
10
;
/* The reader should be stopped */
if
(
reader
->
state
!=
UNIPERIF_STATE_STOPPED
)
{
dev_err
(
reader
->
dev
,
"%s: invalid reader state %d"
,
__func__
,
reader
->
state
);
return
-
EINVAL
;
}
/* Calculate transfer size (in fifo cells and bytes) for frame count */
if
(
reader
->
info
->
type
==
SND_ST_UNIPERIF_TYPE_TDM
)
{
/* transfer size = unip frame size (in 32 bits FIFO cell) */
transfer_size
=
sti_uniperiph_get_user_frame_size
(
runtime
)
/
4
;
}
else
{
transfer_size
=
runtime
->
channels
*
UNIPERIF_FIFO_FRAMES
;
}
/* Calculate number of empty cells available before asserting DREQ */
if
(
reader
->
ver
<
SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
)
trigger_limit
=
UNIPERIF_FIFO_SIZE
-
transfer_size
;
else
/*
* Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
* FDMA_TRIGGER_LIMIT also controls when the state switches
* from OFF or STANDBY to AUDIO DATA.
*/
trigger_limit
=
transfer_size
;
/* Trigger limit must be an even number */
if
((
!
trigger_limit
%
2
)
||
(
trigger_limit
!=
1
&&
transfer_size
%
2
)
||
(
trigger_limit
>
UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK
(
reader
)))
{
dev_err
(
reader
->
dev
,
"invalid trigger limit %d"
,
trigger_limit
);
return
-
EINVAL
;
}
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT
(
reader
,
trigger_limit
);
if
(
UNIPERIF_TYPE_IS_TDM
(
reader
))
ret
=
uni_reader_prepare_tdm
(
runtime
,
reader
);
else
ret
=
uni_reader_prepare_pcm
(
runtime
,
reader
);
if
(
ret
)
return
ret
;
switch
(
reader
->
daifmt
&
SND_SOC_DAIFMT_FORMAT_MASK
)
{
case
SND_SOC_DAIFMT_I2S
:
SET_UNIPERIF_I2S_FMT_ALIGN_LEFT
(
reader
);
...
...
@@ -191,21 +249,26 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
return
-
EINVAL
;
}
SET_UNIPERIF_I2S_FMT_ORDER_MSB
(
reader
);
/* Data clocking (changing) on the rising edge */
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING
(
reader
);
/* Number of channels must be even */
if
((
runtime
->
channels
%
2
)
||
(
runtime
->
channels
<
2
)
||
(
runtime
->
channels
>
10
))
{
dev_err
(
reader
->
dev
,
"%s: invalid nb of channels"
,
__func__
);
return
-
EINVAL
;
/* Data clocking (changing) on the rising/falling edge */
switch
(
reader
->
daifmt
&
SND_SOC_DAIFMT_INV_MASK
)
{
case
SND_SOC_DAIFMT_NB_NF
:
SET_UNIPERIF_I2S_FMT_LR_POL_LOW
(
reader
);
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING
(
reader
);
break
;
case
SND_SOC_DAIFMT_NB_IF
:
SET_UNIPERIF_I2S_FMT_LR_POL_HIG
(
reader
);
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING
(
reader
);
break
;
case
SND_SOC_DAIFMT_IB_NF
:
SET_UNIPERIF_I2S_FMT_LR_POL_LOW
(
reader
);
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING
(
reader
);
break
;
case
SND_SOC_DAIFMT_IB_IF
:
SET_UNIPERIF_I2S_FMT_LR_POL_HIG
(
reader
);
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING
(
reader
);
break
;
}
SET_UNIPERIF_I2S_FMT_NUM_CH
(
reader
,
runtime
->
channels
/
2
);
/* Clear any pending interrupts */
SET_UNIPERIF_ITS_BCLR
(
reader
,
GET_UNIPERIF_ITS
(
reader
));
...
...
@@ -293,6 +356,32 @@ static int uni_reader_trigger(struct snd_pcm_substream *substream,
}
}
static
int
uni_reader_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
struct
sti_uniperiph_data
*
priv
=
snd_soc_dai_get_drvdata
(
dai
);
struct
uniperif
*
reader
=
priv
->
dai_data
.
uni
;
int
ret
;
if
(
!
UNIPERIF_TYPE_IS_TDM
(
reader
))
return
0
;
/* refine hw constraint in tdm mode */
ret
=
snd_pcm_hw_rule_add
(
substream
->
runtime
,
0
,
SNDRV_PCM_HW_PARAM_CHANNELS
,
sti_uniperiph_fix_tdm_chan
,
reader
,
SNDRV_PCM_HW_PARAM_CHANNELS
,
-
1
);
if
(
ret
<
0
)
return
ret
;
return
snd_pcm_hw_rule_add
(
substream
->
runtime
,
0
,
SNDRV_PCM_HW_PARAM_FORMAT
,
sti_uniperiph_fix_tdm_format
,
reader
,
SNDRV_PCM_HW_PARAM_FORMAT
,
-
1
);
}
static
void
uni_reader_shutdown
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
...
...
@@ -310,6 +399,7 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
{
struct
uniperif_info
*
info
;
struct
device_node
*
node
=
pdev
->
dev
.
of_node
;
const
char
*
mode
;
/* Allocate memory for the info structure */
info
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
info
),
GFP_KERNEL
);
...
...
@@ -322,6 +412,17 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
return
-
EINVAL
;
}
/* Read the device mode property */
if
(
of_property_read_string
(
node
,
"st,mode"
,
&
mode
))
{
dev_err
(
&
pdev
->
dev
,
"uniperipheral mode not defined"
);
return
-
EINVAL
;
}
if
(
strcasecmp
(
mode
,
"tdm"
)
==
0
)
info
->
type
=
SND_ST_UNIPERIF_TYPE_TDM
;
else
info
->
type
=
SND_ST_UNIPERIF_TYPE_PCM
;
/* Save the info structure */
reader
->
info
=
info
;
...
...
@@ -329,11 +430,13 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
}
static
const
struct
snd_soc_dai_ops
uni_reader_dai_ops
=
{
.
startup
=
uni_reader_startup
,
.
shutdown
=
uni_reader_shutdown
,
.
prepare
=
uni_reader_prepare
,
.
trigger
=
uni_reader_trigger
,
.
hw_params
=
sti_uniperiph_dai_hw_params
,
.
set_fmt
=
sti_uniperiph_dai_set_fmt
,
.
set_tdm_slot
=
sti_uniperiph_set_tdm_slot
};
int
uni_reader_init
(
struct
platform_device
*
pdev
,
...
...
@@ -343,7 +446,6 @@ int uni_reader_init(struct platform_device *pdev,
reader
->
dev
=
&
pdev
->
dev
;
reader
->
state
=
UNIPERIF_STATE_STOPPED
;
reader
->
hw
=
&
uni_reader_pcm_hw
;
reader
->
dai_ops
=
&
uni_reader_dai_ops
;
ret
=
uni_reader_parse_dt
(
pdev
,
reader
);
...
...
@@ -352,6 +454,11 @@ int uni_reader_init(struct platform_device *pdev,
return
ret
;
}
if
(
UNIPERIF_TYPE_IS_TDM
(
reader
))
reader
->
hw
=
&
uni_tdm_hw
;
else
reader
->
hw
=
&
uni_reader_pcm_hw
;
ret
=
devm_request_irq
(
&
pdev
->
dev
,
reader
->
irq
,
uni_reader_irq_handler
,
IRQF_SHARED
,
dev_name
(
&
pdev
->
dev
),
reader
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment