mirror of
https://github.com/brain-hackers/linux-brain.git
synced 2024-06-09 23:36:23 +09:00
Merge branch 'audio/next' into next
* audio/next: (528 commits) LF-276: ASoC: fsl_easi: constrain period size for edma case LF-215: ASoC: fsl_rpmsg_i2s: Enable WQ_FREEZABLE for workqueue ASoC: SOF: Read tplg filename from board descriptor ASoC: SOF: Update fw_filename from board description ASoC: SOF: Allow probe to continue when we have an actual codec ...
This commit is contained in:
commit
45efd194bf
|
@ -8,7 +8,8 @@ three substreams within totally 10 channels.
|
|||
|
||||
Required properties:
|
||||
|
||||
- compatible : Contains "fsl,imx35-asrc" or "fsl,imx53-asrc".
|
||||
- compatible : Contains "fsl,imx35-asrc", "fsl,imx53-asrc",
|
||||
"fsl,imx8qm-asrc0" or "fsl,imx8qm-asrc1".
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
|
|
16
Documentation/devicetree/bindings/sound/fsl,dsp.txt
Normal file
16
Documentation/devicetree/bindings/sound/fsl,dsp.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
NXP DSP
|
||||
|
||||
The IP is from Cadence.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Contains "fsl,imx8qxp-dsp".
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
Example:
|
||||
|
||||
dsp: dsp@596e8000 {
|
||||
compatible = "fsl,imx8qxp-dsp";
|
||||
reg = <0x0 0x596e8000 0x0 0x88000>;
|
||||
status = "okay";
|
||||
};
|
53
Documentation/devicetree/bindings/sound/fsl,easrc.txt
Normal file
53
Documentation/devicetree/bindings/sound/fsl,easrc.txt
Normal file
|
@ -0,0 +1,53 @@
|
|||
Freescale Asynchronous Sample Rate Converter (ASRC) Controller
|
||||
|
||||
The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
|
||||
signal associated with an input clock into a signal associated with a different
|
||||
output clock. The driver currently works as a Front End of DPCM with other Back
|
||||
Ends Audio controller such as ESAI, SSI and SAI. It has four context to support
|
||||
four substreams within totally 32 channels.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Contains "fsl,imx8mn-easrc".
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
- interrupts : Contains the asrc interrupt.
|
||||
|
||||
- dmas : Generic dma devicetree binding as described in
|
||||
Documentation/devicetree/bindings/dma/dma.txt.
|
||||
|
||||
- dma-names : Contains "ctx0_rx", "ctx0_tx", "ctx1_rx", "ctx1_tx",
|
||||
"ctx2_rx", "ctx2_tx", "ctx3_rx", "ctx3_tx".
|
||||
|
||||
- clocks : Contains an entry for each entry in clock-names.
|
||||
|
||||
- clock-names : Contains the following entries
|
||||
"mem" Peripheral clock to driver module.
|
||||
|
||||
- fsl,easrc-ram-script-name : The coefficient table for the filters
|
||||
- fsl,asrc-rate : Defines a mutual sample rate used by DPCM Back Ends.
|
||||
|
||||
- fsl,asrc-width : Defines a mutual sample width used by DPCM Back Ends.
|
||||
|
||||
Example:
|
||||
|
||||
easrc: easrc@300C0000 {
|
||||
compatible = "fsl,imx8mn-easrc";
|
||||
reg = <0x0 0x300C0000 0x0 0x10000>;
|
||||
interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk IMX8MN_CLK_ASRC_ROOT>;
|
||||
clock-names = "mem";
|
||||
dmas = <&sdma2 16 23 0> , <&sdma2 17 23 0>,
|
||||
<&sdma2 18 23 0> , <&sdma2 19 23 0>,
|
||||
<&sdma2 20 23 0> , <&sdma2 21 23 0>,
|
||||
<&sdma2 22 23 0> , <&sdma2 23 23 0>;
|
||||
dma-names = "ctx0_rx", "ctx0_tx",
|
||||
"ctx1_rx", "ctx1_tx",
|
||||
"ctx2_rx", "ctx2_tx",
|
||||
"ctx3_rx", "ctx3_tx";
|
||||
fsl,easrc-ram-script-name = "imx/easrc/easrc-imx8mn.bin";
|
||||
fsl,asrc-rate = <8000>;
|
||||
fsl,asrc-width = <16>;
|
||||
status = "disabled";
|
||||
};
|
23
Documentation/devicetree/bindings/sound/fsl,mqs.txt
Normal file
23
Documentation/devicetree/bindings/sound/fsl,mqs.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
fsl,mqs audio CODEC
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : must contain one of "fsl,imx6sx-mqs", "fsl,codec-mqs"
|
||||
"fsl,imx8qm-mqs".
|
||||
|
||||
- clocks : a list of phandles + clock-specifiers, one for each entry in
|
||||
clock-names
|
||||
|
||||
- clock-names : must contain "mclk"
|
||||
|
||||
- gpr : the gpr node.
|
||||
|
||||
Example:
|
||||
|
||||
mqs: mqs {
|
||||
compatible = "fsl,imx6sx-mqs";
|
||||
gpr = <&gpr>;
|
||||
clocks = <&clks IMX6SX_CLK_SAI1>;
|
||||
clock-names = "mclk";
|
||||
status = "disabled";
|
||||
};
|
22
Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt
Normal file
22
Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
Freescale rpmsg i2s interface.
|
||||
|
||||
The rpmsg i2s is based on RPMSG that used communicating with M4 core,
|
||||
which provides a synchronous audio interface that supports fullduplex
|
||||
serial interfaces with frame synchronization such as I2S.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Compatible list, contains "fsl,imx7ulp-rpmsg-i2s".
|
||||
"fsl,imx8mq-rpmsg-i2s", "fsl,imx8qxp-rpmsg-i2s"
|
||||
"fsl,imx8qm-rpmsg-i2s"
|
||||
|
||||
- fsl,audioindex : This is an index indicating the audio device index in
|
||||
the M4 side.
|
||||
|
||||
Example:
|
||||
rpmsg_i2s: rpmsg-i2s {
|
||||
compatible = "fsl,imx7ulp-rpmsg-i2s";
|
||||
/* the audio device index in m4 domain */
|
||||
fsl,audioindex = <0> ;
|
||||
status = "okay";
|
||||
};
|
|
@ -6,7 +6,9 @@ a fibre cable.
|
|||
|
||||
Required properties:
|
||||
|
||||
- compatible : Compatible list, must contain "fsl,imx35-spdif".
|
||||
- compatible : Compatible list, must contain "fsl,imx35-spdif",
|
||||
"fsl,vf610-spdif", "fsl,imx8qm-spdif",
|
||||
"fsl,imx8mm-spdif"
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ The compatible list for this generic sound card currently:
|
|||
(compatible with CS4271 and CS4272)
|
||||
|
||||
"fsl,imx-audio-wm8962"
|
||||
(compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt)
|
||||
|
||||
"fsl,imx-audio-sgtl5000"
|
||||
(compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt)
|
||||
|
|
30
Documentation/devicetree/bindings/sound/imx-audio-ak4458.txt
Normal file
30
Documentation/devicetree/bindings/sound/imx-audio-ak4458.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
Freescale i.MX audio complex with AK4458 DAC
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "fsl,imx-audio-ak4458", "fsl,imx-audio-ak4458-mq"
|
||||
- model : The user-visible name of this sound complex
|
||||
- audio-cpu : The phandle of CPU DAI
|
||||
- audio-codec : The phandle of the AK4458 audio DAC
|
||||
- audio-routing : A list of the connections between audio components. Each entry
|
||||
is a pair of strings, the first being the connection's sink, the second being
|
||||
the connection's source. Valid names could be power supplies, AK4458 pins,
|
||||
and the jacks on the board.
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "fsl,imx-audio-ak4458";
|
||||
model = "ak4458-audio";
|
||||
audio-cpu = <&sai1>;
|
||||
audio-codec = <&codec>;
|
||||
audio-routing =
|
||||
"AOUTL1", "Playback",
|
||||
"AOUTR1", "Playback",
|
||||
"AOUTL2", "Playback",
|
||||
"AOUTR2", "Playback",
|
||||
"AOUTL3", "Playback",
|
||||
"AOUTR3", "Playback",
|
||||
"AOUTL4", "Playback",
|
||||
"AOUTR4", "Playback";
|
||||
};
|
27
Documentation/devicetree/bindings/sound/imx-audio-ak4497.txt
Normal file
27
Documentation/devicetree/bindings/sound/imx-audio-ak4497.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
Freescale i.MX audio complex with AK4497 DAC
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "fsl,imx-audio-ak4497", "fsl,imx-audio-ak4497-mq"
|
||||
- model : The user-visible name of this sound complex
|
||||
- audio-cpu : The phandle of CPU DAI
|
||||
- audio-codec : The phandle of the ak4497 audio DAC
|
||||
- audio-routing : A list of the connections between audio components. Each entry
|
||||
is a pair of strings, the first being the connection's sink, the second being
|
||||
the connection's source. Valid names could be power supplies, ak4497 pins,
|
||||
and the jacks on the board.
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "fsl,imx-audio-ak4497";
|
||||
model = "ak4497-audio";
|
||||
audio-cpu = <&sai3>;
|
||||
audio-codec = <&codec>;
|
||||
audio-routing =
|
||||
"AOUTLN", "Playback",
|
||||
"AOUTLP", "Playback",
|
||||
"AOUTRN", "Playback",
|
||||
"AOUTRP", "Playback",
|
||||
};
|
||||
|
30
Documentation/devicetree/bindings/sound/imx-audio-ak5558.txt
Normal file
30
Documentation/devicetree/bindings/sound/imx-audio-ak5558.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
Freescale i.MX audio complex with AK5558 ADC
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "fsl,imx-audio-ak5558", "fsl,imx-audio-ak5558-mq"
|
||||
- model : The user-visible name of this sound complex
|
||||
- audio-cpu : The phandle of CPU DAI
|
||||
- audio-codec : The phandle of the AK5558 audio ADC
|
||||
- audio-routing : A list of the connections between audio components. Each entry
|
||||
is a pair of strings, the first being the connection's sink, the second being
|
||||
the connection's source. Valid names could be power supplies, AK5558 pins,
|
||||
and the jacks on the board.
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "fsl,imx-audio-ak5558";
|
||||
model = "ak5558-audio";
|
||||
audio-cpu = <&sai1>;
|
||||
audio-codec = <&codec>;
|
||||
audio-routing =
|
||||
"AIN1", "Capture",
|
||||
"AIN2", "Capture",
|
||||
"AIN3", "Capture",
|
||||
"AIN4", "Capture",
|
||||
"AIN5", "Capture",
|
||||
"AIN6", "Capture",
|
||||
"AIN7", "Capture",
|
||||
"AIN8", "Capture";
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
Freescale i.MX audio complex with Cadence HDMI
|
||||
|
||||
Required properties:
|
||||
- compatible : "fsl,imx-audio-cdnhdmi", "fsl,imx8mq-evk-cdnhdmi"
|
||||
- model : The user-visible name of this sound complex
|
||||
- audio-cpu : The phandle of the i.MX SAI controller
|
||||
- protocol : 0 is hdmi, 1 is dp.
|
||||
|
||||
Example:
|
||||
|
||||
sound-hdmi {
|
||||
compatible = "fsl,imx-audio-cdnhdmi";
|
||||
model = "imx-audio-hdmi";
|
||||
audio-cpu = <&sai4>;
|
||||
protocol = <0>;
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
Freescale i.MX audio complex with CS42888 codec
|
||||
|
||||
Required properties:
|
||||
- compatible : "fsl,imx-audio-cs42888"
|
||||
- model : The user-visible name of this sound complex
|
||||
- esai-controller : The phandle of the i.MX SSI controller
|
||||
- audio-codec : The phandle of the CS42888 audio codec
|
||||
|
||||
Optional properties:
|
||||
- asrc-controller : The phandle of the i.MX ASRC controller
|
||||
- audio-routing : A list of the connections between audio components.
|
||||
Each entry is a pair of strings, the first being the connection's sink,
|
||||
the second being the connection's source. Valid names could be power
|
||||
supplies, CS42888 pins, and the jacks on the board:
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "fsl,imx6q-sabresd-wm8962",
|
||||
"fsl,imx-audio-wm8962";
|
||||
model = "cs42888-audio";
|
||||
esai-controller = <&esai>;
|
||||
asrc-controller = <&asrc_p2p>;
|
||||
audio-codec = <&codec>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/imx-audio-mqs.txt
Normal file
18
Documentation/devicetree/bindings/sound/imx-audio-mqs.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
Freescale i.MX audio complex with mqs codec
|
||||
|
||||
Required properties:
|
||||
- compatible : "fsl,imx-audio-mqs", "fsl,imx8qm-lpddr4-arm2-mqs".
|
||||
- model : The user-visible name of this sound complex
|
||||
- cpu-dai : The phandle of the i.MX sai controller
|
||||
- audio-codec : The phandle of the mqs audio codec
|
||||
|
||||
Example:
|
||||
|
||||
sound-mqs {
|
||||
compatible = "fsl,imx6sx-sdb-mqs",
|
||||
"fsl,imx-audio-mqs";
|
||||
model = "mqs-audio";
|
||||
cpu-dai = <&sai1>;
|
||||
audio-codec = <&mqs>;
|
||||
};
|
||||
|
13
Documentation/devicetree/bindings/sound/imx-audio-rpmsg.txt
Normal file
13
Documentation/devicetree/bindings/sound/imx-audio-rpmsg.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
Freescale i.MX audio complex with rpmsg devices
|
||||
|
||||
Required properties:
|
||||
- compatible : "fsl,imx-audio-rpmsg"
|
||||
- model : The user-visible name of this sound complex
|
||||
- cpu-dai : The phandle of the i.MX rpmsg i2s device.
|
||||
|
||||
Example:
|
||||
sound-rpmsg {
|
||||
compatible = "fsl,imx-audio-rpmsg";
|
||||
model = "rpmsg-audio";
|
||||
cpu-dai = <&rpmsg_i2s>;
|
||||
};
|
24
Documentation/devicetree/bindings/sound/imx-audio-si476x.txt
Normal file
24
Documentation/devicetree/bindings/sound/imx-audio-si476x.txt
Normal file
|
@ -0,0 +1,24 @@
|
|||
Freescale i.MX audio complex with si476x codec
|
||||
|
||||
Required properties:
|
||||
- compatible : "fsl,imx-audio-si476x"
|
||||
- model : The user-visible name of this sound complex
|
||||
- ssi-controller : The phandle of the i.MX SSI controller
|
||||
|
||||
- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
|
||||
- mux-ext-port : The external port of the i.MX audio muxer
|
||||
|
||||
Note: The AUDMUX port numbering should start at 1, which is consistent with
|
||||
hardware manual.
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "fsl,imx-audio-si476x",
|
||||
"fsl,imx-tuner-si476x";
|
||||
model = "imx-radio-si476x";
|
||||
|
||||
ssi-controller = <&ssi1>;
|
||||
mux-int-port = <2>;
|
||||
mux-ext-port = <5>;
|
||||
};
|
29
Documentation/devicetree/bindings/sound/imx-audio-wm8524.txt
Normal file
29
Documentation/devicetree/bindings/sound/imx-audio-wm8524.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
Freescale i.MX audio complex with WM8524 codec
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "fsl,imx-audio-wm8524"
|
||||
|
||||
- model : The user-visible name of this sound complex
|
||||
|
||||
- audio-cpu : The phandle of CPU DAI
|
||||
|
||||
- audio-codec : The phandle of the WM8962 audio codec
|
||||
|
||||
- audio-routing : A list of the connections between audio components.
|
||||
Each entry is a pair of strings, the first being the
|
||||
connection's sink, the second being the connection's
|
||||
source. Valid names could be power supplies, WM8524
|
||||
pins, and the jacks on the board:
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "fsl,imx-audio-wm8524";
|
||||
model = "wm8524-audio";
|
||||
audio-cpu = <&sai2>;
|
||||
audio-codec = <&codec>;
|
||||
audio-routing =
|
||||
"Line Out Jack", "LINEVOUTL",
|
||||
"Line Out Jack", "LINEVOUTR";
|
||||
};
|
61
Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt
Normal file
61
Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt
Normal file
|
@ -0,0 +1,61 @@
|
|||
Freescale i.MX audio complex with WM8962 codec
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "fsl,imx-audio-wm8962"
|
||||
|
||||
- model : The user-visible name of this sound complex
|
||||
|
||||
- cpu-dai : The phandle of CPU DAI
|
||||
|
||||
- audio-codec : The phandle of the WM8962 audio codec
|
||||
|
||||
- audio-routing : A list of the connections between audio components.
|
||||
Each entry is a pair of strings, the first being the
|
||||
connection's sink, the second being the connection's
|
||||
source. Valid names could be power supplies, WM8962
|
||||
pins, and the jacks on the board:
|
||||
|
||||
Power supplies:
|
||||
* Mic Bias
|
||||
|
||||
Board connectors:
|
||||
* Mic Jack
|
||||
* Headphone Jack
|
||||
* Ext Spk
|
||||
|
||||
- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
|
||||
|
||||
- mux-ext-port : The external port of the i.MX audio muxer
|
||||
|
||||
Note: The AUDMUX port numbering should start at 1, which is consistent with
|
||||
hardware manual.
|
||||
|
||||
Optional properties:
|
||||
- hp-det-gpios : The gpio pin to detect plug in/out event that happens to
|
||||
Headphone jack.
|
||||
- mic-det-gpios: The gpio pin to detect plug in/out event that happens to
|
||||
Microphone jack.
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "fsl,imx6q-sabresd-wm8962",
|
||||
"fsl,imx-audio-wm8962";
|
||||
model = "wm8962-audio";
|
||||
cpu-dai = <&ssi2>;
|
||||
audio-codec = <&codec>;
|
||||
audio-routing =
|
||||
"Headphone Jack", "HPOUTL",
|
||||
"Headphone Jack", "HPOUTR",
|
||||
"Ext Spk", "SPKOUTL",
|
||||
"Ext Spk", "SPKOUTR",
|
||||
"MICBIAS", "AMIC",
|
||||
"IN3R", "MICBIAS",
|
||||
"DMIC", "MICBIAS",
|
||||
"DMICDAT", "DMIC";
|
||||
mux-int-port = <2>;
|
||||
mux-ext-port = <3>;
|
||||
hp-det-gpios = <&gpio7 8 1>;
|
||||
mic-det-gpios = <&gpio1 9 1>;
|
||||
};
|
|
@ -13,6 +13,14 @@ Optional properties:
|
|||
of R51 (Class D Control 2) gets set, indicating that the speaker is
|
||||
in mono mode.
|
||||
|
||||
- amic-mono: This is a boolean property. If present, indicating that the
|
||||
analog micphone is hardware mono input, the driver would enable monomix
|
||||
for it.
|
||||
|
||||
- dmic-mono: This is a boolean property. If present, indicating that the
|
||||
digital micphone is hardware mono input, the driver would enable monomix
|
||||
for it.
|
||||
|
||||
- mic-cfg : Default register value for R48 (Additional Control 4).
|
||||
If absent, the default should be the register default.
|
||||
|
||||
|
|
|
@ -988,6 +988,14 @@ static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl)
|
|||
}
|
||||
break;
|
||||
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
if (ctrl->val)
|
||||
retval = regmap_write(radio->core->regmap,
|
||||
SI476X_PROP_AUDIO_MUTE, 3);
|
||||
else
|
||||
retval = regmap_write(radio->core->regmap,
|
||||
SI476X_PROP_AUDIO_MUTE, 0);
|
||||
break;
|
||||
default:
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
|
@ -1515,6 +1523,16 @@ static int si476x_radio_probe(struct platform_device *pdev)
|
|||
goto exit;
|
||||
}
|
||||
|
||||
ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &si476x_ctrl_ops,
|
||||
V4L2_CID_AUDIO_MUTE,
|
||||
0, 1, 1, 0);
|
||||
rval = radio->ctrl_handler.error;
|
||||
if (ctrl == NULL && rval) {
|
||||
dev_err(&pdev->dev, "Could not initialize V4L2_CID_AUDIO_MUTE control %d\n",
|
||||
rval);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (si476x_core_has_diversity(radio->core)) {
|
||||
si476x_ctrls[SI476X_IDX_DIVERSITY_MODE].def =
|
||||
si476x_phase_diversity_mode_to_idx(radio->core->diversity_mode);
|
||||
|
|
|
@ -97,7 +97,7 @@ static int si476x_core_config_pinmux(struct si476x_core *core)
|
|||
|
||||
static inline void si476x_core_schedule_polling_work(struct si476x_core *core)
|
||||
{
|
||||
schedule_delayed_work(&core->status_monitor,
|
||||
queue_delayed_work(system_freezable_wq, &core->status_monitor,
|
||||
usecs_to_jiffies(SI476X_STATUS_POLL_US));
|
||||
}
|
||||
|
||||
|
@ -294,7 +294,7 @@ int si476x_core_set_power_state(struct si476x_core *core,
|
|||
*/
|
||||
udelay(100);
|
||||
|
||||
err = si476x_core_start(core, false);
|
||||
err = si476x_core_start(core, true);
|
||||
if (err < 0)
|
||||
goto disable_regulators;
|
||||
|
||||
|
@ -303,7 +303,7 @@ int si476x_core_set_power_state(struct si476x_core *core,
|
|||
|
||||
case SI476X_POWER_DOWN:
|
||||
core->power_state = next_state;
|
||||
err = si476x_core_stop(core, false);
|
||||
err = si476x_core_stop(core, true);
|
||||
if (err < 0)
|
||||
core->power_state = SI476X_POWER_INCONSISTENT;
|
||||
disable_regulators:
|
||||
|
@ -729,8 +729,15 @@ static int si476x_core_probe(struct i2c_client *client,
|
|||
memcpy(&core->pinmux, &pdata->pinmux,
|
||||
sizeof(struct si476x_pinmux));
|
||||
} else {
|
||||
dev_err(&client->dev, "No platform data provided\n");
|
||||
return -EINVAL;
|
||||
dev_warn(&client->dev, "Using default platform data.\n");
|
||||
core->power_up_parameters.xcload = 0x28;
|
||||
core->power_up_parameters.func = SI476X_FUNC_FM_RECEIVER;
|
||||
core->power_up_parameters.freq = SI476X_FREQ_37P209375_MHZ;
|
||||
core->diversity_mode = SI476X_PHDIV_DISABLED;
|
||||
core->pinmux.dclk = SI476X_DCLK_DAUDIO;
|
||||
core->pinmux.dfs = SI476X_DFS_DAUDIO;
|
||||
core->pinmux.dout = SI476X_DOUT_I2S_OUTPUT;
|
||||
core->pinmux.xout = SI476X_XOUT_TRISTATE;
|
||||
}
|
||||
|
||||
core->supplies[0].supply = "vd";
|
||||
|
@ -789,12 +796,18 @@ static int si476x_core_probe(struct i2c_client *client,
|
|||
|
||||
core->chip_id = id->driver_data;
|
||||
|
||||
/* Power down si476x first */
|
||||
si476x_core_stop(core, true);
|
||||
|
||||
rval = si476x_core_get_revision_info(core);
|
||||
if (rval < 0) {
|
||||
rval = -ENODEV;
|
||||
goto free_kfifo;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(client->dev.of_node, "revision-a10"))
|
||||
core->revision = SI476X_REVISION_A10;
|
||||
|
||||
cell_num = 0;
|
||||
|
||||
cell = &core->cells[SI476X_RADIO_CELL];
|
||||
|
@ -810,6 +823,7 @@ static int si476x_core_probe(struct i2c_client *client,
|
|||
core->pinmux.xout == SI476X_XOUT_TRISTATE) {
|
||||
cell = &core->cells[SI476X_CODEC_CELL];
|
||||
cell->name = "si476x-codec";
|
||||
cell->of_compatible = "si476x-codec";
|
||||
cell_num++;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -484,6 +484,8 @@ enum si476x_common_receiver_properties {
|
|||
SI476X_PROP_DIGITAL_IO_OUTPUT_SAMPLE_RATE = 0x0202,
|
||||
SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT = 0x0203,
|
||||
|
||||
SI476X_PROP_AUDIO_MUTE = 0x0301,
|
||||
|
||||
SI476X_PROP_SEEK_BAND_BOTTOM = 0x1100,
|
||||
SI476X_PROP_SEEK_BAND_TOP = 0x1101,
|
||||
SI476X_PROP_SEEK_FREQUENCY_SPACING = 0x1102,
|
||||
|
|
|
@ -76,6 +76,7 @@ struct snd_dmaengine_dai_dma_data {
|
|||
const char *chan_name;
|
||||
unsigned int fifo_size;
|
||||
unsigned int flags;
|
||||
unsigned int fifo_num;
|
||||
};
|
||||
|
||||
void snd_dmaengine_pcm_set_config_from_dai_data(
|
||||
|
|
|
@ -22,7 +22,6 @@ struct snd_sof_dsp_ops;
|
|||
*/
|
||||
struct snd_sof_pdata {
|
||||
const struct firmware *fw;
|
||||
const char *drv_name;
|
||||
const char *name;
|
||||
const char *platform;
|
||||
|
||||
|
@ -88,13 +87,13 @@ struct sof_dev_desc {
|
|||
const char *default_fw_path;
|
||||
const char *default_tplg_path;
|
||||
|
||||
/* default firmware name */
|
||||
const char *default_fw_filename;
|
||||
|
||||
const struct snd_sof_dsp_ops *ops;
|
||||
const struct sof_arch_ops *arch_ops;
|
||||
};
|
||||
|
||||
int sof_nocodec_setup(struct device *dev,
|
||||
struct snd_sof_pdata *sof_pdata,
|
||||
struct snd_soc_acpi_mach *mach,
|
||||
const struct sof_dev_desc *desc,
|
||||
const struct snd_sof_dsp_ops *ops);
|
||||
#endif
|
||||
|
|
55
include/sound/sof/dai-imx.h
Normal file
55
include/sound/sof/dai-imx.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
*
|
||||
* Author: Daniel Baluta <daniel.baluta@nxp.com>
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_DAI_IMX_H__
|
||||
#define __INCLUDE_SOUND_SOF_DAI_IMX_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/* ESAI Configuration Request - SOF_IPC_DAI_ESAI_CONFIG */
|
||||
struct sof_ipc_dai_esai_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
|
||||
/* MCLK */
|
||||
uint16_t reserved1;
|
||||
uint16_t mclk_id;
|
||||
uint32_t mclk_direction;
|
||||
|
||||
uint32_t mclk_rate; /* MCLK frequency in Hz */
|
||||
uint32_t fsync_rate; /* FSYNC frequency in Hz */
|
||||
uint32_t bclk_rate; /* BCLK frequency in Hz */
|
||||
|
||||
/* TDM */
|
||||
uint32_t tdm_slots;
|
||||
uint32_t rx_slots;
|
||||
uint32_t tx_slots;
|
||||
uint16_t tdm_slot_width;
|
||||
uint16_t reserved2; /* alignment */
|
||||
} __packed;
|
||||
|
||||
/* SAI Configuration Request - SOF_IPC_DAI_SAI_CONFIG */
|
||||
struct sof_ipc_dai_sai_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
|
||||
/* MCLK */
|
||||
uint16_t reserved1;
|
||||
uint16_t mclk_id;
|
||||
uint32_t mclk_direction;
|
||||
|
||||
uint32_t mclk_rate; /* MCLK frequency in Hz */
|
||||
uint32_t fsync_rate; /* FSYNC frequency in Hz */
|
||||
uint32_t bclk_rate; /* BCLK frequency in Hz */
|
||||
|
||||
/* TDM */
|
||||
uint32_t tdm_slots;
|
||||
uint32_t rx_slots;
|
||||
uint32_t tx_slots;
|
||||
uint16_t tdm_slot_width;
|
||||
uint16_t reserved2; /* alignment */
|
||||
} __packed;
|
||||
#endif
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <sound/sof/header.h>
|
||||
#include <sound/sof/dai-intel.h>
|
||||
#include <sound/sof/dai-imx.h>
|
||||
|
||||
/*
|
||||
* DAI Configuration.
|
||||
|
@ -73,6 +74,8 @@ struct sof_ipc_dai_config {
|
|||
struct sof_ipc_dai_dmic_params dmic;
|
||||
struct sof_ipc_dai_hda_params hda;
|
||||
struct sof_ipc_dai_alh_params alh;
|
||||
struct sof_ipc_dai_esai_params esai;
|
||||
struct sof_ipc_dai_sai_params sai;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
|
|
165
include/uapi/linux/mxc_asrc.h
Normal file
165
include/uapi/linux/mxc_asrc.h
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright 2008-2014 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file mxc_asrc.h
|
||||
*
|
||||
* @brief i.MX Asynchronous Sample Rate Converter
|
||||
*
|
||||
* @ingroup Audio
|
||||
*/
|
||||
|
||||
#ifndef __MXC_ASRC_UAPI_H__
|
||||
#define __MXC_ASRC_UAPI_H__
|
||||
|
||||
#define ASRC_IOC_MAGIC 'C'
|
||||
|
||||
#define ASRC_REQ_PAIR _IOWR(ASRC_IOC_MAGIC, 0, struct asrc_req)
|
||||
#define ASRC_CONFIG_PAIR _IOWR(ASRC_IOC_MAGIC, 1, struct asrc_config)
|
||||
#define ASRC_RELEASE_PAIR _IOW(ASRC_IOC_MAGIC, 2, enum asrc_pair_index)
|
||||
#define ASRC_CONVERT _IOW(ASRC_IOC_MAGIC, 3, struct asrc_convert_buffer)
|
||||
#define ASRC_START_CONV _IOW(ASRC_IOC_MAGIC, 4, enum asrc_pair_index)
|
||||
#define ASRC_STOP_CONV _IOW(ASRC_IOC_MAGIC, 5, enum asrc_pair_index)
|
||||
#define ASRC_STATUS _IOW(ASRC_IOC_MAGIC, 6, struct asrc_status_flags)
|
||||
#define ASRC_FLUSH _IOW(ASRC_IOC_MAGIC, 7, enum asrc_pair_index)
|
||||
|
||||
enum asrc_pair_index {
|
||||
ASRC_INVALID_PAIR = -1,
|
||||
ASRC_PAIR_A = 0,
|
||||
ASRC_PAIR_B = 1,
|
||||
ASRC_PAIR_C = 2,
|
||||
ASRC_PAIR_D = 3,
|
||||
};
|
||||
|
||||
enum asrc_inclk {
|
||||
INCLK_NONE = 0x03,
|
||||
INCLK_ESAI_RX = 0x00,
|
||||
INCLK_SSI1_RX = 0x01,
|
||||
INCLK_SSI2_RX = 0x02,
|
||||
INCLK_SSI3_RX = 0x07,
|
||||
INCLK_SPDIF_RX = 0x04,
|
||||
INCLK_MLB_CLK = 0x05,
|
||||
INCLK_PAD = 0x06,
|
||||
INCLK_ESAI_TX = 0x08,
|
||||
INCLK_SSI1_TX = 0x09,
|
||||
INCLK_SSI2_TX = 0x0a,
|
||||
INCLK_SSI3_TX = 0x0b,
|
||||
INCLK_SPDIF_TX = 0x0c,
|
||||
INCLK_ASRCK1_CLK = 0x0f,
|
||||
/* imx8 */
|
||||
INCLK_AUD_PLL_DIV_CLK0 = 0x10,
|
||||
INCLK_AUD_PLL_DIV_CLK1 = 0x11,
|
||||
INCLK_AUD_CLK0 = 0x12,
|
||||
INCLK_AUD_CLK1 = 0x13,
|
||||
INCLK_ESAI0_RX_CLK = 0x14,
|
||||
INCLK_ESAI0_TX_CLK = 0x15,
|
||||
INCLK_SPDIF0_RX = 0x16,
|
||||
INCLK_SPDIF1_RX = 0x17,
|
||||
INCLK_SAI0_RX_BCLK = 0x18,
|
||||
INCLK_SAI0_TX_BCLK = 0x19,
|
||||
INCLK_SAI1_RX_BCLK = 0x1a,
|
||||
INCLK_SAI1_TX_BCLK = 0x1b,
|
||||
INCLK_SAI2_RX_BCLK = 0x1c,
|
||||
INCLK_SAI3_RX_BCLK = 0x1d,
|
||||
INCLK_ASRC0_MUX_CLK = 0x1e,
|
||||
|
||||
INCLK_ESAI1_RX_CLK = 0x20,
|
||||
INCLK_ESAI1_TX_CLK = 0x21,
|
||||
INCLK_SAI6_TX_BCLK = 0x22,
|
||||
INCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
|
||||
INCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
|
||||
};
|
||||
|
||||
enum asrc_outclk {
|
||||
OUTCLK_NONE = 0x03,
|
||||
OUTCLK_ESAI_TX = 0x00,
|
||||
OUTCLK_SSI1_TX = 0x01,
|
||||
OUTCLK_SSI2_TX = 0x02,
|
||||
OUTCLK_SSI3_TX = 0x07,
|
||||
OUTCLK_SPDIF_TX = 0x04,
|
||||
OUTCLK_MLB_CLK = 0x05,
|
||||
OUTCLK_PAD = 0x06,
|
||||
OUTCLK_ESAI_RX = 0x08,
|
||||
OUTCLK_SSI1_RX = 0x09,
|
||||
OUTCLK_SSI2_RX = 0x0a,
|
||||
OUTCLK_SSI3_RX = 0x0b,
|
||||
OUTCLK_SPDIF_RX = 0x0c,
|
||||
OUTCLK_ASRCK1_CLK = 0x0f,
|
||||
|
||||
/* imx8 */
|
||||
OUTCLK_AUD_PLL_DIV_CLK0 = 0x10,
|
||||
OUTCLK_AUD_PLL_DIV_CLK1 = 0x11,
|
||||
OUTCLK_AUD_CLK0 = 0x12,
|
||||
OUTCLK_AUD_CLK1 = 0x13,
|
||||
OUTCLK_ESAI0_RX_CLK = 0x14,
|
||||
OUTCLK_ESAI0_TX_CLK = 0x15,
|
||||
OUTCLK_SPDIF0_RX = 0x16,
|
||||
OUTCLK_SPDIF1_RX = 0x17,
|
||||
OUTCLK_SAI0_RX_BCLK = 0x18,
|
||||
OUTCLK_SAI0_TX_BCLK = 0x19,
|
||||
OUTCLK_SAI1_RX_BCLK = 0x1a,
|
||||
OUTCLK_SAI1_TX_BCLK = 0x1b,
|
||||
OUTCLK_SAI2_RX_BCLK = 0x1c,
|
||||
OUTCLK_SAI3_RX_BCLK = 0x1d,
|
||||
OUTCLK_ASRCO_MUX_CLK = 0x1e,
|
||||
|
||||
OUTCLK_ESAI1_RX_CLK = 0x20,
|
||||
OUTCLK_ESAI1_TX_CLK = 0x21,
|
||||
OUTCLK_SAI6_TX_BCLK = 0x22,
|
||||
OUTCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
|
||||
OUTCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
|
||||
};
|
||||
|
||||
struct asrc_config {
|
||||
enum asrc_pair_index pair;
|
||||
unsigned int channel_num;
|
||||
unsigned int dma_buffer_size;
|
||||
unsigned int input_sample_rate;
|
||||
unsigned int output_sample_rate;
|
||||
snd_pcm_format_t input_format;
|
||||
snd_pcm_format_t output_format;
|
||||
enum asrc_inclk inclk;
|
||||
enum asrc_outclk outclk;
|
||||
};
|
||||
|
||||
struct asrc_req {
|
||||
unsigned int chn_num;
|
||||
enum asrc_pair_index index;
|
||||
uint64_t supported_in_format;
|
||||
uint64_t supported_out_format;
|
||||
};
|
||||
|
||||
struct asrc_querybuf {
|
||||
unsigned int buffer_index;
|
||||
unsigned int input_length;
|
||||
unsigned int output_length;
|
||||
unsigned long input_offset;
|
||||
unsigned long output_offset;
|
||||
};
|
||||
|
||||
struct asrc_convert_buffer {
|
||||
void *input_buffer_vaddr;
|
||||
void *output_buffer_vaddr;
|
||||
unsigned int input_buffer_length;
|
||||
unsigned int output_buffer_length;
|
||||
};
|
||||
|
||||
struct asrc_status_flags {
|
||||
enum asrc_pair_index index;
|
||||
unsigned int overload_error;
|
||||
};
|
||||
|
||||
enum asrc_error_status {
|
||||
ASRC_TASK_Q_OVERLOAD = 0x01,
|
||||
ASRC_OUTPUT_TASK_OVERLOAD = 0x02,
|
||||
ASRC_INPUT_TASK_OVERLOAD = 0x04,
|
||||
ASRC_OUTPUT_BUFFER_OVERFLOW = 0x08,
|
||||
ASRC_INPUT_BUFFER_UNDERRUN = 0x10,
|
||||
};
|
||||
#endif/* __MXC_ASRC_UAPI_H__ */
|
152
include/uapi/linux/mxc_dsp.h
Normal file
152
include/uapi/linux/mxc_dsp.h
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright 2018 NXP
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __MXC_DSP_UAPI_H__
|
||||
#define __MXC_DSP_UAPI_H__
|
||||
|
||||
#define DSP_IOC_MAGIC 'H'
|
||||
#define DSP_CLIENT_REGISTER _IOW(DSP_IOC_MAGIC, 0, unsigned int)
|
||||
#define DSP_CLIENT_UNREGISTER _IOW(DSP_IOC_MAGIC, 1, unsigned int)
|
||||
#define DSP_IPC_MSG_SEND _IOW(DSP_IOC_MAGIC, 2, unsigned int)
|
||||
#define DSP_IPC_MSG_RECV _IOW(DSP_IOC_MAGIC, 3, unsigned int)
|
||||
#define DSP_GET_SHMEM_INFO _IOW(DSP_IOC_MAGIC, 4, unsigned int)
|
||||
#define DSP_LOAD_LIB _IOW(DSP_IOC_MAGIC, 5, unsigned int)
|
||||
#define DSP_UNLOAD_LIB _IOW(DSP_IOC_MAGIC, 6, unsigned int)
|
||||
|
||||
|
||||
#define CODEC_MP3_DEC 1
|
||||
#define CODEC_AAC_DEC 2
|
||||
#define CODEC_DAB_DEC 3
|
||||
#define CODEC_MP2_DEC 4
|
||||
#define CODEC_BSAC_DEC 5
|
||||
#define CODEC_DRM_DEC 6
|
||||
#define CODEC_SBC_DEC 7
|
||||
#define CODEC_SBC_ENC 8
|
||||
#define CODEC_DEMO_DEC 9
|
||||
|
||||
#define RENDER_ESAI 0x10
|
||||
#define RENDER_SAI 0x11
|
||||
|
||||
enum DSP_ERROR_TYPE {
|
||||
XA_SUCCESS = 0,
|
||||
|
||||
XA_ERROR_STREAM,
|
||||
XA_PARA_ERROR,
|
||||
XA_INSUFFICIENT_MEM,
|
||||
XA_ERR_UNKNOWN,
|
||||
XA_PROFILE_NOT_SUPPORT,
|
||||
XA_INIT_ERR,
|
||||
XA_NO_OUTPUT,
|
||||
|
||||
XA_NOT_ENOUGH_DATA = 0x100,
|
||||
XA_CAPIBILITY_CHANGE = 0x200,
|
||||
XA_END_OF_STREAM = 0x300, /* no output */
|
||||
};
|
||||
|
||||
/* Parameter type to Set /Get */
|
||||
enum DSP_ParaType {
|
||||
/* Set parmameters */
|
||||
/* common */
|
||||
XA_SAMPLERATE = 0,
|
||||
XA_CHANNEL,
|
||||
XA_FRAMED, /* one whole frame input */
|
||||
XA_DEPTH,
|
||||
XA_CODEC_DATA,
|
||||
XA_BITRATE,
|
||||
XA_DOWNMIX_STEREO,
|
||||
XA_STREAM_TYPE,
|
||||
XA_CHAN_MAP_TABLE,
|
||||
//UNIA_CHANNEL_MASK,
|
||||
XA_TO_STEREO,
|
||||
|
||||
/* dedicate for mp3 dec */
|
||||
XA_MP3_DEC_CRC_CHECK = 0x120,
|
||||
XA_MP3_DEC_MCH_ENABLE,
|
||||
XA_MP3_DEC_NONSTD_STRM_SUPPORT,
|
||||
|
||||
/* dedicate for bsac dec */
|
||||
XA_BSAC_DEC_DECODELAYERS = 0x130,
|
||||
|
||||
/* dedicate for aacplus dec */
|
||||
XA_AACPLUS_DEC_BDOWNSAMPLE = 0x140,
|
||||
XA_AACPLUS_DEC_BBITSTREAMDOWNMIX,
|
||||
XA_AACPLUS_DEC_CHANROUTING,
|
||||
|
||||
/* dedicate for dabplus dec */
|
||||
XA_DABPLUS_DEC_BDOWNSAMPLE = 0x150,
|
||||
XA_DABPLUS_DEC_BBITSTREAMDOWNMIX,
|
||||
XA_DABPLUS_DEC_CHANROUTING,
|
||||
|
||||
/* dedicate for sbc enc */
|
||||
XA_SBC_ENC_SUBBANDS = 0x160,
|
||||
XA_SBC_ENC_BLOCKS,
|
||||
XA_SBC_ENC_SNR,
|
||||
XA_SBC_ENC_BITPOOL,
|
||||
XA_SBC_ENC_CHMODE,
|
||||
|
||||
/* Get parmameters */
|
||||
XA_CODEC_DESCRIPTION = 0x200,
|
||||
XA_OUTPUT_PCM_FORMAT,
|
||||
XA_CONSUMED_LENGTH,
|
||||
XA_OUTBUF_ALLOC_SIZE,
|
||||
XA_CONSUMED_CYCLES,
|
||||
|
||||
};
|
||||
|
||||
#define XA_STREAM_DABPLUS_BASE 0x30
|
||||
enum DSP_StreamType {
|
||||
/* AAC/AACPLUS file format */
|
||||
XA_STREAM_UNKNOWN = 0,
|
||||
XA_STREAM_ADTS,
|
||||
XA_STREAM_ADIF,
|
||||
XA_STREAM_RAW,
|
||||
|
||||
XA_STREAM_LATM,
|
||||
XA_STREAM_LATM_OUTOFBAND_CONFIG,
|
||||
XA_STREAM_LOAS,
|
||||
|
||||
/* DABPLUS file format */
|
||||
XA_STREAM_DABPLUS_RAW_SIDEINFO = XA_STREAM_DABPLUS_BASE,
|
||||
XA_STREAM_DABPLUS,
|
||||
|
||||
/* BSAC file raw format */
|
||||
XA_STREAM_BSAC_RAW,
|
||||
|
||||
};
|
||||
|
||||
/* sbc_enc-specific channel modes */
|
||||
enum DSP_SbcEncChmode {
|
||||
XA_CHMODE_MONO = 0,
|
||||
XA_CHMODE_DUAL = 1,
|
||||
XA_CHMODE_STEREO = 2,
|
||||
XA_CHMODE_JOINT = 3,
|
||||
};
|
||||
|
||||
struct shmem_info {
|
||||
unsigned int phys_addr;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
#endif/* __MXC_DSP_UAPI_H__ */
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
/* SOF ABI version major, minor and patch numbers */
|
||||
#define SOF_ABI_MAJOR 3
|
||||
#define SOF_ABI_MINOR 10
|
||||
#define SOF_ABI_MINOR 11
|
||||
#define SOF_ABI_PATCH 0
|
||||
|
||||
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
|
||||
|
|
|
@ -107,11 +107,9 @@
|
|||
#define SOF_TKN_EFFECT_TYPE SOF_TKN_PROCESS_TYPE
|
||||
|
||||
/* SAI */
|
||||
#define SOF_TKN_IMX_SAI_FIRST_TOKEN 1000
|
||||
/* TODO: Add SAI tokens */
|
||||
#define SOF_TKN_IMX_SAI_MCLK_ID 1000
|
||||
|
||||
/* ESAI */
|
||||
#define SOF_TKN_IMX_ESAI_FIRST_TOKEN 1100
|
||||
/* TODO: Add ESAI tokens */
|
||||
#define SOF_TKN_IMX_ESAI_MCLK_ID 1100
|
||||
|
||||
#endif
|
||||
|
|
|
@ -571,8 +571,11 @@ static int atmel_classd_probe(struct platform_device *pdev)
|
|||
dd->pdata = pdata;
|
||||
|
||||
dd->irq = platform_get_irq(pdev, 0);
|
||||
if (dd->irq < 0)
|
||||
return dd->irq;
|
||||
if (dd->irq < 0) {
|
||||
ret = dd->irq;
|
||||
dev_err(dev, "failed to could not get irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dd->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(dd->pclk)) {
|
||||
|
|
|
@ -612,8 +612,11 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
|
|||
dd->dev = dev;
|
||||
|
||||
dd->irq = platform_get_irq(pdev, 0);
|
||||
if (dd->irq < 0)
|
||||
return dd->irq;
|
||||
if (dd->irq < 0) {
|
||||
ret = dd->irq;
|
||||
dev_err(dev, "failed to get irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dd->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(dd->pclk)) {
|
||||
|
|
|
@ -1342,8 +1342,11 @@ static int cygnus_ssp_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
cygaud->irq_num = platform_get_irq(pdev, 0);
|
||||
if (cygaud->irq_num <= 0)
|
||||
return cygaud->irq_num;
|
||||
if (cygaud->irq_num <= 0) {
|
||||
dev_err(dev, "platform_get_irq failed\n");
|
||||
err = cygaud->irq_num;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = audio_clk_init(pdev, cygaud);
|
||||
if (err) {
|
||||
|
|
|
@ -257,6 +257,9 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW)
|
||||
select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW)
|
||||
select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW)
|
||||
select SND_SOC_RPMSG_WM8960
|
||||
select SND_SOC_RPMSG_CS42XX8
|
||||
select SND_SOC_RPMSG_AK4497
|
||||
help
|
||||
Normally ASoC codec drivers are only built if a machine driver which
|
||||
uses them is also built since they are only usable with a machine
|
||||
|
@ -696,6 +699,9 @@ config SND_SOC_ES8328_SPI
|
|||
depends on SPI_MASTER
|
||||
select SND_SOC_ES8328
|
||||
|
||||
config SND_SOC_FSL_MQS
|
||||
tristate
|
||||
|
||||
config SND_SOC_GTM601
|
||||
tristate 'GTM601 UMTS modem audio codec'
|
||||
|
||||
|
@ -1436,6 +1442,14 @@ config SND_SOC_ZX_AUD96P22
|
|||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
|
||||
config SND_SOC_RPMSG_WM8960
|
||||
tristate
|
||||
|
||||
config SND_SOC_RPMSG_CS42XX8
|
||||
tristate
|
||||
|
||||
config SND_SOC_RPMSG_AK4497
|
||||
tristate
|
||||
# Amp
|
||||
config SND_SOC_LM4857
|
||||
tristate
|
||||
|
|
|
@ -86,6 +86,7 @@ snd-soc-es8316-objs := es8316.o
|
|||
snd-soc-es8328-objs := es8328.o
|
||||
snd-soc-es8328-i2c-objs := es8328-i2c.o
|
||||
snd-soc-es8328-spi-objs := es8328-spi.o
|
||||
snd-soc-fsl-mqs-objs := fsl_mqs.o
|
||||
snd-soc-gtm601-objs := gtm601.o
|
||||
snd-soc-hdac-hdmi-objs := hdac_hdmi.o
|
||||
snd-soc-hdac-hda-objs := hdac_hda.o
|
||||
|
@ -274,6 +275,10 @@ snd-soc-wm9712-objs := wm9712.o
|
|||
snd-soc-wm9713-objs := wm9713.o
|
||||
snd-soc-wm-hubs-objs := wm_hubs.o
|
||||
snd-soc-zx-aud96p22-objs := zx_aud96p22.o
|
||||
snd-soc-rpmsg-wm8960-objs := rpmsg_wm8960.o
|
||||
snd-soc-rpmsg-cs42xx8-objs := rpmsg_cs42xx8.o
|
||||
snd-soc-rpmsg-ak4497-objs := rpmsg_ak4497.o
|
||||
|
||||
# Amp
|
||||
snd-soc-max9877-objs := max9877.o
|
||||
snd-soc-max98504-objs := max98504.o
|
||||
|
@ -370,6 +375,7 @@ obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o
|
|||
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o
|
||||
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
|
||||
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
|
||||
|
@ -557,6 +563,9 @@ obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
|
|||
obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
|
||||
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
|
||||
obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o
|
||||
obj-$(CONFIG_SND_SOC_RPMSG_WM8960) += snd-soc-rpmsg-wm8960.o
|
||||
obj-$(CONFIG_SND_SOC_RPMSG_CS42XX8) += snd-soc-rpmsg-cs42xx8.o
|
||||
obj-$(CONFIG_SND_SOC_RPMSG_AK4497) += snd-soc-rpmsg-ak4497.o
|
||||
|
||||
# Amp
|
||||
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
|
||||
|
|
|
@ -18,12 +18,20 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "ak4458.h"
|
||||
|
||||
#define AK4458_NUM_SUPPLIES 2
|
||||
static const char *ak4458_supply_names[AK4458_NUM_SUPPLIES] = {
|
||||
"DVDD",
|
||||
"AVDD",
|
||||
};
|
||||
|
||||
struct ak4458_drvdata {
|
||||
struct snd_soc_dai_driver *dai_drv;
|
||||
const struct snd_soc_component_driver *comp_drv;
|
||||
bool dsd512; /* DSD512 is supported or not */
|
||||
};
|
||||
|
||||
/* AK4458 Codec Private Data */
|
||||
|
@ -32,11 +40,13 @@ struct ak4458_priv {
|
|||
struct regmap *regmap;
|
||||
struct gpio_desc *reset_gpiod;
|
||||
struct gpio_desc *mute_gpiod;
|
||||
const struct ak4458_drvdata *drvdata;
|
||||
int digfil; /* SSLOW, SD, SLOW bits */
|
||||
int fs; /* sampling rate */
|
||||
int fmt;
|
||||
int slots;
|
||||
int slot_width;
|
||||
struct regulator_bulk_data supplies[AK4458_NUM_SUPPLIES];
|
||||
};
|
||||
|
||||
static const struct reg_default ak4458_reg_defaults[] = {
|
||||
|
@ -128,6 +138,9 @@ static const char * const ak4458_ats_select_texts[] = {
|
|||
/* DIF2 bit Audio Interface Format Setting(BICK fs) */
|
||||
static const char * const ak4458_dif_select_texts[] = {"32fs,48fs", "64fs",};
|
||||
|
||||
static const char * const ak4497_dsd_input_path_select[] = {
|
||||
"16_17_19pin", "3_4_5pin"};
|
||||
|
||||
static const struct soc_enum ak4458_dac1_dem_enum =
|
||||
SOC_ENUM_SINGLE(AK4458_01_CONTROL2, 1,
|
||||
ARRAY_SIZE(ak4458_dem_select_texts),
|
||||
|
@ -167,6 +180,10 @@ static const struct soc_enum ak4458_dif_enum =
|
|||
SOC_ENUM_SINGLE(AK4458_00_CONTROL1, 3,
|
||||
ARRAY_SIZE(ak4458_dif_select_texts),
|
||||
ak4458_dif_select_texts);
|
||||
static const struct soc_enum ak4497_dsdp_enum =
|
||||
SOC_ENUM_SINGLE(AK4458_09_DSD2, 2,
|
||||
ARRAY_SIZE(ak4497_dsd_input_path_select),
|
||||
ak4497_dsd_input_path_select);
|
||||
|
||||
static int get_digfil(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
|
@ -274,6 +291,7 @@ static const struct snd_kcontrol_new ak4497_snd_controls[] = {
|
|||
SOC_ENUM("AK4497 Sound Mode", ak4458_sm_enum),
|
||||
SOC_ENUM("AK4497 Attenuation transition Time Setting",
|
||||
ak4458_ats_enum),
|
||||
SOC_ENUM("AK4497 DSD Data Input Pin", ak4497_dsdp_enum),
|
||||
};
|
||||
|
||||
/* ak4497 dapm widgets */
|
||||
|
@ -290,6 +308,20 @@ static const struct snd_soc_dapm_route ak4497_intercon[] = {
|
|||
|
||||
};
|
||||
|
||||
static int ak4458_get_tdm_mode(struct ak4458_priv *ak4458)
|
||||
{
|
||||
switch (ak4458->slots * ak4458->slot_width) {
|
||||
case 128:
|
||||
return 1;
|
||||
case 256:
|
||||
return 2;
|
||||
case 512:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int ak4458_rstn_control(struct snd_soc_component *component, int bit)
|
||||
{
|
||||
int ret;
|
||||
|
@ -318,11 +350,60 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream,
|
|||
struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
|
||||
int pcm_width = max(params_physical_width(params), ak4458->slot_width);
|
||||
int nfs1;
|
||||
u8 format;
|
||||
u8 format, dsdsel0, dsdsel1, dchn;
|
||||
int ret, dsd_bclk, channels, channels_max;
|
||||
bool is_dsd = false;
|
||||
|
||||
channels = params_channels(params);
|
||||
channels_max = dai->driver->playback.channels_max;
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_DSD_U8:
|
||||
case SNDRV_PCM_FORMAT_DSD_U16_LE:
|
||||
case SNDRV_PCM_FORMAT_DSD_U16_BE:
|
||||
case SNDRV_PCM_FORMAT_DSD_U32_LE:
|
||||
case SNDRV_PCM_FORMAT_DSD_U32_BE:
|
||||
is_dsd = true;
|
||||
dsd_bclk = params_rate(params) * params_physical_width(params);
|
||||
break;
|
||||
}
|
||||
|
||||
nfs1 = params_rate(params);
|
||||
ak4458->fs = nfs1;
|
||||
|
||||
if (is_dsd) {
|
||||
switch (dsd_bclk) {
|
||||
case 2822400:
|
||||
dsdsel0 = 0;
|
||||
dsdsel1 = 0;
|
||||
break;
|
||||
case 5644800:
|
||||
dsdsel0 = 1;
|
||||
dsdsel1 = 0;
|
||||
break;
|
||||
case 11289600:
|
||||
dsdsel0 = 0;
|
||||
dsdsel1 = 1;
|
||||
break;
|
||||
case 22579200:
|
||||
if (ak4458->drvdata->dsd512) {
|
||||
dsdsel0 = 1;
|
||||
dsdsel1 = 1;
|
||||
} else {
|
||||
dev_err(dai->dev, "DSD512 not supported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, AK4458_06_DSD1,
|
||||
AK4458_DSDSEL_MASK, dsdsel0);
|
||||
snd_soc_component_update_bits(component, AK4458_09_DSD2,
|
||||
AK4458_DSDSEL_MASK, dsdsel1);
|
||||
}
|
||||
|
||||
/* Master Clock Frequency Auto Setting Mode Enable */
|
||||
snd_soc_component_update_bits(component, AK4458_00_CONTROL1, 0x80, 0x80);
|
||||
|
||||
|
@ -347,6 +428,9 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream,
|
|||
case SND_SOC_DAIFMT_DSP_B:
|
||||
format = AK4458_DIF_32BIT_MSB;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_PDM:
|
||||
format = AK4458_DIF_32BIT_MSB;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -358,8 +442,24 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream,
|
|||
snd_soc_component_update_bits(component, AK4458_00_CONTROL1,
|
||||
AK4458_DIF_MASK, format);
|
||||
|
||||
ak4458_rstn_control(component, 0);
|
||||
ak4458_rstn_control(component, 1);
|
||||
/*
|
||||
* Enable/disable Daisy Chain if in TDM mode and the number of played
|
||||
* channels is bigger than the maximum supported number of channels
|
||||
*/
|
||||
dchn = ak4458_get_tdm_mode(ak4458) &&
|
||||
(ak4458->fmt == SND_SOC_DAIFMT_DSP_B) &&
|
||||
(channels > channels_max) ? AK4458_DCHAIN_MASK : 0;
|
||||
|
||||
snd_soc_component_update_bits(component, AK4458_0B_CONTROL7,
|
||||
AK4458_DCHAIN_MASK, dchn);
|
||||
|
||||
ret = ak4458_rstn_control(component, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ak4458_rstn_control(component, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -368,6 +468,7 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
|
||||
u8 dp;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS: /* Slave Mode */
|
||||
|
@ -385,6 +486,7 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
case SND_SOC_DAIFMT_PDM:
|
||||
ak4458->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
|
||||
break;
|
||||
default:
|
||||
|
@ -393,6 +495,11 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* DSD mode */
|
||||
dp = ak4458->fmt == SND_SOC_DAIFMT_PDM ? AK4458_DP_MASK : 0;
|
||||
snd_soc_component_update_bits(component, AK4458_02_CONTROL3,
|
||||
AK4458_DP_MASK, dp);
|
||||
|
||||
ak4458_rstn_control(component, 0);
|
||||
ak4458_rstn_control(component, 1);
|
||||
|
||||
|
@ -440,31 +547,20 @@ static int ak4458_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
|
|||
ak4458->slots = slots;
|
||||
ak4458->slot_width = slot_width;
|
||||
|
||||
switch (slots * slot_width) {
|
||||
case 128:
|
||||
mode = AK4458_MODE_TDM128;
|
||||
break;
|
||||
case 256:
|
||||
mode = AK4458_MODE_TDM256;
|
||||
break;
|
||||
case 512:
|
||||
mode = AK4458_MODE_TDM512;
|
||||
break;
|
||||
default:
|
||||
mode = AK4458_MODE_NORMAL;
|
||||
break;
|
||||
}
|
||||
mode = ak4458_get_tdm_mode(ak4458) << AK4458_MODE_SHIFT;
|
||||
|
||||
snd_soc_component_update_bits(component, AK4458_0A_CONTROL6,
|
||||
AK4458_MODE_MASK,
|
||||
mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define AK4458_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
SNDRV_PCM_FMTBIT_S32_LE |\
|
||||
SNDRV_PCM_FMTBIT_DSD_U8 |\
|
||||
SNDRV_PCM_FMTBIT_DSD_U16_LE |\
|
||||
SNDRV_PCM_FMTBIT_DSD_U32_LE)
|
||||
|
||||
static const unsigned int ak4458_rates[] = {
|
||||
8000, 11025, 16000, 22050,
|
||||
|
@ -649,11 +745,13 @@ static const struct regmap_config ak4458_regmap = {
|
|||
static const struct ak4458_drvdata ak4458_drvdata = {
|
||||
.dai_drv = &ak4458_dai,
|
||||
.comp_drv = &soc_codec_dev_ak4458,
|
||||
.dsd512 = false,
|
||||
};
|
||||
|
||||
static const struct ak4458_drvdata ak4497_drvdata = {
|
||||
.dai_drv = &ak4497_dai,
|
||||
.comp_drv = &soc_codec_dev_ak4497,
|
||||
.dsd512 = true,
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops ak4458_pm = {
|
||||
|
@ -665,8 +763,8 @@ static const struct dev_pm_ops ak4458_pm = {
|
|||
static int ak4458_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct ak4458_priv *ak4458;
|
||||
const struct ak4458_drvdata *drvdata;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL);
|
||||
if (!ak4458)
|
||||
|
@ -679,7 +777,7 @@ static int ak4458_i2c_probe(struct i2c_client *i2c)
|
|||
i2c_set_clientdata(i2c, ak4458);
|
||||
ak4458->dev = &i2c->dev;
|
||||
|
||||
drvdata = of_device_get_match_data(&i2c->dev);
|
||||
ak4458->drvdata = of_device_get_match_data(&i2c->dev);
|
||||
|
||||
ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
|
@ -691,8 +789,25 @@ static int ak4458_i2c_probe(struct i2c_client *i2c)
|
|||
if (IS_ERR(ak4458->mute_gpiod))
|
||||
return PTR_ERR(ak4458->mute_gpiod);
|
||||
|
||||
ret = devm_snd_soc_register_component(ak4458->dev, drvdata->comp_drv,
|
||||
drvdata->dai_drv, 1);
|
||||
for (i = 0; i < ARRAY_SIZE(ak4458->supplies); i++)
|
||||
ak4458->supplies[i].supply = ak4458_supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(ak4458->dev, ARRAY_SIZE(ak4458->supplies),
|
||||
ak4458->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(ak4458->dev, "Failed to request supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ak4458->supplies),
|
||||
ak4458->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(ak4458->dev, "Failed to enable supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_component(ak4458->dev, ak4458->drvdata->comp_drv,
|
||||
ak4458->drvdata->dai_drv, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(ak4458->dev, "Failed to register CODEC: %d\n", ret);
|
||||
return ret;
|
||||
|
|
|
@ -83,4 +83,9 @@
|
|||
#define AK4458_ATS_SHIFT 6
|
||||
#define AK4458_ATS_MASK GENMASK(7, 6)
|
||||
|
||||
#endif /* _AK4458_H */
|
||||
#define AK4458_DSDSEL_MASK (0x1 << 0)
|
||||
#define AK4458_DP_MASK (0x1 << 7)
|
||||
|
||||
#define AK4458_DCHAIN_MASK (0x1 << 1)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,9 +19,21 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "ak5558.h"
|
||||
|
||||
#define AK5558_SLAVE_CKS_AUTO
|
||||
|
||||
/* enable debug */
|
||||
/* #define AK5558_DEBUG */
|
||||
|
||||
#define AK5558_NUM_SUPPLIES 2
|
||||
static const char *ak5558_supply_names[AK5558_NUM_SUPPLIES] = {
|
||||
"DVDD",
|
||||
"AVDD",
|
||||
};
|
||||
|
||||
/* AK5558 Codec Private Data */
|
||||
struct ak5558_priv {
|
||||
struct snd_soc_component component;
|
||||
|
@ -30,6 +42,7 @@ struct ak5558_priv {
|
|||
struct gpio_desc *reset_gpiod; /* Reset & Power down GPIO */
|
||||
int slots;
|
||||
int slot_width;
|
||||
struct regulator_bulk_data supplies[AK5558_NUM_SUPPLIES];
|
||||
};
|
||||
|
||||
/* ak5558 register cache & default register settings */
|
||||
|
@ -350,6 +363,7 @@ static int ak5558_i2c_probe(struct i2c_client *i2c)
|
|||
{
|
||||
struct ak5558_priv *ak5558;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
ak5558 = devm_kzalloc(&i2c->dev, sizeof(*ak5558), GFP_KERNEL);
|
||||
if (!ak5558)
|
||||
|
@ -367,6 +381,23 @@ static int ak5558_i2c_probe(struct i2c_client *i2c)
|
|||
if (IS_ERR(ak5558->reset_gpiod))
|
||||
return PTR_ERR(ak5558->reset_gpiod);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ak5558->supplies); i++)
|
||||
ak5558->supplies[i].supply = ak5558_supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(ak5558->supplies),
|
||||
ak5558->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ak5558->supplies),
|
||||
ak5558->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_component(&i2c->dev,
|
||||
&soc_codec_dev_ak5558,
|
||||
&ak5558_dai, 1);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
|
@ -43,7 +44,8 @@ struct cs42xx8_priv {
|
|||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
|
||||
bool slave_mode;
|
||||
bool slave_mode[2];
|
||||
bool is_tdm;
|
||||
unsigned long sysclk;
|
||||
u32 tx_channels;
|
||||
struct gpio_desc *gpiod_reset;
|
||||
|
@ -131,7 +133,6 @@ static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = {
|
|||
SND_SOC_DAPM_INPUT("AIN2L"),
|
||||
SND_SOC_DAPM_INPUT("AIN2R"),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("PWR", CS42XX8_PWRCTL, 0, 1, NULL, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = {
|
||||
|
@ -145,35 +146,28 @@ static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = {
|
|||
/* Playback */
|
||||
{ "AOUT1L", NULL, "DAC1" },
|
||||
{ "AOUT1R", NULL, "DAC1" },
|
||||
{ "DAC1", NULL, "PWR" },
|
||||
|
||||
{ "AOUT2L", NULL, "DAC2" },
|
||||
{ "AOUT2R", NULL, "DAC2" },
|
||||
{ "DAC2", NULL, "PWR" },
|
||||
|
||||
{ "AOUT3L", NULL, "DAC3" },
|
||||
{ "AOUT3R", NULL, "DAC3" },
|
||||
{ "DAC3", NULL, "PWR" },
|
||||
|
||||
{ "AOUT4L", NULL, "DAC4" },
|
||||
{ "AOUT4R", NULL, "DAC4" },
|
||||
{ "DAC4", NULL, "PWR" },
|
||||
|
||||
/* Capture */
|
||||
{ "ADC1", NULL, "AIN1L" },
|
||||
{ "ADC1", NULL, "AIN1R" },
|
||||
{ "ADC1", NULL, "PWR" },
|
||||
|
||||
{ "ADC2", NULL, "AIN2L" },
|
||||
{ "ADC2", NULL, "AIN2R" },
|
||||
{ "ADC2", NULL, "PWR" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = {
|
||||
/* Capture */
|
||||
{ "ADC3", NULL, "AIN3L" },
|
||||
{ "ADC3", NULL, "AIN3R" },
|
||||
{ "ADC3", NULL, "PWR" },
|
||||
};
|
||||
|
||||
struct cs42xx8_ratios {
|
||||
|
@ -218,6 +212,8 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
|
||||
u32 val;
|
||||
|
||||
cs42xx8->is_tdm = false;
|
||||
|
||||
/* Set DAI format */
|
||||
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
|
@ -231,6 +227,7 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM;
|
||||
cs42xx8->is_tdm = true;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "unsupported dai format\n");
|
||||
|
@ -241,17 +238,21 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
CS42XX8_INTF_DAC_DIF_MASK |
|
||||
CS42XX8_INTF_ADC_DIF_MASK, val);
|
||||
|
||||
/* Set master/slave audio interface */
|
||||
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
cs42xx8->slave_mode = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
cs42xx8->slave_mode = false;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "unsupported master/slave mode\n");
|
||||
return -EINVAL;
|
||||
if (cs42xx8->slave_mode[0] == cs42xx8->slave_mode[1]) {
|
||||
/* Set master/slave audio interface */
|
||||
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
cs42xx8->slave_mode[0] = true;
|
||||
cs42xx8->slave_mode[1] = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
cs42xx8->slave_mode[0] = false;
|
||||
cs42xx8->slave_mode[1] = false;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "unsupported master/slave mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -281,7 +282,7 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
/* Get functional mode for tx and rx according to rate */
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (cs42xx8->slave_mode) {
|
||||
if (cs42xx8->slave_mode[i]) {
|
||||
fm[i] = CS42XX8_FM_AUTO;
|
||||
} else {
|
||||
if (rate[i] < 50000) {
|
||||
|
@ -336,6 +337,16 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
cs42xx8->rate[tx] = params_rate(params);
|
||||
|
||||
if (cs42xx8->is_tdm && !cs42xx8->slave_mode[tx]) {
|
||||
dev_err(component->dev, "TDM mode is unsupported in master mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cs42xx8->is_tdm && (cs42xx8->sysclk < 256 * cs42xx8->rate[tx])) {
|
||||
dev_err(component->dev, "unsupported sysclk for TDM mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask = CS42XX8_FUNCMOD_MFREQ_MASK;
|
||||
val = cs42xx8_ratios[i].mfreq;
|
||||
|
||||
|
@ -459,6 +470,8 @@ const struct regmap_config cs42xx8_regmap_config = {
|
|||
.volatile_reg = cs42xx8_volatile_register,
|
||||
.writeable_reg = cs42xx8_writeable_register,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
|
||||
|
||||
|
@ -482,7 +495,8 @@ static int cs42xx8_component_probe(struct snd_soc_component *component)
|
|||
|
||||
/* Mute all DAC channels */
|
||||
regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, CS42XX8_DACMUTE_ALL);
|
||||
|
||||
regmap_update_bits(cs42xx8->regmap, CS42XX8_PWRCTL,
|
||||
CS42XX8_PWRCTL_PDN_MASK, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -521,9 +535,11 @@ EXPORT_SYMBOL_GPL(cs42xx8_of_match);
|
|||
|
||||
int cs42xx8_probe(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
const struct of_device_id *of_id;
|
||||
struct cs42xx8_priv *cs42xx8;
|
||||
int ret, val, i;
|
||||
int num_domains = 0;
|
||||
|
||||
if (IS_ERR(regmap)) {
|
||||
ret = PTR_ERR(regmap);
|
||||
|
@ -563,6 +579,36 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
|
|||
|
||||
cs42xx8->sysclk = clk_get_rate(cs42xx8->clk);
|
||||
|
||||
num_domains = of_count_phandle_with_args(np, "power-domains",
|
||||
"#power-domain-cells");
|
||||
for (i = 0; i < num_domains; i++) {
|
||||
struct device *pd_dev;
|
||||
struct device_link *link;
|
||||
|
||||
pd_dev = dev_pm_domain_attach_by_id(dev, i);
|
||||
if (IS_ERR(pd_dev))
|
||||
return PTR_ERR(pd_dev);
|
||||
|
||||
link = device_link_add(dev, pd_dev,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (IS_ERR(link))
|
||||
return PTR_ERR(link);
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "fsl,txm-rxs")) {
|
||||
/* 0 -- rx, 1 -- tx */
|
||||
cs42xx8->slave_mode[0] = true;
|
||||
cs42xx8->slave_mode[1] = false;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "fsl,txs-rxm")) {
|
||||
/* 0 -- rx, 1 -- tx */
|
||||
cs42xx8->slave_mode[0] = false;
|
||||
cs42xx8->slave_mode[1] = true;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++)
|
||||
cs42xx8->supplies[i].supply = cs42xx8_supply_names[i];
|
||||
|
||||
|
|
357
sound/soc/codecs/fsl_mqs.c
Normal file
357
sound/soc/codecs/fsl_mqs.c
Normal file
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
* ALSA SoC IMX MQS driver
|
||||
*
|
||||
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
|
||||
#define REG_MQS_CTRL 0x00
|
||||
|
||||
#define MQS_EN_MASK (0x1 << 28)
|
||||
#define MQS_EN_SHIFT (28)
|
||||
#define MQS_SW_RST_MASK (0x1 << 24)
|
||||
#define MQS_SW_RST_SHIFT (24)
|
||||
#define MQS_OVERSAMPLE_MASK (0x1 << 20)
|
||||
#define MQS_OVERSAMPLE_SHIFT (20)
|
||||
#define MQS_CLK_DIV_MASK (0xFF << 0)
|
||||
#define MQS_CLK_DIV_SHIFT (0)
|
||||
|
||||
|
||||
/* codec private data */
|
||||
struct fsl_mqs {
|
||||
struct platform_device *pdev;
|
||||
struct regmap *gpr;
|
||||
unsigned int reg_iomuxc_gpr2;
|
||||
|
||||
struct regmap *regmap;
|
||||
unsigned int reg_mqs_ctrl;
|
||||
|
||||
struct clk *mclk;
|
||||
struct clk *ipg;
|
||||
|
||||
unsigned long mclk_rate;
|
||||
|
||||
int sysclk_rate;
|
||||
int bclk;
|
||||
int lrclk;
|
||||
bool use_gpr;
|
||||
char name[32];
|
||||
};
|
||||
|
||||
#define FSL_MQS_RATES SNDRV_PCM_RATE_8000_192000
|
||||
#define FSL_MQS_FORMATS SNDRV_PCM_FMTBIT_S16_LE
|
||||
|
||||
static int fsl_mqs_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
|
||||
int div, res;
|
||||
|
||||
mqs_priv->mclk_rate = clk_get_rate(mqs_priv->mclk);
|
||||
|
||||
mqs_priv->bclk = snd_soc_params_to_bclk(params);
|
||||
mqs_priv->lrclk = params_rate(params);
|
||||
|
||||
/*
|
||||
* mclk_rate / (oversample(32,64) * FS * 2 * divider ) = repeat_rate;
|
||||
* if repeat_rate is 8, mqs can achieve better quality.
|
||||
* oversample rate is fix to 32 currently.
|
||||
*/
|
||||
div = mqs_priv->mclk_rate / (32 * 2 * mqs_priv->lrclk * 8);
|
||||
res = mqs_priv->mclk_rate % (32 * 2 * mqs_priv->lrclk * 8);
|
||||
|
||||
if (res == 0 && div > 0 && div <= 256) {
|
||||
if (mqs_priv->use_gpr) {
|
||||
regmap_update_bits(mqs_priv->gpr, IOMUXC_GPR2,
|
||||
IMX6SX_GPR2_MQS_CLK_DIV_MASK,
|
||||
(div-1) << IMX6SX_GPR2_MQS_CLK_DIV_SHIFT);
|
||||
regmap_update_bits(mqs_priv->gpr, IOMUXC_GPR2,
|
||||
IMX6SX_GPR2_MQS_OVERSAMPLE_MASK,
|
||||
0 << IMX6SX_GPR2_MQS_OVERSAMPLE_SHIFT);
|
||||
} else {
|
||||
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
MQS_CLK_DIV_MASK,
|
||||
(div-1) << MQS_CLK_DIV_SHIFT);
|
||||
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
MQS_OVERSAMPLE_MASK,
|
||||
0 << MQS_OVERSAMPLE_SHIFT);
|
||||
}
|
||||
} else
|
||||
dev_err(&mqs_priv->pdev->dev, "can't get proper divider\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_mqs_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_mqs_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
unsigned int freq, int dir)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
|
||||
|
||||
mqs_priv->sysclk_rate = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_mqs_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (mqs_priv->use_gpr)
|
||||
regmap_update_bits(mqs_priv->gpr, IOMUXC_GPR2, IMX6SX_GPR2_MQS_EN_MASK,
|
||||
1 << IMX6SX_GPR2_MQS_EN_SHIFT);
|
||||
else
|
||||
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
MQS_EN_MASK,
|
||||
1 << MQS_EN_SHIFT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_mqs_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (mqs_priv->use_gpr)
|
||||
regmap_update_bits(mqs_priv->gpr, IOMUXC_GPR2,
|
||||
IMX6SX_GPR2_MQS_EN_MASK, 0);
|
||||
else
|
||||
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
MQS_EN_MASK, 0);
|
||||
}
|
||||
|
||||
static struct snd_soc_component_driver soc_codec_fsl_mqs = {
|
||||
.idle_bias_on = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_ops fsl_mqs_dai_ops = {
|
||||
.startup = fsl_mqs_startup,
|
||||
.shutdown = fsl_mqs_shutdown,
|
||||
.hw_params = fsl_mqs_hw_params,
|
||||
.set_fmt = fsl_mqs_set_dai_fmt,
|
||||
.set_sysclk = fsl_mqs_set_dai_sysclk,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver fsl_mqs_dai = {
|
||||
.name = "fsl-mqs-dai",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = FSL_MQS_RATES,
|
||||
.formats = FSL_MQS_FORMATS,
|
||||
},
|
||||
.ops = &fsl_mqs_dai_ops,
|
||||
};
|
||||
|
||||
static const struct regmap_config fsl_mqs_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = REG_MQS_CTRL,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static int fsl_mqs_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *gpr_np = 0;
|
||||
struct fsl_mqs *mqs_priv;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int ret = 0;
|
||||
|
||||
mqs_priv = devm_kzalloc(&pdev->dev, sizeof(*mqs_priv), GFP_KERNEL);
|
||||
if (!mqs_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mqs_priv->pdev = pdev;
|
||||
strncpy(mqs_priv->name, np->name, sizeof(mqs_priv->name) - 1);
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,imx8qm-mqs"))
|
||||
mqs_priv->use_gpr = false;
|
||||
else
|
||||
mqs_priv->use_gpr = true;
|
||||
|
||||
if (mqs_priv->use_gpr) {
|
||||
gpr_np = of_parse_phandle(np, "gpr", 0);
|
||||
if (IS_ERR(gpr_np)) {
|
||||
dev_err(&pdev->dev, "failed to get gpr node by phandle\n");
|
||||
ret = PTR_ERR(gpr_np);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mqs_priv->gpr = syscon_node_to_regmap(gpr_np);
|
||||
if (IS_ERR(mqs_priv->gpr)) {
|
||||
dev_err(&pdev->dev, "failed to get gpr regmap\n");
|
||||
ret = PTR_ERR(mqs_priv->gpr);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
mqs_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
|
||||
"core", regs, &fsl_mqs_regmap_config);
|
||||
if (IS_ERR(mqs_priv->regmap)) {
|
||||
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
|
||||
PTR_ERR(mqs_priv->regmap));
|
||||
return PTR_ERR(mqs_priv->regmap);
|
||||
}
|
||||
|
||||
mqs_priv->ipg = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(mqs_priv->ipg)) {
|
||||
dev_err(&pdev->dev, "failed to get the clock: %ld\n",
|
||||
PTR_ERR(mqs_priv->ipg));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
mqs_priv->mclk = devm_clk_get(&pdev->dev, "mclk");
|
||||
if (IS_ERR(mqs_priv->mclk)) {
|
||||
dev_err(&pdev->dev, "failed to get the clock: %ld\n",
|
||||
PTR_ERR(mqs_priv->mclk));
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, mqs_priv);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return devm_snd_soc_register_component(&pdev->dev, &soc_codec_fsl_mqs,
|
||||
&fsl_mqs_dai, 1);
|
||||
out:
|
||||
if (!IS_ERR(gpr_np))
|
||||
of_node_put(gpr_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_mqs_remove(struct platform_device *pdev)
|
||||
{
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_mqs_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
|
||||
|
||||
if (mqs_priv->ipg)
|
||||
clk_prepare_enable(mqs_priv->ipg);
|
||||
|
||||
if (mqs_priv->mclk)
|
||||
clk_prepare_enable(mqs_priv->mclk);
|
||||
|
||||
if (mqs_priv->use_gpr)
|
||||
regmap_write(mqs_priv->gpr, IOMUXC_GPR2,
|
||||
mqs_priv->reg_iomuxc_gpr2);
|
||||
else
|
||||
regmap_write(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
mqs_priv->reg_mqs_ctrl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_mqs_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
|
||||
|
||||
if (mqs_priv->use_gpr)
|
||||
regmap_read(mqs_priv->gpr, IOMUXC_GPR2,
|
||||
&mqs_priv->reg_iomuxc_gpr2);
|
||||
else
|
||||
regmap_read(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
&mqs_priv->reg_mqs_ctrl);
|
||||
|
||||
if (mqs_priv->mclk)
|
||||
clk_disable_unprepare(mqs_priv->mclk);
|
||||
|
||||
if (mqs_priv->ipg)
|
||||
clk_disable_unprepare(mqs_priv->ipg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops fsl_mqs_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(fsl_mqs_runtime_suspend,
|
||||
fsl_mqs_runtime_resume,
|
||||
NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id fsl_mqs_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx8qm-mqs", },
|
||||
{ .compatible = "fsl,imx6sx-mqs", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids);
|
||||
|
||||
|
||||
static struct platform_driver fsl_mqs_driver = {
|
||||
.probe = fsl_mqs_probe,
|
||||
.remove = fsl_mqs_remove,
|
||||
.driver = {
|
||||
.name = "fsl-mqs",
|
||||
.of_match_table = fsl_mqs_dt_ids,
|
||||
.pm = &fsl_mqs_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(fsl_mqs_driver);
|
||||
|
||||
MODULE_AUTHOR("shengjiu wang <shengjiu.wang@freescale.com>");
|
||||
MODULE_DESCRIPTION("MQS dummy codec driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform: fsl-mqs");
|
|
@ -281,6 +281,7 @@ struct hdmi_codec_priv {
|
|||
|
||||
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
|
||||
SND_SOC_DAPM_OUTPUT("TX"),
|
||||
SND_SOC_DAPM_OUTPUT("RX"),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -388,6 +389,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
int ret = 0;
|
||||
|
||||
ret = test_and_set_bit(0, &hcp->busy);
|
||||
|
@ -402,7 +404,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (hcp->hcd.ops->get_eld) {
|
||||
if (tx && hcp->hcd.ops->get_eld) {
|
||||
ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data,
|
||||
hcp->eld, sizeof(hcp->eld));
|
||||
|
||||
|
@ -471,13 +473,11 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
|||
/* Select a channel allocation that matches with ELD and pcm channels */
|
||||
idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels);
|
||||
if (idx < 0) {
|
||||
dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
|
||||
idx);
|
||||
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
|
||||
return idx;
|
||||
} else {
|
||||
hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
|
||||
hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
|
||||
}
|
||||
hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
|
||||
hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
|
||||
|
||||
hp.sample_width = params_width(params);
|
||||
hp.sample_rate = params_rate(params);
|
||||
|
@ -647,14 +647,20 @@ static int hdmi_dai_probe(struct snd_soc_dai *dai)
|
|||
{
|
||||
struct snd_soc_dapm_context *dapm;
|
||||
struct hdmi_codec_daifmt *daifmt;
|
||||
struct snd_soc_dapm_route route = {
|
||||
.sink = "TX",
|
||||
.source = dai->driver->playback.stream_name,
|
||||
struct snd_soc_dapm_route route[] = {
|
||||
{
|
||||
.sink = "TX",
|
||||
.source = dai->driver->playback.stream_name,
|
||||
},
|
||||
{
|
||||
.sink = dai->driver->playback.stream_name,
|
||||
.source = "RX",
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
dapm = snd_soc_component_get_dapm(dai->component);
|
||||
ret = snd_soc_dapm_add_routes(dapm, &route, 1);
|
||||
ret = snd_soc_dapm_add_routes(dapm, route, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -744,6 +750,14 @@ static const struct snd_soc_dai_driver hdmi_i2s_dai = {
|
|||
.sig_bits = 24,
|
||||
},
|
||||
.ops = &hdmi_codec_i2s_dai_ops,
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.rates = HDMI_RATES,
|
||||
.formats = I2S_FORMATS,
|
||||
.sig_bits = 24,
|
||||
},
|
||||
.pcm_new = hdmi_codec_pcm_new,
|
||||
};
|
||||
|
||||
|
@ -760,6 +774,13 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = {
|
|||
.formats = SPDIF_FORMATS,
|
||||
},
|
||||
.ops = &hdmi_codec_spdif_dai_ops,
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = HDMI_RATES,
|
||||
.formats = SPDIF_FORMATS,
|
||||
},
|
||||
.pcm_new = hdmi_codec_pcm_new,
|
||||
};
|
||||
|
||||
|
|
|
@ -1185,8 +1185,10 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
|
||||
if (irq < 0)
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "failed to get mbhc switch irq\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
pm8916_mbhc_switch_irq_handler,
|
||||
|
@ -1198,8 +1200,10 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
|
|||
|
||||
if (priv->mbhc_btn_enabled) {
|
||||
irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
|
||||
if (irq < 0)
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "failed to get button press irq\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
mbhc_btn_press_irq_handler,
|
||||
|
@ -1210,8 +1214,10 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
|
|||
dev_err(dev, "cannot request mbhc button press irq\n");
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det");
|
||||
if (irq < 0)
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "failed to get button release irq\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
mbhc_btn_release_irq_handler,
|
||||
|
|
1110
sound/soc/codecs/rpmsg_ak4497.c
Normal file
1110
sound/soc/codecs/rpmsg_ak4497.c
Normal file
File diff suppressed because it is too large
Load Diff
90
sound/soc/codecs/rpmsg_ak4497.h
Normal file
90
sound/soc/codecs/rpmsg_ak4497.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* ak4497.h -- audio driver for ak4497
|
||||
*
|
||||
* Copyright (C) 2016 Asahi Kasei Microdevices Corporation
|
||||
* Copyright (C) 2017, NXP
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#ifndef _RPMSG_AK4497_H
|
||||
#define _RPMSG_AK4497_H
|
||||
|
||||
#define AK4497_00_CONTROL1 0x00
|
||||
#define AK4497_01_CONTROL2 0x01
|
||||
#define AK4497_02_CONTROL3 0x02
|
||||
#define AK4497_03_LCHATT 0x03
|
||||
#define AK4497_04_RCHATT 0x04
|
||||
#define AK4497_05_CONTROL4 0x05
|
||||
#define AK4497_06_DSD1 0x06
|
||||
#define AK4497_07_CONTROL5 0x07
|
||||
#define AK4497_08_SOUNDCONTROL 0x08
|
||||
#define AK4497_09_DSD2 0x09
|
||||
#define AK4497_0A_CONTROL7 0x0A
|
||||
#define AK4497_0B_CONTROL8 0x0B
|
||||
#define AK4497_0C_RESERVED 0x0C
|
||||
#define AK4497_0D_RESERVED 0x0D
|
||||
#define AK4497_0E_RESERVED 0x0E
|
||||
#define AK4497_0F_RESERVED 0x0F
|
||||
#define AK4497_10_RESERVED 0x10
|
||||
#define AK4497_11_RESERVED 0x11
|
||||
#define AK4497_12_RESERVED 0x12
|
||||
#define AK4497_13_RESERVED 0x13
|
||||
#define AK4497_14_RESERVED 0x14
|
||||
#define AK4497_15_DFSREAD 0x15
|
||||
|
||||
|
||||
#define AK4497_MAX_REGISTERS (AK4497_15_DFSREAD)
|
||||
|
||||
/* Bitfield Definitions */
|
||||
|
||||
/* AK4497_00_CONTROL1 (0x00) Fields */
|
||||
#define AK4497_DIF 0x0E
|
||||
#define AK4497_DIF_MSB_MODE (2 << 1)
|
||||
#define AK4497_DIF_I2S_MODE (3 << 1)
|
||||
#define AK4497_DIF_32BIT_MODE (4 << 1)
|
||||
|
||||
#define AK4497_DIF_16BIT_LSB (0 << 1)
|
||||
#define AK4497_DIF_20BIT_LSB (1 << 1)
|
||||
#define AK4497_DIF_24BIT_MSB (2 << 1)
|
||||
#define AK4497_DIF_24BIT_I2S (3 << 1)
|
||||
#define AK4497_DIF_24BIT_LSB (4 << 1)
|
||||
#define AK4497_DIF_32BIT_LSB (5 << 1)
|
||||
#define AK4497_DIF_32BIT_MSB (6 << 1)
|
||||
#define AK4497_DIF_32BIT_I2S (7 << 1)
|
||||
|
||||
/* AK4497_02_CONTROL3 (0x02) Fields */
|
||||
#define AK4497_DIF_DSD 0x80
|
||||
#define AK4497_DIF_DSD_MODE (1 << 7)
|
||||
|
||||
|
||||
/* AK4497_01_CONTROL2 (0x01) Fields */
|
||||
/* AK4497_05_CONTROL4 (0x05) Fields */
|
||||
#define AK4497_DFS 0x18
|
||||
#define AK4497_DFS_48KHZ (0x0 << 3) // 30kHz to 54kHz
|
||||
#define AK4497_DFS_96KHZ (0x1 << 3) // 54kHz to 108kHz
|
||||
#define AK4497_DFS_192KHZ (0x2 << 3) // 120kHz to 216kHz
|
||||
#define AK4497_DFS_384KHZ (0x0 << 3)
|
||||
#define AK4497_DFS_768KHZ (0x1 << 3)
|
||||
|
||||
#define AK4497_DFS2 0x2
|
||||
#define AK4497_DFS2_48KHZ (0x0 << 1) // 30kHz to 216kHz
|
||||
#define AK4497_DFS2_384KHZ (0x1 << 1) // 384kHz, 768kHz to 108kHz
|
||||
|
||||
|
||||
#define AK4497_DSDSEL0 0x1
|
||||
#define AK4497_DSDSEL0_2MHZ 0x0
|
||||
#define AK4497_DSDSEL0_5MHZ 0x1
|
||||
#define AK4497_DSDSEL0_11MHZ 0x0
|
||||
#define AK4497_DSDSEL0_22MHZ 0x1
|
||||
|
||||
#define AK4497_DSDSEL1 0x1
|
||||
#define AK4497_DSDSEL1_2MHZ 0x0
|
||||
#define AK4497_DSDSEL1_5MHZ 0x0
|
||||
#define AK4497_DSDSEL1_11MHZ 0x1
|
||||
#define AK4497_DSDSEL1_22MHZ 0x1
|
||||
|
||||
#endif
|
752
sound/soc/codecs/rpmsg_cs42xx8.c
Normal file
752
sound/soc/codecs/rpmsg_cs42xx8.c
Normal file
|
@ -0,0 +1,752 @@
|
|||
/*
|
||||
* Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver
|
||||
*
|
||||
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Author: Nicolin Chen <Guangyu.Chen@freescale.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "rpmsg_cs42xx8.h"
|
||||
#include "../fsl/fsl_rpmsg_i2s.h"
|
||||
|
||||
#define CS42XX8_NUM_SUPPLIES 4
|
||||
static const char *const cs42xx8_supply_names[CS42XX8_NUM_SUPPLIES] = {
|
||||
"VA",
|
||||
"VD",
|
||||
"VLS",
|
||||
"VLC",
|
||||
};
|
||||
|
||||
#define CS42XX8_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE)
|
||||
|
||||
/* codec private data */
|
||||
struct rpmsg_cs42xx8_priv {
|
||||
struct regulator_bulk_data supplies[CS42XX8_NUM_SUPPLIES];
|
||||
struct cs42xx8_driver_data *drvdata;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
|
||||
bool slave_mode[2];
|
||||
unsigned long sysclk;
|
||||
u32 tx_channels;
|
||||
int rate[2];
|
||||
int reset_gpio;
|
||||
int audioindex;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s;
|
||||
};
|
||||
|
||||
/* -127.5dB to 0dB with step of 0.5dB */
|
||||
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
|
||||
/* -64dB to 24dB with step of 0.5dB */
|
||||
static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 0);
|
||||
|
||||
static const char *const cs42xx8_adc_single[] = { "Differential", "Single-Ended" };
|
||||
static const char *const cs42xx8_szc[] = { "Immediate Change", "Zero Cross",
|
||||
"Soft Ramp", "Soft Ramp on Zero Cross" };
|
||||
|
||||
static const struct soc_enum adc1_single_enum =
|
||||
SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 4, 2, cs42xx8_adc_single);
|
||||
static const struct soc_enum adc2_single_enum =
|
||||
SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 3, 2, cs42xx8_adc_single);
|
||||
static const struct soc_enum adc3_single_enum =
|
||||
SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 2, 2, cs42xx8_adc_single);
|
||||
static const struct soc_enum dac_szc_enum =
|
||||
SOC_ENUM_SINGLE(CS42XX8_TXCTL, 5, 4, cs42xx8_szc);
|
||||
static const struct soc_enum adc_szc_enum =
|
||||
SOC_ENUM_SINGLE(CS42XX8_TXCTL, 0, 4, cs42xx8_szc);
|
||||
|
||||
static const struct snd_kcontrol_new cs42xx8_snd_controls[] = {
|
||||
SOC_DOUBLE_R_TLV("DAC1 Playback Volume", CS42XX8_VOLAOUT1,
|
||||
CS42XX8_VOLAOUT2, 0, 0xff, 1, dac_tlv),
|
||||
SOC_DOUBLE_R_TLV("DAC2 Playback Volume", CS42XX8_VOLAOUT3,
|
||||
CS42XX8_VOLAOUT4, 0, 0xff, 1, dac_tlv),
|
||||
SOC_DOUBLE_R_TLV("DAC3 Playback Volume", CS42XX8_VOLAOUT5,
|
||||
CS42XX8_VOLAOUT6, 0, 0xff, 1, dac_tlv),
|
||||
SOC_DOUBLE_R_TLV("DAC4 Playback Volume", CS42XX8_VOLAOUT7,
|
||||
CS42XX8_VOLAOUT8, 0, 0xff, 1, dac_tlv),
|
||||
SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", CS42XX8_VOLAIN1,
|
||||
CS42XX8_VOLAIN2, 0, -0x80, 0x30, 7, 0, adc_tlv),
|
||||
SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", CS42XX8_VOLAIN3,
|
||||
CS42XX8_VOLAIN4, 0, -0x80, 0x30, 7, 0, adc_tlv),
|
||||
SOC_DOUBLE("DAC1 Invert Switch", CS42XX8_DACINV, 0, 1, 1, 0),
|
||||
SOC_DOUBLE("DAC2 Invert Switch", CS42XX8_DACINV, 2, 3, 1, 0),
|
||||
SOC_DOUBLE("DAC3 Invert Switch", CS42XX8_DACINV, 4, 5, 1, 0),
|
||||
SOC_DOUBLE("DAC4 Invert Switch", CS42XX8_DACINV, 6, 7, 1, 0),
|
||||
SOC_DOUBLE("ADC1 Invert Switch", CS42XX8_ADCINV, 0, 1, 1, 0),
|
||||
SOC_DOUBLE("ADC2 Invert Switch", CS42XX8_ADCINV, 2, 3, 1, 0),
|
||||
SOC_SINGLE("ADC High-Pass Filter Switch", CS42XX8_ADCCTL, 7, 1, 1),
|
||||
SOC_SINGLE("DAC De-emphasis Switch", CS42XX8_ADCCTL, 5, 1, 0),
|
||||
SOC_ENUM("ADC1 Single Ended Mode Switch", adc1_single_enum),
|
||||
SOC_ENUM("ADC2 Single Ended Mode Switch", adc2_single_enum),
|
||||
SOC_SINGLE("DAC Single Volume Control Switch", CS42XX8_TXCTL, 7, 1, 0),
|
||||
SOC_ENUM("DAC Soft Ramp & Zero Cross Control Switch", dac_szc_enum),
|
||||
SOC_SINGLE("DAC Auto Mute Switch", CS42XX8_TXCTL, 4, 1, 0),
|
||||
SOC_SINGLE("Mute ADC Serial Port Switch", CS42XX8_TXCTL, 3, 1, 0),
|
||||
SOC_SINGLE("ADC Single Volume Control Switch", CS42XX8_TXCTL, 2, 1, 0),
|
||||
SOC_ENUM("ADC Soft Ramp & Zero Cross Control Switch", adc_szc_enum),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new cs42xx8_adc3_snd_controls[] = {
|
||||
SOC_DOUBLE_R_S_TLV("ADC3 Capture Volume", CS42XX8_VOLAIN5,
|
||||
CS42XX8_VOLAIN6, 0, -0x80, 0x30, 7, 0, adc_tlv),
|
||||
SOC_DOUBLE("ADC3 Invert Switch", CS42XX8_ADCINV, 4, 5, 1, 0),
|
||||
SOC_ENUM("ADC3 Single Ended Mode Switch", adc3_single_enum),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DAC1", "Playback", CS42XX8_PWRCTL, 1, 1),
|
||||
SND_SOC_DAPM_DAC("DAC2", "Playback", CS42XX8_PWRCTL, 2, 1),
|
||||
SND_SOC_DAPM_DAC("DAC3", "Playback", CS42XX8_PWRCTL, 3, 1),
|
||||
SND_SOC_DAPM_DAC("DAC4", "Playback", CS42XX8_PWRCTL, 4, 1),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("AOUT1L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT1R"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT2L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT2R"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT3L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT3R"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT4L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT4R"),
|
||||
|
||||
SND_SOC_DAPM_ADC("ADC1", "Capture", CS42XX8_PWRCTL, 5, 1),
|
||||
SND_SOC_DAPM_ADC("ADC2", "Capture", CS42XX8_PWRCTL, 6, 1),
|
||||
|
||||
SND_SOC_DAPM_INPUT("AIN1L"),
|
||||
SND_SOC_DAPM_INPUT("AIN1R"),
|
||||
SND_SOC_DAPM_INPUT("AIN2L"),
|
||||
SND_SOC_DAPM_INPUT("AIN2R"),
|
||||
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_ADC("ADC3", "Capture", CS42XX8_PWRCTL, 7, 1),
|
||||
|
||||
SND_SOC_DAPM_INPUT("AIN3L"),
|
||||
SND_SOC_DAPM_INPUT("AIN3R"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = {
|
||||
/* Playback */
|
||||
{ "AOUT1L", NULL, "DAC1" },
|
||||
{ "AOUT1R", NULL, "DAC1" },
|
||||
|
||||
{ "AOUT2L", NULL, "DAC2" },
|
||||
{ "AOUT2R", NULL, "DAC2" },
|
||||
|
||||
{ "AOUT3L", NULL, "DAC3" },
|
||||
{ "AOUT3R", NULL, "DAC3" },
|
||||
|
||||
{ "AOUT4L", NULL, "DAC4" },
|
||||
{ "AOUT4R", NULL, "DAC4" },
|
||||
|
||||
/* Capture */
|
||||
{ "ADC1", NULL, "AIN1L" },
|
||||
{ "ADC1", NULL, "AIN1R" },
|
||||
|
||||
{ "ADC2", NULL, "AIN2L" },
|
||||
{ "ADC2", NULL, "AIN2R" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = {
|
||||
/* Capture */
|
||||
{ "ADC3", NULL, "AIN3L" },
|
||||
{ "ADC3", NULL, "AIN3R" },
|
||||
};
|
||||
|
||||
struct cs42xx8_ratios {
|
||||
unsigned int mfreq;
|
||||
unsigned int min_mclk;
|
||||
unsigned int max_mclk;
|
||||
unsigned int ratio[3];
|
||||
};
|
||||
|
||||
static const struct cs42xx8_ratios cs42xx8_ratios[] = {
|
||||
{ 0, 1029000, 12800000, {256, 128, 64} },
|
||||
{ 2, 1536000, 19200000, {384, 192, 96} },
|
||||
{ 4, 2048000, 25600000, {512, 256, 128} },
|
||||
{ 6, 3072000, 38400000, {768, 384, 192} },
|
||||
{ 8, 4096000, 51200000, {1024, 512, 256} },
|
||||
};
|
||||
|
||||
static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
cs42xx8->sysclk = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs42xx8_set_dai_fmt(struct snd_soc_dai *dai,
|
||||
unsigned int format)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
|
||||
u32 val;
|
||||
|
||||
/* Set DAI format */
|
||||
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
val = CS42XX8_INTF_DAC_DIF_LEFTJ | CS42XX8_INTF_ADC_DIF_LEFTJ;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
val = CS42XX8_INTF_DAC_DIF_I2S | CS42XX8_INTF_ADC_DIF_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
val = CS42XX8_INTF_DAC_DIF_RIGHTJ | CS42XX8_INTF_ADC_DIF_RIGHTJ;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "unsupported dai format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_update_bits(cs42xx8->regmap, CS42XX8_INTF,
|
||||
CS42XX8_INTF_DAC_DIF_MASK |
|
||||
CS42XX8_INTF_ADC_DIF_MASK, val);
|
||||
|
||||
if (cs42xx8->slave_mode[0] == cs42xx8->slave_mode[1]) {
|
||||
/* Set master/slave audio interface */
|
||||
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
cs42xx8->slave_mode[0] = true;
|
||||
cs42xx8->slave_mode[1] = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
cs42xx8->slave_mode[0] = false;
|
||||
cs42xx8->slave_mode[1] = false;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "unsupported master/slave mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
u32 ratio[2];
|
||||
u32 rate[2];
|
||||
u32 fm[2];
|
||||
u32 i, val, mask;
|
||||
bool condition1, condition2;
|
||||
|
||||
if (tx)
|
||||
cs42xx8->tx_channels = params_channels(params);
|
||||
|
||||
rate[tx] = params_rate(params);
|
||||
rate[!tx] = cs42xx8->rate[!tx];
|
||||
|
||||
ratio[tx] = rate[tx] > 0 ? cs42xx8->sysclk / rate[tx] : 0;
|
||||
ratio[!tx] = rate[!tx] > 0 ? cs42xx8->sysclk / rate[!tx] : 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (cs42xx8->slave_mode[i]) {
|
||||
fm[i] = CS42XX8_FM_AUTO;
|
||||
} else {
|
||||
if (rate[i] < 50000)
|
||||
fm[i] = CS42XX8_FM_SINGLE;
|
||||
else if (rate[i] > 50000 && rate[i] < 100000)
|
||||
fm[i] = CS42XX8_FM_DOUBLE;
|
||||
else if (rate[i] > 100000 && rate[i] < 200000)
|
||||
fm[i] = CS42XX8_FM_QUAD;
|
||||
else {
|
||||
dev_err(component->dev,
|
||||
"unsupported sample rate or rate combine\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
|
||||
condition1 = ((fm[tx] == CS42XX8_FM_AUTO) ?
|
||||
(cs42xx8_ratios[i].ratio[0] == ratio[tx] ||
|
||||
cs42xx8_ratios[i].ratio[1] == ratio[tx] ||
|
||||
cs42xx8_ratios[i].ratio[2] == ratio[tx]) :
|
||||
(cs42xx8_ratios[i].ratio[fm[tx]] == ratio[tx])) &&
|
||||
cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk &&
|
||||
cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk;
|
||||
|
||||
if (ratio[tx] <= 0)
|
||||
condition1 = true;
|
||||
|
||||
condition2 = ((fm[!tx] == CS42XX8_FM_AUTO) ?
|
||||
(cs42xx8_ratios[i].ratio[0] == ratio[!tx] ||
|
||||
cs42xx8_ratios[i].ratio[1] == ratio[!tx] ||
|
||||
cs42xx8_ratios[i].ratio[2] == ratio[!tx]) :
|
||||
(cs42xx8_ratios[i].ratio[fm[!tx]] == ratio[!tx]));
|
||||
|
||||
if (ratio[!tx] <= 0)
|
||||
condition2 = true;
|
||||
|
||||
if (condition1 && condition2)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(cs42xx8_ratios)) {
|
||||
dev_err(component->dev, "unsupported sysclk ratio\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs42xx8->rate[tx] = params_rate(params);
|
||||
|
||||
mask = CS42XX8_FUNCMOD_MFREQ_MASK;
|
||||
val = cs42xx8_ratios[i].mfreq;
|
||||
|
||||
regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
|
||||
CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask,
|
||||
CS42XX8_FUNCMOD_xC_FM(tx, fm[tx]) | val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs42xx8_hw_free(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
|
||||
cs42xx8->rate[tx] = 0;
|
||||
|
||||
regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
|
||||
CS42XX8_FUNCMOD_xC_FM_MASK(tx),
|
||||
CS42XX8_FUNCMOD_xC_FM(tx, CS42XX8_FM_AUTO));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
|
||||
u8 dac_unmute = cs42xx8->tx_channels ?
|
||||
~((0x1 << cs42xx8->tx_channels) - 1) : 0;
|
||||
|
||||
regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE,
|
||||
mute ? CS42XX8_DACMUTE_ALL : dac_unmute);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops cs42xx8_dai_ops = {
|
||||
.set_fmt = cs42xx8_set_dai_fmt,
|
||||
.set_sysclk = cs42xx8_set_dai_sysclk,
|
||||
.hw_params = cs42xx8_hw_params,
|
||||
.hw_free = cs42xx8_hw_free,
|
||||
.digital_mute = cs42xx8_digital_mute,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver cs42xx8_dai = {
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = CS42XX8_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = CS42XX8_FORMATS,
|
||||
},
|
||||
.ops = &cs42xx8_dai_ops,
|
||||
};
|
||||
|
||||
static const struct reg_default cs42xx8_reg[] = {
|
||||
{ 0x02, 0x00 }, /* Power Control */
|
||||
{ 0x03, 0xF0 }, /* Functional Mode */
|
||||
{ 0x04, 0x46 }, /* Interface Formats */
|
||||
{ 0x05, 0x00 }, /* ADC Control & DAC De-Emphasis */
|
||||
{ 0x06, 0x10 }, /* Transition Control */
|
||||
{ 0x07, 0x00 }, /* DAC Channel Mute */
|
||||
{ 0x08, 0x00 }, /* Volume Control AOUT1 */
|
||||
{ 0x09, 0x00 }, /* Volume Control AOUT2 */
|
||||
{ 0x0a, 0x00 }, /* Volume Control AOUT3 */
|
||||
{ 0x0b, 0x00 }, /* Volume Control AOUT4 */
|
||||
{ 0x0c, 0x00 }, /* Volume Control AOUT5 */
|
||||
{ 0x0d, 0x00 }, /* Volume Control AOUT6 */
|
||||
{ 0x0e, 0x00 }, /* Volume Control AOUT7 */
|
||||
{ 0x0f, 0x00 }, /* Volume Control AOUT8 */
|
||||
{ 0x10, 0x00 }, /* DAC Channel Invert */
|
||||
{ 0x11, 0x00 }, /* Volume Control AIN1 */
|
||||
{ 0x12, 0x00 }, /* Volume Control AIN2 */
|
||||
{ 0x13, 0x00 }, /* Volume Control AIN3 */
|
||||
{ 0x14, 0x00 }, /* Volume Control AIN4 */
|
||||
{ 0x15, 0x00 }, /* Volume Control AIN5 */
|
||||
{ 0x16, 0x00 }, /* Volume Control AIN6 */
|
||||
{ 0x17, 0x00 }, /* ADC Channel Invert */
|
||||
{ 0x18, 0x00 }, /* Status Control */
|
||||
{ 0x1a, 0x00 }, /* Status Mask */
|
||||
{ 0x1b, 0x00 }, /* MUTEC Pin Control */
|
||||
};
|
||||
|
||||
static bool cs42xx8_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS42XX8_STATUS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool cs42xx8_writeable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS42XX8_CHIPID:
|
||||
case CS42XX8_STATUS:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static int rpmsg_cs42xx8_read(void *context, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8 = context;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = cs42xx8->rpmsg_i2s;
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg_s *rpmsg = &i2s_info->rpmsg[GET_CODEC_VALUE].send_msg;
|
||||
int err, reg_val;
|
||||
|
||||
mutex_lock(&i2s_info->i2c_lock);
|
||||
rpmsg->param.audioindex = cs42xx8->audioindex;
|
||||
rpmsg->param.buffer_addr = reg;
|
||||
rpmsg->header.cmd = GET_CODEC_VALUE;
|
||||
err = i2s_info->send_message(&i2s_info->rpmsg[GET_CODEC_VALUE], i2s_info);
|
||||
reg_val = i2s_info->rpmsg[GET_CODEC_VALUE].recv_msg.param.reg_data;
|
||||
mutex_unlock(&i2s_info->i2c_lock);
|
||||
if (err)
|
||||
return -EIO;
|
||||
|
||||
*val = reg_val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rpmsg_cs42xx8_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8 = context;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = cs42xx8->rpmsg_i2s;
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg_s *rpmsg = &i2s_info->rpmsg[SET_CODEC_VALUE].send_msg;
|
||||
int err;
|
||||
|
||||
mutex_lock(&i2s_info->i2c_lock);
|
||||
rpmsg->param.audioindex = cs42xx8->audioindex;
|
||||
rpmsg->param.buffer_addr = reg;
|
||||
rpmsg->param.buffer_size = val;
|
||||
rpmsg->header.cmd = SET_CODEC_VALUE;
|
||||
err = i2s_info->send_message(&i2s_info->rpmsg[SET_CODEC_VALUE], i2s_info);
|
||||
mutex_unlock(&i2s_info->i2c_lock);
|
||||
if (err)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct regmap_config rpmsg_cs42xx8_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = CS42XX8_LASTREG,
|
||||
.reg_defaults = cs42xx8_reg,
|
||||
.num_reg_defaults = ARRAY_SIZE(cs42xx8_reg),
|
||||
.volatile_reg = cs42xx8_volatile_register,
|
||||
.writeable_reg = cs42xx8_writeable_register,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
|
||||
.reg_read = rpmsg_cs42xx8_read,
|
||||
.reg_write = rpmsg_cs42xx8_write,
|
||||
};
|
||||
|
||||
static int cs42xx8_codec_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
|
||||
|
||||
switch (cs42xx8->drvdata->num_adcs) {
|
||||
case 3:
|
||||
snd_soc_add_component_controls(component, cs42xx8_adc3_snd_controls,
|
||||
ARRAY_SIZE(cs42xx8_adc3_snd_controls));
|
||||
snd_soc_dapm_new_controls(dapm, cs42xx8_adc3_dapm_widgets,
|
||||
ARRAY_SIZE(cs42xx8_adc3_dapm_widgets));
|
||||
snd_soc_dapm_add_routes(dapm, cs42xx8_adc3_dapm_routes,
|
||||
ARRAY_SIZE(cs42xx8_adc3_dapm_routes));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Mute all DAC channels */
|
||||
regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, CS42XX8_DACMUTE_ALL);
|
||||
regmap_update_bits(cs42xx8->regmap, CS42XX8_PWRCTL,
|
||||
CS42XX8_PWRCTL_PDN_MASK, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver cs42xx8_driver = {
|
||||
.probe = cs42xx8_codec_probe,
|
||||
.controls = cs42xx8_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(cs42xx8_snd_controls),
|
||||
.dapm_widgets = cs42xx8_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets),
|
||||
.dapm_routes = cs42xx8_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes),
|
||||
.suspend_bias_off = 1,
|
||||
.idle_bias_on = 1,
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static int rpmsg_cs42xx8_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(pdev->dev.parent);
|
||||
struct fsl_rpmsg_codec *pdata = pdev->dev.platform_data;
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret, val, i;
|
||||
|
||||
cs42xx8 = devm_kzalloc(&pdev->dev, sizeof(*cs42xx8), GFP_KERNEL);
|
||||
if (cs42xx8 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
cs42xx8->regmap = devm_regmap_init(&pdev->dev, NULL,
|
||||
cs42xx8, &rpmsg_cs42xx8_regmap_config);
|
||||
if (IS_ERR(cs42xx8->regmap))
|
||||
return PTR_ERR(cs42xx8->regmap);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, cs42xx8);
|
||||
|
||||
cs42xx8->drvdata = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct cs42xx8_driver_data), GFP_KERNEL);
|
||||
if (!cs42xx8->drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
cs42xx8->rpmsg_i2s = rpmsg_i2s;
|
||||
|
||||
memcpy(cs42xx8->drvdata->name, pdata->name, 32);
|
||||
cs42xx8->drvdata->num_adcs = pdata->num_adcs;
|
||||
cs42xx8->audioindex = pdata->audioindex;
|
||||
cs42xx8->reset_gpio = of_get_named_gpio(pdev->dev.parent->of_node,
|
||||
"reset-gpio", 0);
|
||||
if (gpio_is_valid(cs42xx8->reset_gpio)) {
|
||||
ret = devm_gpio_request_one(dev, cs42xx8->reset_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "cs42xx8 reset");
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to get reset gpio\n");
|
||||
return ret;
|
||||
}
|
||||
gpio_set_value_cansleep(cs42xx8->reset_gpio, 1);
|
||||
}
|
||||
|
||||
cs42xx8->clk = devm_clk_get(pdev->dev.parent, "mclk");
|
||||
if (IS_ERR(cs42xx8->clk)) {
|
||||
dev_err(dev, "failed to get the clock: %ld\n",
|
||||
PTR_ERR(cs42xx8->clk));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs42xx8->sysclk = clk_get_rate(cs42xx8->clk);
|
||||
|
||||
if (of_property_read_bool(pdev->dev.parent->of_node, "fsl,txm-rxs")) {
|
||||
/* 0 -- rx, 1 -- tx */
|
||||
cs42xx8->slave_mode[0] = true;
|
||||
cs42xx8->slave_mode[1] = false;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(pdev->dev.parent->of_node, "fsl,txs-rxm")) {
|
||||
/* 0 -- rx, 1 -- tx */
|
||||
cs42xx8->slave_mode[0] = false;
|
||||
cs42xx8->slave_mode[1] = true;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++)
|
||||
cs42xx8->supplies[i].supply = cs42xx8_supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(pdev->dev.parent,
|
||||
ARRAY_SIZE(cs42xx8->supplies), cs42xx8->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
|
||||
cs42xx8->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Make sure hardware reset done */
|
||||
usleep_range(5000, 10000);
|
||||
|
||||
/*
|
||||
* We haven't marked the chip revision as volatile due to
|
||||
* sharing a register with the right input volume; explicitly
|
||||
* bypass the cache to read it.
|
||||
*/
|
||||
regcache_cache_bypass(cs42xx8->regmap, true);
|
||||
|
||||
/* Validate the chip ID */
|
||||
ret = regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to get device ID, ret = %d", ret);
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
/* The top four bits of the chip ID should be 0000 */
|
||||
if (((val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4) != 0x00) {
|
||||
dev_err(dev, "unmatched chip ID: %d\n",
|
||||
(val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4);
|
||||
ret = -EINVAL;
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
dev_info(dev, "found device, revision %X\n",
|
||||
val & CS42XX8_CHIPID_REV_ID_MASK);
|
||||
|
||||
regcache_cache_bypass(cs42xx8->regmap, false);
|
||||
|
||||
cs42xx8_dai.name = cs42xx8->drvdata->name;
|
||||
|
||||
/* Each adc supports stereo input */
|
||||
cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_request_idle(dev);
|
||||
|
||||
ret = devm_snd_soc_register_component(dev, &cs42xx8_driver, &cs42xx8_dai, 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register codec:%d\n", ret);
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
regcache_cache_only(cs42xx8->regmap, true);
|
||||
|
||||
err_enable:
|
||||
regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
|
||||
cs42xx8->supplies);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int cs42xx8_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(cs42xx8->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable mclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(cs42xx8->reset_gpio)) {
|
||||
gpio_set_value_cansleep(cs42xx8->reset_gpio, 0);
|
||||
gpio_set_value_cansleep(cs42xx8->reset_gpio, 1);
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
|
||||
cs42xx8->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable supplies: %d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
regmap_update_bits(cs42xx8->regmap, CS42XX8_PWRCTL,
|
||||
CS42XX8_PWRCTL_PDN_MASK, 1);
|
||||
/* Make sure hardware reset done */
|
||||
usleep_range(5000, 10000);
|
||||
|
||||
regmap_update_bits(cs42xx8->regmap, CS42XX8_PWRCTL,
|
||||
CS42XX8_PWRCTL_PDN_MASK, 0);
|
||||
|
||||
regcache_cache_only(cs42xx8->regmap, false);
|
||||
|
||||
ret = regcache_sync(cs42xx8->regmap);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to sync regmap: %d\n", ret);
|
||||
goto err_bulk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_bulk:
|
||||
regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
|
||||
cs42xx8->supplies);
|
||||
err_clk:
|
||||
clk_disable_unprepare(cs42xx8->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cs42xx8_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct rpmsg_cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(cs42xx8->regmap, true);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
|
||||
cs42xx8->supplies);
|
||||
|
||||
clk_disable_unprepare(cs42xx8->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct dev_pm_ops rpmsg_cs42xx8_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static int rpmsg_cs42xx8_codec_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rpmsg_cs42xx8_codec_driver = {
|
||||
.driver = {
|
||||
.name = RPMSG_CODEC_DRV_NAME_CS42888,
|
||||
.pm = &rpmsg_cs42xx8_pm,
|
||||
},
|
||||
.probe = rpmsg_cs42xx8_codec_probe,
|
||||
.remove = rpmsg_cs42xx8_codec_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(rpmsg_cs42xx8_codec_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver");
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_LICENSE("GPL");
|
232
sound/soc/codecs/rpmsg_cs42xx8.h
Normal file
232
sound/soc/codecs/rpmsg_cs42xx8.h
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* cs42xx8.h - Cirrus Logic CS42448/CS42888 Audio CODEC driver header file
|
||||
*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Author: Nicolin Chen <Guangyu.Chen@freescale.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef _RPMSG_CS42XX8_H
|
||||
#define _RPMSG_CS42XX8_H
|
||||
|
||||
struct cs42xx8_driver_data {
|
||||
char name[32];
|
||||
int num_adcs;
|
||||
};
|
||||
|
||||
/* CS42888 register map */
|
||||
#define CS42XX8_CHIPID 0x01 /* Chip ID */
|
||||
#define CS42XX8_PWRCTL 0x02 /* Power Control */
|
||||
#define CS42XX8_FUNCMOD 0x03 /* Functional Mode */
|
||||
#define CS42XX8_INTF 0x04 /* Interface Formats */
|
||||
#define CS42XX8_ADCCTL 0x05 /* ADC Control */
|
||||
#define CS42XX8_TXCTL 0x06 /* Transition Control */
|
||||
#define CS42XX8_DACMUTE 0x07 /* DAC Mute Control */
|
||||
#define CS42XX8_VOLAOUT1 0x08 /* Volume Control AOUT1 */
|
||||
#define CS42XX8_VOLAOUT2 0x09 /* Volume Control AOUT2 */
|
||||
#define CS42XX8_VOLAOUT3 0x0A /* Volume Control AOUT3 */
|
||||
#define CS42XX8_VOLAOUT4 0x0B /* Volume Control AOUT4 */
|
||||
#define CS42XX8_VOLAOUT5 0x0C /* Volume Control AOUT5 */
|
||||
#define CS42XX8_VOLAOUT6 0x0D /* Volume Control AOUT6 */
|
||||
#define CS42XX8_VOLAOUT7 0x0E /* Volume Control AOUT7 */
|
||||
#define CS42XX8_VOLAOUT8 0x0F /* Volume Control AOUT8 */
|
||||
#define CS42XX8_DACINV 0x10 /* DAC Channel Invert */
|
||||
#define CS42XX8_VOLAIN1 0x11 /* Volume Control AIN1 */
|
||||
#define CS42XX8_VOLAIN2 0x12 /* Volume Control AIN2 */
|
||||
#define CS42XX8_VOLAIN3 0x13 /* Volume Control AIN3 */
|
||||
#define CS42XX8_VOLAIN4 0x14 /* Volume Control AIN4 */
|
||||
#define CS42XX8_VOLAIN5 0x15 /* Volume Control AIN5 */
|
||||
#define CS42XX8_VOLAIN6 0x16 /* Volume Control AIN6 */
|
||||
#define CS42XX8_ADCINV 0x17 /* ADC Channel Invert */
|
||||
#define CS42XX8_STATUSCTL 0x18 /* Status Control */
|
||||
#define CS42XX8_STATUS 0x19 /* Status */
|
||||
#define CS42XX8_STATUSM 0x1A /* Status Mask */
|
||||
#define CS42XX8_MUTEC 0x1B /* MUTEC Pin Control */
|
||||
|
||||
#define CS42XX8_FIRSTREG CS42XX8_CHIPID
|
||||
#define CS42XX8_LASTREG CS42XX8_MUTEC
|
||||
#define CS42XX8_NUMREGS (CS42XX8_LASTREG - CS42XX8_FIRSTREG + 1)
|
||||
#define CS42XX8_I2C_INCR 0x80
|
||||
|
||||
/* Chip I.D. and Revision Register (Address 01h) */
|
||||
#define CS42XX8_CHIPID_CHIP_ID_MASK 0xF0
|
||||
#define CS42XX8_CHIPID_REV_ID_MASK 0x0F
|
||||
|
||||
/* Power Control (Address 02h) */
|
||||
#define CS42XX8_PWRCTL_PDN_ADC3_SHIFT 7
|
||||
#define CS42XX8_PWRCTL_PDN_ADC3_MASK (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_ADC3 (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_ADC2_SHIFT 6
|
||||
#define CS42XX8_PWRCTL_PDN_ADC2_MASK (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_ADC2 (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_ADC1_SHIFT 5
|
||||
#define CS42XX8_PWRCTL_PDN_ADC1_MASK (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_ADC1 (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_DAC4_SHIFT 4
|
||||
#define CS42XX8_PWRCTL_PDN_DAC4_MASK (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_DAC4 (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_DAC3_SHIFT 3
|
||||
#define CS42XX8_PWRCTL_PDN_DAC3_MASK (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_DAC3 (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_DAC2_SHIFT 2
|
||||
#define CS42XX8_PWRCTL_PDN_DAC2_MASK (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_DAC2 (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_DAC1_SHIFT 1
|
||||
#define CS42XX8_PWRCTL_PDN_DAC1_MASK (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_DAC1 (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN_SHIFT 0
|
||||
#define CS42XX8_PWRCTL_PDN_MASK (1 << CS42XX8_PWRCTL_PDN_SHIFT)
|
||||
#define CS42XX8_PWRCTL_PDN (1 << CS42XX8_PWRCTL_PDN_SHIFT)
|
||||
|
||||
/* Functional Mode (Address 03h) */
|
||||
#define CS42XX8_FUNCMOD_DAC_FM_SHIFT 6
|
||||
#define CS42XX8_FUNCMOD_DAC_FM_WIDTH 2
|
||||
#define CS42XX8_FUNCMOD_DAC_FM_MASK (((1 << CS42XX8_FUNCMOD_DAC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_DAC_FM_SHIFT)
|
||||
#define CS42XX8_FUNCMOD_DAC_FM(v) ((v) << CS42XX8_FUNCMOD_DAC_FM_SHIFT)
|
||||
#define CS42XX8_FUNCMOD_ADC_FM_SHIFT 4
|
||||
#define CS42XX8_FUNCMOD_ADC_FM_WIDTH 2
|
||||
#define CS42XX8_FUNCMOD_ADC_FM_MASK (((1 << CS42XX8_FUNCMOD_ADC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_ADC_FM_SHIFT)
|
||||
#define CS42XX8_FUNCMOD_ADC_FM(v) ((v) << CS42XX8_FUNCMOD_ADC_FM_SHIFT)
|
||||
#define CS42XX8_FUNCMOD_xC_FM_MASK(x) ((x) ? CS42XX8_FUNCMOD_DAC_FM_MASK : CS42XX8_FUNCMOD_ADC_FM_MASK)
|
||||
#define CS42XX8_FUNCMOD_xC_FM(x, v) ((x) ? CS42XX8_FUNCMOD_DAC_FM(v) : CS42XX8_FUNCMOD_ADC_FM(v))
|
||||
#define CS42XX8_FUNCMOD_MFREQ_SHIFT 1
|
||||
#define CS42XX8_FUNCMOD_MFREQ_WIDTH 3
|
||||
#define CS42XX8_FUNCMOD_MFREQ_MASK (((1 << CS42XX8_FUNCMOD_MFREQ_WIDTH) - 1) << CS42XX8_FUNCMOD_MFREQ_SHIFT)
|
||||
#define CS42XX8_FUNCMOD_MFREQ_256(s) ((0 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
|
||||
#define CS42XX8_FUNCMOD_MFREQ_384(s) ((1 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
|
||||
#define CS42XX8_FUNCMOD_MFREQ_512(s) ((2 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
|
||||
#define CS42XX8_FUNCMOD_MFREQ_768(s) ((3 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
|
||||
#define CS42XX8_FUNCMOD_MFREQ_1024(s) ((4 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
|
||||
|
||||
#define CS42XX8_FM_SINGLE 0
|
||||
#define CS42XX8_FM_DOUBLE 1
|
||||
#define CS42XX8_FM_QUAD 2
|
||||
#define CS42XX8_FM_AUTO 3
|
||||
|
||||
/* Interface Formats (Address 04h) */
|
||||
#define CS42XX8_INTF_FREEZE_SHIFT 7
|
||||
#define CS42XX8_INTF_FREEZE_MASK (1 << CS42XX8_INTF_FREEZE_SHIFT)
|
||||
#define CS42XX8_INTF_FREEZE (1 << CS42XX8_INTF_FREEZE_SHIFT)
|
||||
#define CS42XX8_INTF_AUX_DIF_SHIFT 6
|
||||
#define CS42XX8_INTF_AUX_DIF_MASK (1 << CS42XX8_INTF_AUX_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_AUX_DIF (1 << CS42XX8_INTF_AUX_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_DAC_DIF_SHIFT 3
|
||||
#define CS42XX8_INTF_DAC_DIF_WIDTH 3
|
||||
#define CS42XX8_INTF_DAC_DIF_MASK (((1 << CS42XX8_INTF_DAC_DIF_WIDTH) - 1) << CS42XX8_INTF_DAC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_DAC_DIF_LEFTJ (0 << CS42XX8_INTF_DAC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_DAC_DIF_I2S (1 << CS42XX8_INTF_DAC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_DAC_DIF_RIGHTJ (2 << CS42XX8_INTF_DAC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_DAC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_DAC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_DAC_DIF_ONELINE_20 (4 << CS42XX8_INTF_DAC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_DAC_DIF_ONELINE_24 (5 << CS42XX8_INTF_DAC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_DAC_DIF_TDM (6 << CS42XX8_INTF_DAC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_ADC_DIF_SHIFT 0
|
||||
#define CS42XX8_INTF_ADC_DIF_WIDTH 3
|
||||
#define CS42XX8_INTF_ADC_DIF_MASK (((1 << CS42XX8_INTF_ADC_DIF_WIDTH) - 1) << CS42XX8_INTF_ADC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_ADC_DIF_LEFTJ (0 << CS42XX8_INTF_ADC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_ADC_DIF_I2S (1 << CS42XX8_INTF_ADC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_ADC_DIF_RIGHTJ (2 << CS42XX8_INTF_ADC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_ADC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_ADC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_ADC_DIF_ONELINE_20 (4 << CS42XX8_INTF_ADC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_ADC_DIF_ONELINE_24 (5 << CS42XX8_INTF_ADC_DIF_SHIFT)
|
||||
#define CS42XX8_INTF_ADC_DIF_TDM (6 << CS42XX8_INTF_ADC_DIF_SHIFT)
|
||||
|
||||
/* ADC Control & DAC De-Emphasis (Address 05h) */
|
||||
#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT 7
|
||||
#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_MASK (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT)
|
||||
#define CS42XX8_ADCCTL_ADC_HPF_FREEZE (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT)
|
||||
#define CS42XX8_ADCCTL_DAC_DEM_SHIFT 5
|
||||
#define CS42XX8_ADCCTL_DAC_DEM_MASK (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT)
|
||||
#define CS42XX8_ADCCTL_DAC_DEM (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT)
|
||||
#define CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT 4
|
||||
#define CS42XX8_ADCCTL_ADC1_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT)
|
||||
#define CS42XX8_ADCCTL_ADC1_SINGLE (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT)
|
||||
#define CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT 3
|
||||
#define CS42XX8_ADCCTL_ADC2_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT)
|
||||
#define CS42XX8_ADCCTL_ADC2_SINGLE (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT)
|
||||
#define CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT 2
|
||||
#define CS42XX8_ADCCTL_ADC3_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT)
|
||||
#define CS42XX8_ADCCTL_ADC3_SINGLE (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT)
|
||||
#define CS42XX8_ADCCTL_AIN5_MUX_SHIFT 1
|
||||
#define CS42XX8_ADCCTL_AIN5_MUX_MASK (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT)
|
||||
#define CS42XX8_ADCCTL_AIN5_MUX (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT)
|
||||
#define CS42XX8_ADCCTL_AIN6_MUX_SHIFT 0
|
||||
#define CS42XX8_ADCCTL_AIN6_MUX_MASK (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT)
|
||||
#define CS42XX8_ADCCTL_AIN6_MUX (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT)
|
||||
|
||||
/* Transition Control (Address 06h) */
|
||||
#define CS42XX8_TXCTL_DAC_SNGVOL_SHIFT 7
|
||||
#define CS42XX8_TXCTL_DAC_SNGVOL_MASK (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT)
|
||||
#define CS42XX8_TXCTL_DAC_SNGVOL (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT)
|
||||
#define CS42XX8_TXCTL_DAC_SZC_SHIFT 5
|
||||
#define CS42XX8_TXCTL_DAC_SZC_WIDTH 2
|
||||
#define CS42XX8_TXCTL_DAC_SZC_MASK (((1 << CS42XX8_TXCTL_DAC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_DAC_SZC_SHIFT)
|
||||
#define CS42XX8_TXCTL_DAC_SZC_IC (0 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
|
||||
#define CS42XX8_TXCTL_DAC_SZC_ZC (1 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
|
||||
#define CS42XX8_TXCTL_DAC_SZC_SR (2 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
|
||||
#define CS42XX8_TXCTL_DAC_SZC_SRZC (3 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
|
||||
#define CS42XX8_TXCTL_AMUTE_SHIFT 4
|
||||
#define CS42XX8_TXCTL_AMUTE_MASK (1 << CS42XX8_TXCTL_AMUTE_SHIFT)
|
||||
#define CS42XX8_TXCTL_AMUTE (1 << CS42XX8_TXCTL_AMUTE_SHIFT)
|
||||
#define CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT 3
|
||||
#define CS42XX8_TXCTL_MUTE_ADC_SP_MASK (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT)
|
||||
#define CS42XX8_TXCTL_MUTE_ADC_SP (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT)
|
||||
#define CS42XX8_TXCTL_ADC_SNGVOL_SHIFT 2
|
||||
#define CS42XX8_TXCTL_ADC_SNGVOL_MASK (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT)
|
||||
#define CS42XX8_TXCTL_ADC_SNGVOL (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT)
|
||||
#define CS42XX8_TXCTL_ADC_SZC_SHIFT 0
|
||||
#define CS42XX8_TXCTL_ADC_SZC_MASK (((1 << CS42XX8_TXCTL_ADC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_ADC_SZC_SHIFT)
|
||||
#define CS42XX8_TXCTL_ADC_SZC_IC (0 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
|
||||
#define CS42XX8_TXCTL_ADC_SZC_ZC (1 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
|
||||
#define CS42XX8_TXCTL_ADC_SZC_SR (2 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
|
||||
#define CS42XX8_TXCTL_ADC_SZC_SRZC (3 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
|
||||
|
||||
/* DAC Channel Mute (Address 07h) */
|
||||
#define CS42XX8_DACMUTE_AOUT(n) (0x1 << n)
|
||||
#define CS42XX8_DACMUTE_ALL 0xff
|
||||
|
||||
/* Status Control (Address 18h)*/
|
||||
#define CS42XX8_STATUSCTL_INI_SHIFT 2
|
||||
#define CS42XX8_STATUSCTL_INI_WIDTH 2
|
||||
#define CS42XX8_STATUSCTL_INI_MASK (((1 << CS42XX8_STATUSCTL_INI_WIDTH) - 1) << CS42XX8_STATUSCTL_INI_SHIFT)
|
||||
#define CS42XX8_STATUSCTL_INT_ACTIVE_HIGH (0 << CS42XX8_STATUSCTL_INI_SHIFT)
|
||||
#define CS42XX8_STATUSCTL_INT_ACTIVE_LOW (1 << CS42XX8_STATUSCTL_INI_SHIFT)
|
||||
#define CS42XX8_STATUSCTL_INT_OPEN_DRAIN (2 << CS42XX8_STATUSCTL_INI_SHIFT)
|
||||
|
||||
/* Status (Address 19h)*/
|
||||
#define CS42XX8_STATUS_DAC_CLK_ERR_SHIFT 4
|
||||
#define CS42XX8_STATUS_DAC_CLK_ERR_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_SHIFT)
|
||||
#define CS42XX8_STATUS_ADC_CLK_ERR_SHIFT 3
|
||||
#define CS42XX8_STATUS_ADC_CLK_ERR_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_SHIFT)
|
||||
#define CS42XX8_STATUS_ADC3_OVFL_SHIFT 2
|
||||
#define CS42XX8_STATUS_ADC3_OVFL_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_SHIFT)
|
||||
#define CS42XX8_STATUS_ADC2_OVFL_SHIFT 1
|
||||
#define CS42XX8_STATUS_ADC2_OVFL_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_SHIFT)
|
||||
#define CS42XX8_STATUS_ADC1_OVFL_SHIFT 0
|
||||
#define CS42XX8_STATUS_ADC1_OVFL_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_SHIFT)
|
||||
|
||||
/* Status Mask (Address 1Ah) */
|
||||
#define CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT 4
|
||||
#define CS42XX8_STATUS_DAC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT)
|
||||
#define CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT 3
|
||||
#define CS42XX8_STATUS_ADC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT)
|
||||
#define CS42XX8_STATUS_ADC3_OVFL_M_SHIFT 2
|
||||
#define CS42XX8_STATUS_ADC3_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_M_SHIFT)
|
||||
#define CS42XX8_STATUS_ADC2_OVFL_M_SHIFT 1
|
||||
#define CS42XX8_STATUS_ADC2_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_M_SHIFT)
|
||||
#define CS42XX8_STATUS_ADC1_OVFL_M_SHIFT 0
|
||||
#define CS42XX8_STATUS_ADC1_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_M_SHIFT)
|
||||
|
||||
/* MUTEC Pin Control (Address 1Bh) */
|
||||
#define CS42XX8_MUTEC_MCPOLARITY_SHIFT 1
|
||||
#define CS42XX8_MUTEC_MCPOLARITY_MASK (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
|
||||
#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_LOW (0 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
|
||||
#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_HIGH (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
|
||||
#define CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT 0
|
||||
#define CS42XX8_MUTEC_MUTEC_ACTIVE_MASK (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT)
|
||||
#define CS42XX8_MUTEC_MUTEC_ACTIVE (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT)
|
||||
#endif /* _CS42XX8_H */
|
1542
sound/soc/codecs/rpmsg_wm8960.c
Normal file
1542
sound/soc/codecs/rpmsg_wm8960.c
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -199,9 +199,28 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int si476x_codec_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai) {
|
||||
struct si476x_core *core = i2c_mfd_cell_to_core(dai->dev);
|
||||
|
||||
if (!si476x_core_is_powered_up(core))
|
||||
si476x_core_set_power_state(core, SI476X_POWER_UP_FULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void si476x_codec_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai) {
|
||||
struct si476x_core *core = i2c_mfd_cell_to_core(dai->dev);
|
||||
|
||||
if (si476x_core_is_powered_up(core))
|
||||
si476x_core_set_power_state(core, SI476X_POWER_DOWN);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops si476x_dai_ops = {
|
||||
.hw_params = si476x_codec_hw_params,
|
||||
.set_fmt = si476x_codec_set_dai_fmt,
|
||||
.startup = si476x_codec_startup,
|
||||
.shutdown = si476x_codec_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver si476x_dai = {
|
||||
|
|
|
@ -1108,8 +1108,10 @@ static int twl6040_probe(struct snd_soc_component *component)
|
|||
priv->component = component;
|
||||
|
||||
priv->plug_irq = platform_get_irq(pdev, 0);
|
||||
if (priv->plug_irq < 0)
|
||||
if (priv->plug_irq < 0) {
|
||||
dev_err(component->dev, "invalid irq: %d\n", priv->plug_irq);
|
||||
return priv->plug_irq;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work);
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ static int wm8524_startup(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
||||
/* The set of sample rates that can be supported depends on the
|
||||
* MCLK supplied to the CODEC - enforce this.
|
||||
|
@ -71,9 +72,10 @@ static int wm8524_startup(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&wm8524->rate_constraint);
|
||||
if (!rtd->dai_link->be_hw_params_fixup)
|
||||
snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&wm8524->rate_constraint);
|
||||
|
||||
gpiod_set_value_cansleep(wm8524->mute, 1);
|
||||
|
||||
|
@ -159,7 +161,8 @@ static int wm8524_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
|
|||
|
||||
#define WM8524_RATES SNDRV_PCM_RATE_8000_192000
|
||||
|
||||
#define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
|
||||
#define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static const struct snd_soc_dai_ops wm8524_dai_ops = {
|
||||
.startup = wm8524_startup,
|
||||
|
|
|
@ -608,10 +608,6 @@ static const int bclk_divs[] = {
|
|||
* - lrclk = sysclk / dac_divs
|
||||
* - 10 * bclk = sysclk / bclk_divs
|
||||
*
|
||||
* If we cannot find an exact match for (sysclk, lrclk, bclk)
|
||||
* triplet, we relax the bclk such that bclk is chosen as the
|
||||
* closest available frequency greater than expected bclk.
|
||||
*
|
||||
* @wm8960_priv: wm8960 codec private data
|
||||
* @mclk: MCLK used to derive sysclk
|
||||
* @sysclk_idx: sysclk_divs index for found sysclk
|
||||
|
@ -629,7 +625,7 @@ int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk,
|
|||
{
|
||||
int sysclk, bclk, lrclk;
|
||||
int i, j, k;
|
||||
int diff, closest = mclk;
|
||||
int diff;
|
||||
|
||||
/* marker for no match */
|
||||
*bclk_idx = -1;
|
||||
|
@ -653,12 +649,6 @@ int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk,
|
|||
*bclk_idx = k;
|
||||
break;
|
||||
}
|
||||
if (diff > 0 && closest > diff) {
|
||||
*sysclk_idx = i;
|
||||
*dac_idx = j;
|
||||
*bclk_idx = k;
|
||||
closest = diff;
|
||||
}
|
||||
}
|
||||
if (k != ARRAY_SIZE(bclk_divs))
|
||||
break;
|
||||
|
@ -676,10 +666,6 @@ int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk,
|
|||
* - freq_out = sysclk * sysclk_divs
|
||||
* - 10 * sysclk = bclk * bclk_divs
|
||||
*
|
||||
* If we cannot find an exact match for (sysclk, lrclk, bclk)
|
||||
* triplet, we relax the bclk such that bclk is chosen as the
|
||||
* closest available frequency greater than expected bclk.
|
||||
*
|
||||
* @component: component structure
|
||||
* @freq_in: input frequency used to derive freq out via PLL
|
||||
* @sysclk_idx: sysclk_divs index for found sysclk
|
||||
|
@ -697,12 +683,11 @@ int wm8960_configure_pll(struct snd_soc_component *component, int freq_in,
|
|||
{
|
||||
struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
|
||||
int sysclk, bclk, lrclk, freq_out;
|
||||
int diff, closest, best_freq_out;
|
||||
int diff, best_freq_out;
|
||||
int i, j, k;
|
||||
|
||||
bclk = wm8960->bclk;
|
||||
lrclk = wm8960->lrclk;
|
||||
closest = freq_in;
|
||||
|
||||
best_freq_out = -EINVAL;
|
||||
*sysclk_idx = *dac_idx = *bclk_idx = -1;
|
||||
|
@ -725,13 +710,6 @@ int wm8960_configure_pll(struct snd_soc_component *component, int freq_in,
|
|||
*bclk_idx = k;
|
||||
return freq_out;
|
||||
}
|
||||
if (diff > 0 && closest > diff) {
|
||||
*sysclk_idx = i;
|
||||
*dac_idx = j;
|
||||
*bclk_idx = k;
|
||||
closest = diff;
|
||||
best_freq_out = freq_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -860,8 +838,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
wm8960->is_stream_in_use[tx] = true;
|
||||
|
||||
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON &&
|
||||
!wm8960->is_stream_in_use[!tx])
|
||||
if (!wm8960->is_stream_in_use[!tx])
|
||||
return wm8960_configure_clocking(component);
|
||||
|
||||
return 0;
|
||||
|
@ -1120,11 +1097,6 @@ static bool is_pll_freq_available(unsigned int source, unsigned int target)
|
|||
target *= 4;
|
||||
Ndiv = target / source;
|
||||
|
||||
if (Ndiv < 6) {
|
||||
source >>= 1;
|
||||
Ndiv = target / source;
|
||||
}
|
||||
|
||||
if ((Ndiv < 6) || (Ndiv > 12))
|
||||
return false;
|
||||
|
||||
|
@ -1235,6 +1207,9 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
|
|||
if (pll_id == WM8960_SYSCLK_AUTO)
|
||||
return 0;
|
||||
|
||||
if (is_pll_freq_available(freq_in, freq_out))
|
||||
return -EINVAL;
|
||||
|
||||
return wm8960_set_pll(component, freq_in, freq_out);
|
||||
}
|
||||
|
||||
|
@ -1398,6 +1373,7 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
|
|||
struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
|
||||
struct wm8960_priv *wm8960;
|
||||
int ret;
|
||||
int repeat_reset = 10;
|
||||
|
||||
wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv),
|
||||
GFP_KERNEL);
|
||||
|
@ -1419,7 +1395,11 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
|
|||
else if (i2c->dev.of_node)
|
||||
wm8960_set_pdata_from_of(i2c, &wm8960->pdata);
|
||||
|
||||
ret = wm8960_reset(wm8960->regmap);
|
||||
do {
|
||||
ret = wm8960_reset(wm8960->regmap);
|
||||
repeat_reset--;
|
||||
} while (repeat_reset > 0 && ret != 0);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c->dev, "Failed to issue reset\n");
|
||||
return ret;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* wm8962.c -- WM8962 ALSA SoC Audio driver
|
||||
*
|
||||
* Copyright 2010-2 Wolfson Microelectronics plc
|
||||
* Copyright 2017 NXP
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*/
|
||||
|
@ -82,6 +83,7 @@ struct wm8962_priv {
|
|||
#endif
|
||||
|
||||
int irq;
|
||||
u32 cache_clocking2_reg;
|
||||
};
|
||||
|
||||
/* We can't use the same notifier block for more than one supply and
|
||||
|
@ -1777,8 +1779,11 @@ SND_SOC_BYTES("HD Bass Coefficients", WM8962_HDBASS_AI_1, 30),
|
|||
|
||||
SOC_DOUBLE("ALC Switch", WM8962_ALC1, WM8962_ALCL_ENA_SHIFT,
|
||||
WM8962_ALCR_ENA_SHIFT, 1, 0),
|
||||
SND_SOC_BYTES_MASK("ALC Coefficients", WM8962_ALC1, 4,
|
||||
SND_SOC_BYTES_MASK("ALC1", WM8962_ALC1, 1,
|
||||
WM8962_ALCL_ENA_MASK | WM8962_ALCR_ENA_MASK),
|
||||
SND_SOC_BYTES("ALC2", WM8962_ALC2, 1),
|
||||
SND_SOC_BYTES("ALC3", WM8962_ALC3, 1),
|
||||
SND_SOC_BYTES("Noise Gate", WM8962_NOISE_GATE, 1),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
|
||||
|
@ -2554,11 +2559,17 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
|
||||
snd_pcm_format_t sample_format = params_format(params);
|
||||
int i;
|
||||
int aif0 = 0;
|
||||
int adctl3 = 0;
|
||||
|
||||
wm8962->bclk = snd_soc_params_to_bclk(params);
|
||||
if (sample_format == SNDRV_PCM_FORMAT_S20_3LE)
|
||||
wm8962->bclk = params_rate(params) *
|
||||
params_channels(params) *
|
||||
params_physical_width(params);
|
||||
else
|
||||
wm8962->bclk = snd_soc_params_to_bclk(params);
|
||||
if (params_channels(params) == 1)
|
||||
wm8962->bclk *= 2;
|
||||
|
||||
|
@ -2788,7 +2799,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
|
|||
|
||||
if (target % Fref == 0) {
|
||||
fll_div->theta = 0;
|
||||
fll_div->lambda = 0;
|
||||
fll_div->lambda = 1;
|
||||
} else {
|
||||
gcd_fll = gcd(target, fratio * Fref);
|
||||
|
||||
|
@ -2858,7 +2869,7 @@ static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int s
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fll_div.theta || fll_div.lambda)
|
||||
if (fll_div.theta)
|
||||
fll1 |= WM8962_FLL_FRAC;
|
||||
|
||||
/* Stop the FLL while we reconfigure */
|
||||
|
@ -3813,6 +3824,10 @@ static int wm8962_runtime_resume(struct device *dev)
|
|||
|
||||
regcache_sync(wm8962->regmap);
|
||||
|
||||
regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
|
||||
WM8962_SYSCLK_SRC_MASK,
|
||||
wm8962->cache_clocking2_reg);
|
||||
|
||||
regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP,
|
||||
WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA,
|
||||
WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA);
|
||||
|
@ -3842,6 +3857,9 @@ static int wm8962_runtime_suspend(struct device *dev)
|
|||
WM8962_STARTUP_BIAS_ENA |
|
||||
WM8962_VMID_BUF_ENA, 0);
|
||||
|
||||
regmap_read(wm8962->regmap, WM8962_CLOCKING2,
|
||||
&wm8962->cache_clocking2_reg);
|
||||
|
||||
regcache_cache_only(wm8962->regmap, true);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies),
|
||||
|
@ -3854,6 +3872,7 @@ static int wm8962_runtime_suspend(struct device *dev)
|
|||
#endif
|
||||
|
||||
static const struct dev_pm_ops wm8962_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(wm8962_runtime_suspend, wm8962_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
|
|
|
@ -65,6 +65,14 @@ config SND_SOC_FSL_ESAI
|
|||
This option is only useful for out-of-tree drivers since
|
||||
in-tree drivers select it automatically.
|
||||
|
||||
config SND_SOC_FSL_DAI
|
||||
tristate "Generic FSL DAI support for Sound Open Firmware"
|
||||
help
|
||||
Say Y if you want to enable generic FSL DAI support to be used
|
||||
with Sound Open Firmware. This module takes care of enabling
|
||||
clocks, power domain, pinctrl for FSL DAIs. The rest of DAI
|
||||
control is taken care of by SOF firmware.
|
||||
|
||||
config SND_SOC_FSL_MICFIL
|
||||
tristate "Pulse Density Modulation Microphone Interface (MICFIL) module support"
|
||||
select REGMAP_MMIO
|
||||
|
@ -74,13 +82,47 @@ config SND_SOC_FSL_MICFIL
|
|||
Say Y if you want to add Pulse Density Modulation microphone
|
||||
interface (MICFIL) support for NXP.
|
||||
|
||||
config SND_SOC_FSL_EASRC
|
||||
tristate "Enhanced ASRC module support"
|
||||
select REGMAP_MMIO
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y if you want to add Enhanced ASRC support for NXP. The ASRC is
|
||||
a digital module that converts audio from a source sample rate to a
|
||||
destination sample rate. It is a new design module compare with the
|
||||
old ASRC.
|
||||
|
||||
config SND_SOC_FSL_DSP
|
||||
tristate "dsp module support"
|
||||
select SND_SOC_COMPRESS
|
||||
help
|
||||
Say Y if you want to add hifi 4 support for the Freescale CPUs.
|
||||
which is a DSP core for audio processing.
|
||||
|
||||
config SND_SOC_FSL_RPMSG_I2S
|
||||
tristate "I2S base on the RPMSG support"
|
||||
depends on HAVE_IMX_RPMSG
|
||||
help
|
||||
Say Y if you want to add rpmsg i2s support for the Freescale CPUs.
|
||||
which is depends on the rpmsg.
|
||||
This option is only useful for out-of-tree drivers since
|
||||
in-tree drivers select it automatically.
|
||||
|
||||
config SND_SOC_FSL_UTILS
|
||||
tristate
|
||||
|
||||
config SND_SOC_FSL_HDMI
|
||||
tristate
|
||||
|
||||
config SND_SOC_IMX_PCM_DMA
|
||||
tristate
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
|
||||
config SND_SOC_IMX_PCM_RPMSG
|
||||
tristate
|
||||
depends on HAVE_IMX_RPMSG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
|
||||
config SND_SOC_IMX_AUDMUX
|
||||
tristate "Digital Audio Mux module support"
|
||||
help
|
||||
|
@ -98,7 +140,7 @@ config SND_POWERPC_SOC
|
|||
|
||||
config SND_IMX_SOC
|
||||
tristate "SoC Audio for Freescale i.MX CPUs"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on ARCH_MXC || ARCH_MXC_ARM64 || COMPILE_TEST
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the i.MX CPUs.
|
||||
|
@ -201,6 +243,11 @@ config SND_SOC_IMX_SSI
|
|||
tristate
|
||||
select SND_SOC_FSL_UTILS
|
||||
|
||||
config SND_SOC_IMX_HDMI_DMA
|
||||
bool
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
|
||||
comment "SoC Audio support for Freescale i.MX boards:"
|
||||
|
||||
config SND_MXC_SOC_WM1133_EV1
|
||||
|
@ -249,6 +296,149 @@ config SND_SOC_EUKREA_TLV320
|
|||
Enable I2S based access to the TLV320AIC23B codec attached
|
||||
to the SSI interface
|
||||
|
||||
config SND_SOC_IMX_AK4458
|
||||
tristate "SoC Audio support for i.MX boards with AK4458"
|
||||
depends on OF && I2C
|
||||
select SND_SOC_AK4458_I2C
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_SAI
|
||||
select SND_SOC_FSL_UTILS
|
||||
help
|
||||
SoC Audio support for i.MX boards with AK4458
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
an AK4458 DAC.
|
||||
|
||||
config SND_SOC_IMX_AK5558
|
||||
tristate "SoC Audio support for i.MX boards with AK5558"
|
||||
depends on OF && I2C
|
||||
select SND_SOC_AK5558
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_SAI
|
||||
select SND_SOC_FSL_UTILS
|
||||
help
|
||||
SoC Audio support for i.MX boards with AK5558
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
an AK5558 ADC.
|
||||
|
||||
config SND_SOC_IMX_AK4497
|
||||
tristate "SoC Audio support for i.MX boards with AK4497"
|
||||
depends on OF && I2C
|
||||
select SND_SOC_AK4458
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_SAI
|
||||
select SND_SOC_FSL_UTILS
|
||||
help
|
||||
SoC Audio support for i.MX boards with AK4497
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
an AK4497 DAC.
|
||||
|
||||
config SND_SOC_IMX_WM8960
|
||||
tristate "SoC Audio support for i.MX boards with wm8960"
|
||||
depends on OF && I2C
|
||||
select SND_SOC_WM8960
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_SAI
|
||||
select SND_SOC_FSL_UTILS
|
||||
select SND_KCTL_JACK
|
||||
help
|
||||
SoC Audio support for i.MX boards with WM8960
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
a wm8960 codec.
|
||||
|
||||
config SND_SOC_IMX_WM8524
|
||||
tristate "SoC Audio support for i.MX boards with wm8524"
|
||||
depends on OF && I2C
|
||||
select SND_SOC_WM8524
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_SAI
|
||||
select SND_SOC_FSL_UTILS
|
||||
select SND_KCTL_JACK
|
||||
help
|
||||
SoC Audio support for i.MX boards with WM8524
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
a wm8524 codec.
|
||||
|
||||
config SND_SOC_IMX_SII902X
|
||||
tristate "SoC Audio support for i.MX boards with sii902x"
|
||||
depends on OF && I2C
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_SAI
|
||||
select SND_SOC_FSL_UTILS
|
||||
help
|
||||
SoC Audio support for i.MX boards with SII902X
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
a sii902x.
|
||||
|
||||
config SND_SOC_IMX_WM8958
|
||||
tristate "SoC Audio support for i.MX boards with wm8958"
|
||||
depends on OF && I2C
|
||||
select MFD_WM8994
|
||||
select SND_SOC_WM8994
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_SAI
|
||||
select SND_SOC_FSL_UTILS
|
||||
select SND_KCTL_JACK
|
||||
help
|
||||
SoC Audio support for i.MX boards with WM8958
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
a wm8958 codec.
|
||||
|
||||
config SND_SOC_IMX_CS42888
|
||||
tristate "SoC Audio support for i.MX boards with cs42888"
|
||||
depends on OF && I2C
|
||||
select SND_SOC_CS42XX8_I2C
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_ESAI
|
||||
select SND_SOC_FSL_ASRC
|
||||
select SND_SOC_FSL_UTILS
|
||||
help
|
||||
SoC Audio support for i.MX boards with cs42888
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
a cs42888 codec.
|
||||
|
||||
config SND_SOC_IMX_WM8962
|
||||
tristate "SoC Audio support for i.MX boards with wm8962"
|
||||
depends on OF && I2C && INPUT
|
||||
select SND_SOC_WM8962
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_IMX_AUDMUX
|
||||
select SND_SOC_FSL_SSI
|
||||
select SND_KCTL_JACK
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
a wm8962 codec.
|
||||
|
||||
config SND_SOC_IMX_WM8962_ANDROID
|
||||
tristate "SoC Audio support for i.MX boards with wm8962 in android"
|
||||
depends on SND_SOC_IMX_WM8962=y
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
a wm8962 codec in android.
|
||||
|
||||
config SND_SOC_IMX_MICFIL
|
||||
tristate "SoC Audio support for i.MX boards with micfil"
|
||||
depends on OF && I2C
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_MICFIL
|
||||
help
|
||||
Soc Audio support for i.MX boards with micfil
|
||||
Say Y if you want to add support for SoC audio on
|
||||
an i.MX board with micfil.
|
||||
|
||||
config SND_SOC_IMX_RPMSG
|
||||
tristate "SoC Audio support for i.MX boards with rpmsg"
|
||||
depends on HAVE_IMX_RPMSG
|
||||
select SND_SOC_IMX_PCM_RPMSG
|
||||
select SND_SOC_FSL_RPMSG_I2S
|
||||
select SND_SOC_RPMSG_WM8960
|
||||
select SND_SOC_RPMSG_AK4497
|
||||
select SND_SOC_RPMSG_CS42XX8
|
||||
help
|
||||
SoC Audio support for i.MX boards with rpmsg.
|
||||
There should be rpmsg devices defined in other core
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
a rpmsg devices.
|
||||
|
||||
config SND_SOC_IMX_ES8328
|
||||
tristate "SoC Audio support for i.MX boards with the ES8328 codec"
|
||||
depends on OF && (I2C || SPI)
|
||||
|
@ -272,6 +462,14 @@ config SND_SOC_IMX_SGTL5000
|
|||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
a sgtl5000 codec.
|
||||
|
||||
config SND_SOC_IMX_MQS
|
||||
tristate "SoC Audio support for i.MX boards with MQS"
|
||||
depends on OF
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_SAI
|
||||
select SND_SOC_FSL_MQS
|
||||
select SND_SOC_FSL_UTILS
|
||||
|
||||
config SND_SOC_IMX_SPDIF
|
||||
tristate "SoC Audio support for i.MX boards with S/PDIF"
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
|
@ -302,7 +500,7 @@ config SND_SOC_FSL_ASOC_CARD
|
|||
help
|
||||
ALSA SoC Audio support with ASRC feature for Freescale SoCs that have
|
||||
ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888,
|
||||
CS4271, CS4272 and SGTL5000.
|
||||
CS4271, CS4272, and SGTL5000.
|
||||
Say Y if you want to add support for Freescale Generic ASoC Sound Card.
|
||||
|
||||
config SND_SOC_IMX_AUDMIX
|
||||
|
@ -314,6 +512,59 @@ config SND_SOC_IMX_AUDMIX
|
|||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
an Audio Mixer.
|
||||
|
||||
config SND_SOC_IMX_PDM_MIC
|
||||
tristate "SoC Audio support for i.MX boards with PDM mic on SAI"
|
||||
depends on OF
|
||||
select SND_SOC_IMX_PDM_DMA
|
||||
select SND_SOC_FSL_SAI
|
||||
help
|
||||
SoC Audio support for i.MX boards with PDM microphones on SAI
|
||||
Say Y if you want to add support for SoC Audio support for i.MX boards
|
||||
with PDM microphones on SAI.
|
||||
|
||||
config SND_SOC_IMX_DSP
|
||||
tristate "SoC Audio support for i.MX boards with DSP port"
|
||||
select SND_SOC_FSL_DSP
|
||||
select SND_SOC_COMPRESS
|
||||
help
|
||||
SoC Audio support for i.MX boards with DSP audio
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
IMX DSP.
|
||||
|
||||
config SND_SOC_IMX_SI476X
|
||||
tristate "SoC Audio support for i.MX boards with si476x"
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_IMX_AUDMUX
|
||||
select SND_SOC_FSL_SSI
|
||||
select SND_SOC_FSL_UTILS
|
||||
select SND_SOC_SI476X
|
||||
help
|
||||
SoC Audio support for i.MX boards with SI476x
|
||||
Say Y if you want to add support for Soc audio for the AMFM Tuner chip
|
||||
SI476x module.
|
||||
|
||||
config SND_SOC_IMX_HDMI
|
||||
tristate "SoC Audio support for i.MX boards with HDMI port"
|
||||
depends on MFD_MXC_HDMI
|
||||
select SND_SOC_IMX_HDMI_DMA
|
||||
select SND_SOC_FSL_HDMI
|
||||
select SND_SOC_HDMI_CODEC
|
||||
help
|
||||
SoC Audio support for i.MX boards with HDMI audio
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
IMX HDMI.
|
||||
|
||||
config SND_SOC_IMX_CDNHDMI
|
||||
tristate "SoC Audio support for i.MX boards with CDN HDMI port"
|
||||
depends on DRM_IMX_CDNS_MHDP
|
||||
select SND_SOC_IMX_PCM_DMA
|
||||
select SND_SOC_FSL_SAI
|
||||
select SND_SOC_HDMI_CODEC
|
||||
help
|
||||
SoC Audio support for i.MX boards with CDN HDMI audio
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
IMX CDN HDMI.
|
||||
|
||||
endif # SND_IMX_SOC
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -13,27 +13,40 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
|
|||
|
||||
# Freescale SSI/DMA/SAI/SPDIF Support
|
||||
snd-soc-fsl-audmix-objs := fsl_audmix.o
|
||||
snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
|
||||
snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
|
||||
snd-soc-fsl-dsp-objs := fsl_dsp.o fsl_dsp_proxy.o fsl_dsp_pool.o \
|
||||
fsl_dsp_library_load.o fsl_dsp_xaf_api.o fsl_dsp_cpu.o \
|
||||
fsl_dsp_platform_compress.o
|
||||
snd-soc-fsl-sai-objs := fsl_sai.o
|
||||
snd-soc-fsl-ssi-y := fsl_ssi.o
|
||||
snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
|
||||
snd-soc-fsl-spdif-objs := fsl_spdif.o
|
||||
snd-soc-fsl-esai-objs := fsl_esai.o
|
||||
snd-soc-fsl-dai-objs := fsl_dai.o
|
||||
snd-soc-fsl-micfil-objs := fsl_micfil.o
|
||||
snd-soc-fsl-utils-objs := fsl_utils.o
|
||||
snd-soc-fsl-dma-objs := fsl_dma.o
|
||||
snd-soc-fsl-easrc-objs := fsl_easrc.o fsl_easrc_dma.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
|
||||
snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
|
||||
snd-soc-fsl-rpmsg-i2s-objs := fsl_rpmsg_i2s.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
|
||||
snd-soc-fsl-hdmi-objs := fsl_hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_DSP) += snd-soc-fsl-dsp.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_DAI) += snd-soc-fsl-dai.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_HDMI) += snd-soc-fsl-hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_EASRC) += snd-soc-fsl-easrc.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_RPMSG_I2S) += snd-soc-fsl-rpmsg-i2s.o
|
||||
|
||||
# MPC5200 Platform Support
|
||||
obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
|
||||
|
@ -51,7 +64,10 @@ obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o
|
|||
obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o imx-pcm-dma-v2.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_HDMI_DMA) += imx-hdmi-dma.o hdmi_pcm.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_PCM_RPMSG) += imx-pcm-rpmsg.o
|
||||
|
||||
# i.MX Machine Support
|
||||
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
|
||||
|
@ -59,17 +75,53 @@ snd-soc-phycore-ac97-objs := phycore-ac97.o
|
|||
snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
|
||||
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
|
||||
snd-soc-imx-es8328-objs := imx-es8328.o
|
||||
snd-soc-imx-cs42888-objs := imx-cs42888.o
|
||||
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
|
||||
snd-soc-imx-wm8958-objs := imx-wm8958.o
|
||||
snd-soc-imx-wm8960-objs := imx-wm8960.o
|
||||
snd-soc-imx-wm8524-objs := imx-wm8524.o
|
||||
snd-soc-imx-wm8962-objs := imx-wm8962.o
|
||||
snd-soc-imx-sii902x-objs := imx-sii902x.o
|
||||
snd-soc-imx-spdif-objs := imx-spdif.o
|
||||
snd-soc-imx-mc13783-objs := imx-mc13783.o
|
||||
snd-soc-imx-audmix-objs := imx-audmix.o
|
||||
snd-soc-imx-mqs-objs := imx-mqs.o
|
||||
snd-soc-imx-pdm-objs := imx-pdm.o
|
||||
snd-soc-imx-ak4458-objs := imx-ak4458.o
|
||||
snd-soc-imx-ak5558-objs := imx-ak5558.o
|
||||
snd-soc-imx-ak4497-objs := imx-ak4497.o
|
||||
snd-soc-imx-micfil-objs := imx-micfil.o
|
||||
snd-soc-imx-dsp-objs := imx-dsp.o
|
||||
snd-soc-imx-si476x-objs := imx-si476x.o
|
||||
snd-soc-imx-hdmi-objs := imx-hdmi.o
|
||||
snd-soc-imx-cdnhdmi-objs := imx-cdnhdmi.o
|
||||
snd-soc-imx-rpmsg-objs := imx-rpmsg.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
|
||||
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
|
||||
obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
|
||||
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_CS42888) += snd-soc-imx-cs42888.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
|
||||
obj-${CONFIG_SND_SOC_IMX_WM8958} += snd-soc-imx-wm8958.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_WM8960) += snd-soc-imx-wm8960.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_WM8524) += snd-soc-imx-wm8524.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_SII902X) += snd-soc-imx-sii902x.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_MQS) += snd-soc-imx-mqs.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_PDM_MIC) += snd-soc-imx-pdm.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_AK4458) += snd-soc-imx-ak4458.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_AK5558) += snd-soc-imx-ak5558.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_AK4497) += snd-soc-imx-ak4497.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_MICFIL) += snd-soc-imx-micfil.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_DSP) += snd-soc-imx-dsp.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_SI476X) += snd-soc-imx-si476x.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_CDNHDMI) += snd-soc-imx-cdnhdmi.o
|
||||
|
||||
AFLAGS_hdmi_pcm.o := -march=armv7-a -mtune=cortex-a9 -mfpu=neon -mfloat-abi=softfp
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
//
|
||||
// Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver
|
||||
//
|
||||
// Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
// Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
|
||||
// Copyright 2017 NXP
|
||||
//
|
||||
// Author: Nicolin Chen <nicoleotsuka@gmail.com>
|
||||
|
||||
|
@ -13,6 +14,9 @@
|
|||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_data/dma-imx.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
|
@ -23,6 +27,9 @@
|
|||
#define pair_err(fmt, ...) \
|
||||
dev_err(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
|
||||
|
||||
#define pair_warn(fmt, ...) \
|
||||
dev_warn(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
|
||||
|
||||
#define pair_dbg(fmt, ...) \
|
||||
dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
|
||||
|
||||
|
@ -41,26 +48,58 @@ static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = {
|
|||
* The following tables map the relationship between asrc_inclk/asrc_outclk in
|
||||
* fsl_asrc.h and the registers of ASRCSR
|
||||
*/
|
||||
#define CLK_MAP_NUM 48
|
||||
static unsigned char input_clk_map_imx35[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
};
|
||||
|
||||
static unsigned char output_clk_map_imx35[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
};
|
||||
|
||||
/* i.MX53 uses the same map for input and output */
|
||||
static unsigned char input_clk_map_imx53[] = {
|
||||
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
|
||||
0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
|
||||
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
|
||||
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
|
||||
};
|
||||
|
||||
static unsigned char output_clk_map_imx53[] = {
|
||||
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
|
||||
0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
|
||||
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
|
||||
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
|
||||
};
|
||||
|
||||
static unsigned char *clk_map[2];
|
||||
/* i.MX8 uses the same map for input and output */
|
||||
static unsigned char input_clk_map_imx8_0[] = {
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
};
|
||||
|
||||
static unsigned char output_clk_map_imx8_0[] = {
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
};
|
||||
|
||||
static unsigned char input_clk_map_imx8_1[] = {
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
|
||||
0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
};
|
||||
|
||||
static unsigned char output_clk_map_imx8_1[] = {
|
||||
0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
|
||||
0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
|
||||
};
|
||||
|
||||
/**
|
||||
* Select the pre-processing and post-processing options
|
||||
|
@ -115,7 +154,7 @@ static void fsl_asrc_sel_proc(int inrate, int outrate,
|
|||
* within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
|
||||
* while pair A and pair C are comparatively independent.
|
||||
*/
|
||||
static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
|
||||
int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
|
||||
{
|
||||
enum asrc_pair_index index = ASRC_INVALID_PAIR;
|
||||
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
||||
|
@ -158,7 +197,7 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
|
|||
*
|
||||
* It clears the resource from asrc_priv and releases the occupied channels.
|
||||
*/
|
||||
static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
|
||||
void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
|
@ -260,17 +299,20 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
|
|||
* of struct asrc_config which includes in/output sample rate, width, channel
|
||||
* and clock settings.
|
||||
*/
|
||||
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
||||
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool p2p_in, bool p2p_out)
|
||||
{
|
||||
struct asrc_config *config = pair->config;
|
||||
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
u32 inrate, outrate, indiv, outdiv;
|
||||
u32 clk_index[2], div[2];
|
||||
u32 clk_index[2], div[2], rem[2];
|
||||
u64 clk_rate;
|
||||
int in, out, channels;
|
||||
int pre_proc, post_proc;
|
||||
struct clk *clk;
|
||||
bool ideal;
|
||||
enum asrc_word_width input_word_width;
|
||||
enum asrc_word_width output_word_width;
|
||||
|
||||
if (!config) {
|
||||
pair_err("invalid pair config\n");
|
||||
|
@ -283,9 +325,32 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Validate output width */
|
||||
if (config->output_word_width == ASRC_WIDTH_8_BIT) {
|
||||
pair_err("does not support 8bit width output\n");
|
||||
switch (snd_pcm_format_width(config->input_format)) {
|
||||
case 8:
|
||||
input_word_width = ASRC_WIDTH_8_BIT;
|
||||
break;
|
||||
case 16:
|
||||
input_word_width = ASRC_WIDTH_16_BIT;
|
||||
break;
|
||||
case 24:
|
||||
input_word_width = ASRC_WIDTH_24_BIT;
|
||||
break;
|
||||
default:
|
||||
pair_err("does not support this input format, %d\n",
|
||||
config->input_format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (snd_pcm_format_width(config->output_format)) {
|
||||
case 16:
|
||||
output_word_width = ASRC_WIDTH_16_BIT;
|
||||
break;
|
||||
case 24:
|
||||
output_word_width = ASRC_WIDTH_24_BIT;
|
||||
break;
|
||||
default:
|
||||
pair_err("does not support this output format, %d\n",
|
||||
config->output_format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -320,13 +385,14 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
|||
}
|
||||
|
||||
/* Validate input and output clock sources */
|
||||
clk_index[IN] = clk_map[IN][config->inclk];
|
||||
clk_index[OUT] = clk_map[OUT][config->outclk];
|
||||
clk_index[IN] = asrc_priv->clk_map[IN][config->inclk];
|
||||
clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk];
|
||||
|
||||
/* We only have output clock for ideal ratio mode */
|
||||
clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
|
||||
|
||||
div[IN] = clk_get_rate(clk) / inrate;
|
||||
clk_rate = clk_get_rate(clk);
|
||||
rem[IN] = do_div(clk_rate, inrate);
|
||||
div[IN] = (u32)clk_rate;
|
||||
if (div[IN] == 0) {
|
||||
pair_err("failed to support input sample rate %dHz by asrck_%x\n",
|
||||
inrate, clk_index[ideal ? OUT : IN]);
|
||||
|
@ -335,11 +401,20 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
|||
|
||||
clk = asrc_priv->asrck_clk[clk_index[OUT]];
|
||||
|
||||
/* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */
|
||||
if (ideal)
|
||||
div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE;
|
||||
else
|
||||
div[OUT] = clk_get_rate(clk) / outrate;
|
||||
/*
|
||||
* When P2P mode, output rate should align with the out samplerate.
|
||||
* if set too high output rate, there will be lots of Overload.
|
||||
* When M2M mode, output rate should also need to align with the out
|
||||
* samplerate, but M2M must use less time to achieve good performance.
|
||||
*/
|
||||
clk_rate = clk_get_rate(clk);
|
||||
if (p2p_out || p2p_in || (!ideal)) {
|
||||
rem[OUT] = do_div(clk_rate, outrate);
|
||||
div[OUT] = clk_rate;
|
||||
} else {
|
||||
rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
|
||||
div[OUT] = clk_rate;
|
||||
}
|
||||
|
||||
if (div[OUT] == 0) {
|
||||
pair_err("failed to support output sample rate %dHz by asrck_%x\n",
|
||||
|
@ -347,6 +422,23 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ideal && (div[IN] > 1024 || div[OUT] > 1024 ||
|
||||
rem[IN] != 0 || rem[OUT] != 0)) {
|
||||
pair_err("The divider can't be used for non ideal mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ideal && div[IN] > 1024 && div[OUT] > 1024) {
|
||||
pair_warn("both divider (%d, %d) are larger than threshold\n",
|
||||
div[IN], div[OUT]);
|
||||
}
|
||||
|
||||
if (div[IN] > 1024)
|
||||
div[IN] = 1024;
|
||||
|
||||
if (div[OUT] > 1024)
|
||||
div[OUT] = 1024;
|
||||
|
||||
/* Set the channel number */
|
||||
channels = config->channel_num;
|
||||
|
||||
|
@ -361,8 +453,11 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
|||
/* Default setting: Automatic selection for processing mode */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
|
||||
ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
|
||||
|
||||
/* Default setting: use internal measured ratio */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
|
||||
ASRCTR_USRi_MASK(index), 0);
|
||||
ASRCTR_USRi_MASK(index) | ASRCTR_IDRi_MASK(index),
|
||||
ASRCTR_USR(index));
|
||||
|
||||
/* Set the input and output clock sources */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCSR,
|
||||
|
@ -383,8 +478,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
|||
/* Implement word_width configurations */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
|
||||
ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
|
||||
ASRMCR1i_OW16(config->output_word_width) |
|
||||
ASRMCR1i_IWD(config->input_word_width));
|
||||
ASRMCR1i_OW16(output_word_width) |
|
||||
ASRMCR1i_IWD(input_word_width));
|
||||
|
||||
/* Enable BUFFER STALL */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
|
||||
|
@ -427,7 +522,7 @@ static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
|
|||
{
|
||||
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
int reg, retry = 10, i;
|
||||
int reg, retry = 50, i;
|
||||
|
||||
/* Enable the current pair */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
|
||||
|
@ -440,6 +535,9 @@ static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
|
|||
reg &= ASRCFG_INIRQi_MASK(index);
|
||||
} while (!reg && --retry);
|
||||
|
||||
if (retry == 0)
|
||||
pair_warn("initialization is not finished\n");
|
||||
|
||||
/* Make the input fifo to ASRC STALL level */
|
||||
regmap_read(asrc_priv->regmap, REG_ASRCNCR, ®);
|
||||
for (i = 0; i < pair->channels * 4; i++)
|
||||
|
@ -492,18 +590,73 @@ static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
|
|||
SNDRV_PCM_HW_PARAM_RATE, &fsl_asrc_rate_constraints);
|
||||
}
|
||||
|
||||
static int fsl_asrc_select_clk(struct fsl_asrc *asrc_priv,
|
||||
struct fsl_asrc_pair *pair,
|
||||
int in_rate,
|
||||
int out_rate)
|
||||
{
|
||||
struct asrc_config *config = pair->config;
|
||||
int clk_rate;
|
||||
int clk_index;
|
||||
int i = 0, j = 0;
|
||||
int rate[2];
|
||||
int select_clk[2];
|
||||
bool clk_sel[2];
|
||||
|
||||
rate[0] = in_rate;
|
||||
rate[1] = out_rate;
|
||||
|
||||
/*select proper clock for asrc p2p mode*/
|
||||
for (j = 0; j < 2; j++) {
|
||||
for (i = 0; i < CLK_MAP_NUM; i++) {
|
||||
clk_index = asrc_priv->clk_map[j][i];
|
||||
clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]);
|
||||
if (clk_rate != 0 && (clk_rate / rate[j]) <= 1024 &&
|
||||
(clk_rate % rate[j]) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == CLK_MAP_NUM) {
|
||||
select_clk[j] = OUTCLK_ASRCK1_CLK;
|
||||
clk_sel[j] = false;
|
||||
} else {
|
||||
select_clk[j] = i;
|
||||
clk_sel[j] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (clk_sel[0] != true || clk_sel[1] != true)
|
||||
select_clk[IN] = INCLK_NONE;
|
||||
|
||||
config->inclk = select_clk[IN];
|
||||
config->outclk = select_clk[OUT];
|
||||
|
||||
/*
|
||||
* FIXME: workaroud for 176400/192000 with 8 channel input case
|
||||
* the output sample rate is 48kHz.
|
||||
* with ideal ratio mode, the asrc seems has performance issue
|
||||
* that the output sound is not correct. so switch to non-ideal
|
||||
* ratio mode
|
||||
*/
|
||||
if (config->channel_num >= 8 && config->input_sample_rate >= 176400
|
||||
&& config->inclk == INCLK_NONE)
|
||||
config->inclk = INCLK_ASRCK1_CLK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
|
||||
int width = params_width(params);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
unsigned int channels = params_channels(params);
|
||||
unsigned int rate = params_rate(params);
|
||||
struct asrc_config config;
|
||||
int word_width, ret;
|
||||
snd_pcm_format_t format;
|
||||
int ret;
|
||||
|
||||
ret = fsl_asrc_request_pair(channels, pair);
|
||||
if (ret) {
|
||||
|
@ -511,39 +664,56 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
return ret;
|
||||
}
|
||||
|
||||
pair->pair_streams |= BIT(substream->stream);
|
||||
pair->config = &config;
|
||||
|
||||
if (width == 16)
|
||||
width = ASRC_WIDTH_16_BIT;
|
||||
else
|
||||
width = ASRC_WIDTH_24_BIT;
|
||||
|
||||
if (asrc_priv->asrc_width == 16)
|
||||
word_width = ASRC_WIDTH_16_BIT;
|
||||
format = SNDRV_PCM_FORMAT_S16_LE;
|
||||
else
|
||||
word_width = ASRC_WIDTH_24_BIT;
|
||||
format = SNDRV_PCM_FORMAT_S24_LE;
|
||||
|
||||
config.pair = pair->index;
|
||||
config.channel_num = channels;
|
||||
config.inclk = INCLK_NONE;
|
||||
config.outclk = OUTCLK_ASRCK1_CLK;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
config.input_word_width = width;
|
||||
config.output_word_width = word_width;
|
||||
config.input_format = params_format(params);
|
||||
config.output_format = format;
|
||||
config.input_sample_rate = rate;
|
||||
config.output_sample_rate = asrc_priv->asrc_rate;
|
||||
|
||||
ret = fsl_asrc_select_clk(asrc_priv, pair,
|
||||
config.input_sample_rate,
|
||||
config.output_sample_rate);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "fail to select clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = fsl_asrc_config_pair(pair, false, true);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "fail to config asrc pair\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
} else {
|
||||
config.input_word_width = word_width;
|
||||
config.output_word_width = width;
|
||||
config.input_format = format;
|
||||
config.output_format = params_format(params);
|
||||
config.input_sample_rate = asrc_priv->asrc_rate;
|
||||
config.output_sample_rate = rate;
|
||||
}
|
||||
|
||||
ret = fsl_asrc_config_pair(pair);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "fail to config asrc pair\n");
|
||||
return ret;
|
||||
ret = fsl_asrc_select_clk(asrc_priv, pair,
|
||||
config.input_sample_rate,
|
||||
config.output_sample_rate);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "fail to select clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = fsl_asrc_config_pair(pair, true, false);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "fail to config asrc pair\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -555,8 +725,10 @@ static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream,
|
|||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
|
||||
if (pair)
|
||||
if (pair && (pair->pair_streams & BIT(substream->stream))) {
|
||||
fsl_asrc_release_pair(pair);
|
||||
pair->pair_streams &= ~BIT(substream->stream);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -572,6 +744,8 @@ static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
fsl_asrc_start_pair(pair);
|
||||
/* Output enough data to content the DMA burstsize of BE */
|
||||
mdelay(1);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
|
@ -602,9 +776,13 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
#define FSL_ASRC_FORMATS_RX (SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE)
|
||||
SNDRV_PCM_FMTBIT_S24_3LE)
|
||||
#define FSL_ASRC_FORMATS_TX (SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S8 | \
|
||||
SNDRV_PCM_FMTBIT_S24_3LE)
|
||||
|
||||
static struct snd_soc_dai_driver fsl_asrc_dai = {
|
||||
.probe = fsl_asrc_dai_probe,
|
||||
|
@ -615,7 +793,7 @@ static struct snd_soc_dai_driver fsl_asrc_dai = {
|
|||
.rate_min = 5512,
|
||||
.rate_max = 192000,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = FSL_ASRC_FORMATS,
|
||||
.formats = FSL_ASRC_FORMATS_TX,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "ASRC-Capture",
|
||||
|
@ -624,7 +802,7 @@ static struct snd_soc_dai_driver fsl_asrc_dai = {
|
|||
.rate_min = 5512,
|
||||
.rate_max = 192000,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = FSL_ASRC_FORMATS,
|
||||
.formats = FSL_ASRC_FORMATS_RX,
|
||||
},
|
||||
.ops = &fsl_asrc_dai_ops,
|
||||
};
|
||||
|
@ -772,11 +950,15 @@ static const struct regmap_config fsl_asrc_regmap_config = {
|
|||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
#include "fsl_asrc_m2m.c"
|
||||
|
||||
/**
|
||||
* Initialize ASRC registers with a default configurations
|
||||
*/
|
||||
static int fsl_asrc_init(struct fsl_asrc *asrc_priv)
|
||||
{
|
||||
unsigned long ipg_rate;
|
||||
|
||||
/* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
|
||||
regmap_write(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
|
||||
|
||||
|
@ -794,11 +976,12 @@ static int fsl_asrc_init(struct fsl_asrc *asrc_priv)
|
|||
regmap_update_bits(asrc_priv->regmap, REG_ASRTFR1,
|
||||
ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
|
||||
|
||||
/* Set the processing clock for 76KHz to 133M */
|
||||
regmap_write(asrc_priv->regmap, REG_ASR76K, 0x06D6);
|
||||
|
||||
/* Set the processing clock for 56KHz to 133M */
|
||||
return regmap_write(asrc_priv->regmap, REG_ASR56K, 0x0947);
|
||||
ipg_rate = clk_get_rate(asrc_priv->ipg_clk);
|
||||
/* Set the period of the 76KHz and 56KHz sampling clocks based on
|
||||
* the ASRC processing clock.
|
||||
*/
|
||||
regmap_write(asrc_priv->regmap, REG_ASR76K, ipg_rate / 76000);
|
||||
return regmap_write(asrc_priv->regmap, REG_ASR56K, ipg_rate / 56000);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -862,6 +1045,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
|||
void __iomem *regs;
|
||||
int irq, ret, i;
|
||||
char tmp[16];
|
||||
int num_domains = 0;
|
||||
|
||||
asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
|
||||
if (!asrc_priv)
|
||||
|
@ -885,8 +1069,10 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
|
||||
dev_name(&pdev->dev), asrc_priv);
|
||||
|
@ -920,14 +1106,52 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
num_domains = of_count_phandle_with_args(np, "power-domains",
|
||||
"#power-domain-cells");
|
||||
for (i = 0; i < num_domains; i++) {
|
||||
struct device *pd_dev;
|
||||
struct device_link *link;
|
||||
|
||||
pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i);
|
||||
if (IS_ERR(pd_dev))
|
||||
return PTR_ERR(pd_dev);
|
||||
|
||||
link = device_link_add(&pdev->dev, pd_dev,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (IS_ERR(link))
|
||||
return PTR_ERR(link);
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
|
||||
asrc_priv->channel_bits = 3;
|
||||
clk_map[IN] = input_clk_map_imx35;
|
||||
clk_map[OUT] = output_clk_map_imx35;
|
||||
} else {
|
||||
strncpy(asrc_priv->name, "mxc_asrc",
|
||||
sizeof(asrc_priv->name) - 1);
|
||||
asrc_priv->clk_map[IN] = input_clk_map_imx35;
|
||||
asrc_priv->clk_map[OUT] = output_clk_map_imx35;
|
||||
asrc_priv->dma_type = DMA_SDMA;
|
||||
} else if (of_device_is_compatible(np, "fsl,imx53-asrc")) {
|
||||
asrc_priv->channel_bits = 4;
|
||||
clk_map[IN] = input_clk_map_imx53;
|
||||
clk_map[OUT] = output_clk_map_imx53;
|
||||
strncpy(asrc_priv->name, "mxc_asrc",
|
||||
sizeof(asrc_priv->name) - 1);
|
||||
asrc_priv->clk_map[IN] = input_clk_map_imx53;
|
||||
asrc_priv->clk_map[OUT] = output_clk_map_imx53;
|
||||
asrc_priv->dma_type = DMA_SDMA;
|
||||
} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc0")) {
|
||||
asrc_priv->channel_bits = 4;
|
||||
strncpy(asrc_priv->name, "mxc_asrc",
|
||||
sizeof(asrc_priv->name) - 1);
|
||||
asrc_priv->clk_map[IN] = input_clk_map_imx8_0;
|
||||
asrc_priv->clk_map[OUT] = output_clk_map_imx8_0;
|
||||
asrc_priv->dma_type = DMA_EDMA;
|
||||
} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc1")) {
|
||||
asrc_priv->channel_bits = 4;
|
||||
strncpy(asrc_priv->name, "mxc_asrc1",
|
||||
sizeof(asrc_priv->name) - 1);
|
||||
asrc_priv->clk_map[IN] = input_clk_map_imx8_1;
|
||||
asrc_priv->clk_map[OUT] = output_clk_map_imx8_1;
|
||||
asrc_priv->dma_type = DMA_EDMA;
|
||||
}
|
||||
|
||||
ret = fsl_asrc_init(asrc_priv);
|
||||
|
@ -961,6 +1185,8 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
|||
pm_runtime_enable(&pdev->dev);
|
||||
spin_lock_init(&asrc_priv->lock);
|
||||
|
||||
regcache_cache_only(asrc_priv->regmap, true);
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
|
||||
&fsl_asrc_dai, 1);
|
||||
if (ret) {
|
||||
|
@ -968,6 +1194,12 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = fsl_asrc_m2m_init(asrc_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -976,6 +1208,9 @@ static int fsl_asrc_runtime_resume(struct device *dev)
|
|||
{
|
||||
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
|
||||
int i, ret;
|
||||
u32 asrctr;
|
||||
u32 reg;
|
||||
int retry = 50;
|
||||
|
||||
ret = clk_prepare_enable(asrc_priv->mem_clk);
|
||||
if (ret)
|
||||
|
@ -994,6 +1229,34 @@ static int fsl_asrc_runtime_resume(struct device *dev)
|
|||
goto disable_asrck_clk;
|
||||
}
|
||||
|
||||
/* Stop all pairs provisionally */
|
||||
regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr);
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
|
||||
ASRCTR_ASRCEi_ALL_MASK, 0);
|
||||
|
||||
/* Restore all registers */
|
||||
regcache_cache_only(asrc_priv->regmap, false);
|
||||
regcache_mark_dirty(asrc_priv->regmap);
|
||||
regcache_sync(asrc_priv->regmap);
|
||||
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
|
||||
ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
|
||||
ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
|
||||
|
||||
/* Restart enabled pairs */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
|
||||
ASRCTR_ASRCEi_ALL_MASK, asrctr);
|
||||
|
||||
/* Wait for status of initialization */
|
||||
do {
|
||||
udelay(5);
|
||||
regmap_read(asrc_priv->regmap, REG_ASRCFG, ®);
|
||||
reg = (reg >> ASRCFG_INIRQi_SHIFT(0)) & 0x7;
|
||||
} while (!(reg == ((asrctr & 0xE) >> 1)) && --retry);
|
||||
|
||||
if (retry == 0)
|
||||
dev_warn(dev, "initialization is not finished\n");
|
||||
|
||||
return 0;
|
||||
|
||||
disable_asrck_clk:
|
||||
|
@ -1013,6 +1276,11 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
|
|||
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
regmap_read(asrc_priv->regmap, REG_ASRCFG,
|
||||
&asrc_priv->regcache_cfg);
|
||||
|
||||
regcache_cache_only(asrc_priv->regmap, true);
|
||||
|
||||
for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
|
||||
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
|
||||
if (!IS_ERR(asrc_priv->spba_clk))
|
||||
|
@ -1028,39 +1296,25 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
|
|||
static int fsl_asrc_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
regmap_read(asrc_priv->regmap, REG_ASRCFG,
|
||||
&asrc_priv->regcache_cfg);
|
||||
fsl_asrc_m2m_suspend(asrc_priv);
|
||||
|
||||
regcache_cache_only(asrc_priv->regmap, true);
|
||||
regcache_mark_dirty(asrc_priv->regmap);
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_asrc_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
|
||||
u32 asrctr;
|
||||
int ret;
|
||||
|
||||
/* Stop all pairs provisionally */
|
||||
regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr);
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
|
||||
ASRCTR_ASRCEi_ALL_MASK, 0);
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
|
||||
/* Restore all registers */
|
||||
regcache_cache_only(asrc_priv->regmap, false);
|
||||
regcache_sync(asrc_priv->regmap);
|
||||
fsl_asrc_m2m_resume(asrc_priv);
|
||||
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
|
||||
ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
|
||||
ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
|
||||
|
||||
/* Restart enabled pairs */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
|
||||
ASRCTR_ASRCEi_ALL_MASK, asrctr);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
|
@ -1072,12 +1326,15 @@ static const struct dev_pm_ops fsl_asrc_pm = {
|
|||
static const struct of_device_id fsl_asrc_ids[] = {
|
||||
{ .compatible = "fsl,imx35-asrc", },
|
||||
{ .compatible = "fsl,imx53-asrc", },
|
||||
{ .compatible = "fsl,imx8qm-asrc0", },
|
||||
{ .compatible = "fsl,imx8qm-asrc1", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
|
||||
|
||||
static struct platform_driver fsl_asrc_driver = {
|
||||
.probe = fsl_asrc_probe,
|
||||
.remove = fsl_asrc_m2m_remove,
|
||||
.driver = {
|
||||
.name = "fsl-asrc",
|
||||
.of_match_table = fsl_asrc_ids,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* fsl_asrc.h - Freescale ASRC ALSA SoC header file
|
||||
*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Author: Nicolin Chen <nicoleotsuka@gmail.com>
|
||||
*/
|
||||
|
@ -10,6 +10,12 @@
|
|||
#ifndef _FSL_ASRC_H
|
||||
#define _FSL_ASRC_H
|
||||
|
||||
#include <sound/asound.h>
|
||||
#include <uapi/linux/mxc_asrc.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#define ASRC_PAIR_MAX_NUM (ASRC_PAIR_C + 1)
|
||||
|
||||
#define IN 0
|
||||
#define OUT 1
|
||||
|
||||
|
@ -20,7 +26,8 @@
|
|||
#define ASRC_FIFO_THRESHOLD_MAX 63
|
||||
#define ASRC_DMA_BUFFER_SIZE (1024 * 48 * 4)
|
||||
#define ASRC_MAX_BUFFER_SIZE (1024 * 48)
|
||||
#define ASRC_OUTPUT_LAST_SAMPLE 8
|
||||
#define ASRC_OUTPUT_LAST_SAMPLE_MAX 32
|
||||
#define ASRC_OUTPUT_LAST_SAMPLE 4
|
||||
|
||||
#define IDEAL_RATIO_RATE 1000000
|
||||
|
||||
|
@ -283,106 +290,15 @@
|
|||
#define ASRMCR1i_OW16_MASK (1 << ASRMCR1i_OW16_SHIFT)
|
||||
#define ASRMCR1i_OW16(v) ((v) << ASRMCR1i_OW16_SHIFT)
|
||||
|
||||
|
||||
enum asrc_pair_index {
|
||||
ASRC_INVALID_PAIR = -1,
|
||||
ASRC_PAIR_A = 0,
|
||||
ASRC_PAIR_B = 1,
|
||||
ASRC_PAIR_C = 2,
|
||||
};
|
||||
|
||||
#define ASRC_PAIR_MAX_NUM (ASRC_PAIR_C + 1)
|
||||
|
||||
enum asrc_inclk {
|
||||
INCLK_NONE = 0x03,
|
||||
INCLK_ESAI_RX = 0x00,
|
||||
INCLK_SSI1_RX = 0x01,
|
||||
INCLK_SSI2_RX = 0x02,
|
||||
INCLK_SSI3_RX = 0x07,
|
||||
INCLK_SPDIF_RX = 0x04,
|
||||
INCLK_MLB_CLK = 0x05,
|
||||
INCLK_PAD = 0x06,
|
||||
INCLK_ESAI_TX = 0x08,
|
||||
INCLK_SSI1_TX = 0x09,
|
||||
INCLK_SSI2_TX = 0x0a,
|
||||
INCLK_SSI3_TX = 0x0b,
|
||||
INCLK_SPDIF_TX = 0x0c,
|
||||
INCLK_ASRCK1_CLK = 0x0f,
|
||||
};
|
||||
|
||||
enum asrc_outclk {
|
||||
OUTCLK_NONE = 0x03,
|
||||
OUTCLK_ESAI_TX = 0x00,
|
||||
OUTCLK_SSI1_TX = 0x01,
|
||||
OUTCLK_SSI2_TX = 0x02,
|
||||
OUTCLK_SSI3_TX = 0x07,
|
||||
OUTCLK_SPDIF_TX = 0x04,
|
||||
OUTCLK_MLB_CLK = 0x05,
|
||||
OUTCLK_PAD = 0x06,
|
||||
OUTCLK_ESAI_RX = 0x08,
|
||||
OUTCLK_SSI1_RX = 0x09,
|
||||
OUTCLK_SSI2_RX = 0x0a,
|
||||
OUTCLK_SSI3_RX = 0x0b,
|
||||
OUTCLK_SPDIF_RX = 0x0c,
|
||||
OUTCLK_ASRCK1_CLK = 0x0f,
|
||||
};
|
||||
|
||||
#define ASRC_CLK_MAX_NUM 16
|
||||
|
||||
enum asrc_word_width {
|
||||
ASRC_WIDTH_24_BIT = 0,
|
||||
ASRC_WIDTH_16_BIT = 1,
|
||||
ASRC_WIDTH_8_BIT = 2,
|
||||
};
|
||||
|
||||
struct asrc_config {
|
||||
enum asrc_pair_index pair;
|
||||
unsigned int channel_num;
|
||||
unsigned int buffer_num;
|
||||
unsigned int dma_buffer_size;
|
||||
unsigned int input_sample_rate;
|
||||
unsigned int output_sample_rate;
|
||||
enum asrc_word_width input_word_width;
|
||||
enum asrc_word_width output_word_width;
|
||||
enum asrc_inclk inclk;
|
||||
enum asrc_outclk outclk;
|
||||
};
|
||||
|
||||
struct asrc_req {
|
||||
unsigned int chn_num;
|
||||
enum asrc_pair_index index;
|
||||
};
|
||||
|
||||
struct asrc_querybuf {
|
||||
unsigned int buffer_index;
|
||||
unsigned int input_length;
|
||||
unsigned int output_length;
|
||||
unsigned long input_offset;
|
||||
unsigned long output_offset;
|
||||
};
|
||||
|
||||
struct asrc_convert_buffer {
|
||||
void *input_buffer_vaddr;
|
||||
void *output_buffer_vaddr;
|
||||
unsigned int input_buffer_length;
|
||||
unsigned int output_buffer_length;
|
||||
};
|
||||
|
||||
struct asrc_status_flags {
|
||||
enum asrc_pair_index index;
|
||||
unsigned int overload_error;
|
||||
};
|
||||
|
||||
enum asrc_error_status {
|
||||
ASRC_TASK_Q_OVERLOAD = 0x01,
|
||||
ASRC_OUTPUT_TASK_OVERLOAD = 0x02,
|
||||
ASRC_INPUT_TASK_OVERLOAD = 0x04,
|
||||
ASRC_OUTPUT_BUFFER_OVERFLOW = 0x08,
|
||||
ASRC_INPUT_BUFFER_UNDERRUN = 0x10,
|
||||
ASRC_WIDTH_8_BIT = 2,
|
||||
};
|
||||
|
||||
struct dma_block {
|
||||
dma_addr_t dma_paddr;
|
||||
void *dma_vaddr;
|
||||
unsigned int length;
|
||||
};
|
||||
|
@ -413,6 +329,7 @@ struct fsl_asrc_pair {
|
|||
struct dma_chan *dma_chan[2];
|
||||
struct imx_dma_data dma_data;
|
||||
unsigned int pos;
|
||||
unsigned int pair_streams;
|
||||
|
||||
void *private;
|
||||
};
|
||||
|
@ -433,6 +350,7 @@ struct fsl_asrc_pair {
|
|||
* @pair: pair pointers
|
||||
* @channel_bits: width of ASRCNCR register for each pair
|
||||
* @channel_avail: non-occupied channel numbers
|
||||
* @pair_streams:indicat which substream is running
|
||||
* @asrc_rate: default sample rate for ASoC Back-Ends
|
||||
* @asrc_width: default sample width for ASoC Back-Ends
|
||||
* @regcache_cfg: store register value of REG_ASRCFG
|
||||
|
@ -447,19 +365,29 @@ struct fsl_asrc {
|
|||
struct clk *ipg_clk;
|
||||
struct clk *spba_clk;
|
||||
struct clk *asrck_clk[ASRC_CLK_MAX_NUM];
|
||||
unsigned char *clk_map[2];
|
||||
spinlock_t lock;
|
||||
|
||||
struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM];
|
||||
struct miscdevice asrc_miscdev;
|
||||
unsigned int channel_bits;
|
||||
unsigned int channel_avail;
|
||||
|
||||
int asrc_rate;
|
||||
int asrc_width;
|
||||
int dma_type; /* 0 is sdma, 1 is edma */
|
||||
|
||||
u32 regcache_cfg;
|
||||
char name[20];
|
||||
};
|
||||
|
||||
#define DMA_SDMA 0
|
||||
#define DMA_EDMA 1
|
||||
|
||||
#define DRV_NAME "fsl-asrc-dai"
|
||||
extern struct snd_soc_component_driver fsl_asrc_component;
|
||||
struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir);
|
||||
int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair);
|
||||
void fsl_asrc_release_pair(struct fsl_asrc_pair *pair);
|
||||
|
||||
#endif /* _FSL_ASRC_H */
|
||||
|
|
|
@ -16,16 +16,14 @@
|
|||
|
||||
#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
|
||||
|
||||
static const struct snd_pcm_hardware snd_imx_hardware = {
|
||||
static struct snd_pcm_hardware snd_imx_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
.buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = 65535, /* Limited by SDMA engine */
|
||||
.period_bytes_max = 65532, /* Limited by SDMA engine */
|
||||
.periods_min = 2,
|
||||
.periods_max = 255,
|
||||
.fifo_size = 0,
|
||||
|
@ -148,6 +146,7 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
|
|||
struct device *dev_be;
|
||||
u8 dir = tx ? OUT : IN;
|
||||
dma_cap_mask_t mask;
|
||||
enum sdma_peripheral_type be_peripheral_type;
|
||||
int ret;
|
||||
|
||||
/* Fetch the Back-End dma_data from DPCM */
|
||||
|
@ -201,19 +200,43 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
/* Get DMA request of Back-End */
|
||||
tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
|
||||
tmp_data = tmp_chan->private;
|
||||
pair->dma_data.dma_request = tmp_data->dma_request;
|
||||
dma_release_channel(tmp_chan);
|
||||
if (tmp_chan) {
|
||||
tmp_data = tmp_chan->private;
|
||||
if (tmp_data) {
|
||||
pair->dma_data.dma_request = tmp_data->dma_request;
|
||||
be_peripheral_type = tmp_data->peripheral_type;
|
||||
if (tx && be_peripheral_type == IMX_DMATYPE_SSI_DUAL)
|
||||
pair->dma_data.dst_dualfifo = true;
|
||||
if (!tx && be_peripheral_type == IMX_DMATYPE_SSI_DUAL)
|
||||
pair->dma_data.src_dualfifo = true;
|
||||
}
|
||||
dma_release_channel(tmp_chan);
|
||||
}
|
||||
|
||||
/* Get DMA request of Front-End */
|
||||
tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
|
||||
tmp_data = tmp_chan->private;
|
||||
pair->dma_data.dma_request2 = tmp_data->dma_request;
|
||||
pair->dma_data.peripheral_type = tmp_data->peripheral_type;
|
||||
pair->dma_data.priority = tmp_data->priority;
|
||||
dma_release_channel(tmp_chan);
|
||||
if (tmp_chan) {
|
||||
tmp_data = tmp_chan->private;
|
||||
if (tmp_data) {
|
||||
pair->dma_data.dma_request2 = tmp_data->dma_request;
|
||||
pair->dma_data.peripheral_type =
|
||||
tmp_data->peripheral_type;
|
||||
pair->dma_data.priority = tmp_data->priority;
|
||||
}
|
||||
dma_release_channel(tmp_chan);
|
||||
}
|
||||
|
||||
/* For sdma DEV_TO_DEV, there is two dma request
|
||||
* But for emda DEV_TO_DEV, there is only one dma request, which is
|
||||
* from the BE.
|
||||
*/
|
||||
if (pair->dma_data.dma_request2 != pair->dma_data.dma_request)
|
||||
pair->dma_chan[dir] =
|
||||
dma_request_channel(mask, filter, &pair->dma_data);
|
||||
else
|
||||
pair->dma_chan[dir] =
|
||||
fsl_asrc_get_dma_channel(pair, dir);
|
||||
|
||||
pair->dma_chan[dir] = dma_request_channel(mask, filter, &pair->dma_data);
|
||||
if (!pair->dma_chan[dir]) {
|
||||
dev_err(dev, "failed to request DMA channel for Back-End\n");
|
||||
return -EINVAL;
|
||||
|
@ -224,6 +247,8 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
|
|||
else
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
||||
memset(&config_be, 0, sizeof(config_be));
|
||||
|
||||
config_be.direction = DMA_DEV_TO_DEV;
|
||||
config_be.src_addr_width = buswidth;
|
||||
config_be.src_maxburst = dma_params_be->maxburst;
|
||||
|
@ -276,6 +301,16 @@ static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
|
|||
struct device *dev = component->dev;
|
||||
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
|
||||
struct fsl_asrc_pair *pair;
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
u8 dir = tx ? OUT : IN;
|
||||
struct dma_slave_caps dma_caps;
|
||||
struct dma_chan *tmp_chan;
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
|
||||
if (!pair)
|
||||
|
@ -285,8 +320,78 @@ static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
|
|||
|
||||
runtime->private_data = pair;
|
||||
|
||||
snd_pcm_hw_constraint_integer(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
ret = snd_pcm_hw_constraint_integer(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set pcm hw params periods\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
|
||||
fsl_asrc_request_pair(1, pair);
|
||||
|
||||
tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
|
||||
if (!tmp_chan) {
|
||||
dev_err(dev, "can't get dma channel\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = dma_get_slave_caps(tmp_chan, &dma_caps);
|
||||
if (ret == 0) {
|
||||
if (dma_caps.cmd_pause)
|
||||
snd_imx_hardware.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
|
||||
if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
|
||||
snd_imx_hardware.info |= SNDRV_PCM_INFO_BATCH;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
addr_widths = dma_caps.dst_addr_widths;
|
||||
else
|
||||
addr_widths = dma_caps.src_addr_widths;
|
||||
}
|
||||
|
||||
/*
|
||||
* If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
|
||||
* hw.formats set to 0, meaning no restrictions are in place.
|
||||
* In this case it's the responsibility of the DAI driver to
|
||||
* provide the supported format information.
|
||||
*/
|
||||
if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
|
||||
/*
|
||||
* Prepare formats mask for valid/allowed sample types. If the
|
||||
* dma does not have support for the given physical word size,
|
||||
* it needs to be masked out so user space can not use the
|
||||
* format which produces corrupted audio.
|
||||
* In case the dma driver does not implement the slave_caps the
|
||||
* default assumption is that it supports 1, 2 and 4 bytes
|
||||
* widths.
|
||||
*/
|
||||
for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
|
||||
int bits = snd_pcm_format_physical_width(i);
|
||||
|
||||
/*
|
||||
* Enable only samples with DMA supported physical
|
||||
* widths
|
||||
*/
|
||||
switch (bits) {
|
||||
case 8:
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
case 64:
|
||||
if (addr_widths & (1 << (bits / 8)))
|
||||
snd_imx_hardware.formats |= (1LL << i);
|
||||
break;
|
||||
default:
|
||||
/* Unsupported types */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp_chan)
|
||||
dma_release_channel(tmp_chan);
|
||||
fsl_asrc_release_pair(pair);
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
|
||||
|
||||
return 0;
|
||||
|
|
1046
sound/soc/fsl/fsl_asrc_m2m.c
Normal file
1046
sound/soc/fsl/fsl_asrc_m2m.c
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -286,6 +286,7 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned long lock_flags;
|
||||
|
||||
/* Capture stream shall not be handled */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
|
@ -295,12 +296,16 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
spin_lock_irqsave(&priv->lock, lock_flags);
|
||||
priv->tdms |= BIT(dai->driver->id);
|
||||
spin_unlock_irqrestore(&priv->lock, lock_flags);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
spin_lock_irqsave(&priv->lock, lock_flags);
|
||||
priv->tdms &= ~BIT(dai->driver->id);
|
||||
spin_unlock_irqrestore(&priv->lock, lock_flags);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
|
@ -96,6 +96,7 @@ struct fsl_audmix {
|
|||
struct platform_device *pdev;
|
||||
struct regmap *regmap;
|
||||
struct clk *ipg_clk;
|
||||
spinlock_t lock; /* Protect tdms */
|
||||
u8 tdms;
|
||||
};
|
||||
|
||||
|
|
276
sound/soc/fsl/fsl_dai.c
Normal file
276
sound/soc/fsl/fsl_dai.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Freescale Generic DAI driver for DSP
|
||||
//
|
||||
// Copyright 2019 NXP
|
||||
// Author: Daniel Baluta <daniel.baluta@nxp.com>
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
enum fsl_dai_type {
|
||||
FSL_DAI_TYPE_NONE,
|
||||
FSL_DAI_TYPE_SAI,
|
||||
FSL_DAI_TYPE_ESAI,
|
||||
};
|
||||
|
||||
#define FSL_DAI_ESAI_CLK_NUM 4
|
||||
static const char *esai_clks[FSL_DAI_ESAI_CLK_NUM] = {
|
||||
"core",
|
||||
"extal",
|
||||
"fsys",
|
||||
"spba",
|
||||
};
|
||||
|
||||
#define FSL_DAI_SAI_CLK_NUM 5
|
||||
static const char *sai_clks[FSL_DAI_SAI_CLK_NUM] = {
|
||||
"bus",
|
||||
"mclk0",
|
||||
"mclk1",
|
||||
"mclk2",
|
||||
"mclk3",
|
||||
};
|
||||
|
||||
struct fsl_dai {
|
||||
struct platform_device *pdev;
|
||||
|
||||
/* DAI clocks */
|
||||
struct clk **clks;
|
||||
const char **clk_names;
|
||||
int num_clks;
|
||||
|
||||
/* Power Domain handling */
|
||||
int num_domains;
|
||||
struct device **pd_dev;
|
||||
struct device_link **link;
|
||||
|
||||
/* DAIS */
|
||||
struct snd_soc_dai_driver *dai_drv;
|
||||
int num_drv;
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver fsl_esai_dai = {
|
||||
.name = "esai0",
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver fsl_sai_dai = {
|
||||
.name = "sai1",
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver fsl_dai_component = {
|
||||
.name = "fsl-dai",
|
||||
};
|
||||
|
||||
static int fsl_dai_init_clocks(struct fsl_dai *dai_priv)
|
||||
{
|
||||
struct device *dev = &dai_priv->pdev->dev;
|
||||
int i;
|
||||
|
||||
dai_priv->clks = devm_kcalloc(dev, dai_priv->num_clks,
|
||||
sizeof(*dai_priv->clks), GFP_KERNEL);
|
||||
if (!dai_priv->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < dai_priv->num_clks; i++) {
|
||||
dai_priv->clks[i] = devm_clk_get(dev, dai_priv->clk_names[i]);
|
||||
if (IS_ERR(dai_priv->clks[i])) {
|
||||
dev_dbg(dev, "Failed to get clk %s\n",
|
||||
dai_priv->clk_names[i]);
|
||||
dai_priv->clks[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fsl_get_dai_type(struct fsl_dai *dai_priv)
|
||||
{
|
||||
struct device_node *np = dai_priv->pdev->dev.of_node;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,esai-dai"))
|
||||
return FSL_DAI_TYPE_ESAI;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,sai-dai"))
|
||||
return FSL_DAI_TYPE_SAI;
|
||||
|
||||
return FSL_DAI_TYPE_NONE;
|
||||
}
|
||||
|
||||
static int fsl_dai_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct fsl_dai *priv;
|
||||
int dai_type;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->pdev = pdev;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
dai_type = fsl_get_dai_type(priv);
|
||||
switch (dai_type) {
|
||||
case FSL_DAI_TYPE_ESAI:
|
||||
priv->clk_names = esai_clks;
|
||||
priv->num_clks = FSL_DAI_ESAI_CLK_NUM;
|
||||
priv->dai_drv = &fsl_esai_dai;
|
||||
priv->num_drv = 1;
|
||||
break;
|
||||
case FSL_DAI_TYPE_SAI:
|
||||
priv->clk_names = sai_clks;
|
||||
priv->num_clks = FSL_DAI_SAI_CLK_NUM;
|
||||
priv->dai_drv = &fsl_sai_dai;
|
||||
priv->num_drv = 1;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Invalid DAI type %d\n", dai_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = fsl_dai_init_clocks(priv);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Error at init clocks\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->num_domains = of_count_phandle_with_args(np, "power-domains",
|
||||
"#power-domain-cells");
|
||||
if (priv->num_domains < 0) {
|
||||
dev_err(&pdev->dev, "no power-domains property in %pOF\n", np);
|
||||
return priv->num_domains;
|
||||
}
|
||||
|
||||
priv->pd_dev = devm_kmalloc_array(&pdev->dev, priv->num_domains,
|
||||
sizeof(*priv->pd_dev), GFP_KERNEL);
|
||||
if (!priv->pd_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->link = devm_kmalloc_array(&pdev->dev, priv->num_domains,
|
||||
sizeof(*priv->link), GFP_KERNEL);
|
||||
if (!priv->link)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < priv->num_domains; i++) {
|
||||
priv->pd_dev[i] = dev_pm_domain_attach_by_id(&pdev->dev, i);
|
||||
if (IS_ERR(priv->pd_dev[i])) {
|
||||
ret = PTR_ERR(priv->pd_dev[i]);
|
||||
goto unroll_pm;
|
||||
}
|
||||
|
||||
priv->link[i] = device_link_add(&pdev->dev, priv->pd_dev[i],
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (!priv->link[i]) {
|
||||
ret = -EINVAL;
|
||||
dev_pm_domain_detach(priv->pd_dev[i], false);
|
||||
goto unroll_pm;
|
||||
}
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_dai_component,
|
||||
priv->dai_drv, priv->num_drv);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register DAI ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
|
||||
unroll_pm:
|
||||
while (--i >= 0) {
|
||||
device_link_del(priv->link[i]);
|
||||
dev_pm_domain_detach(priv->pd_dev[i], false);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_dai_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_dai *priv = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
pm_runtime_disable(&priv->pdev->dev);
|
||||
|
||||
for (i = 0; i < priv->num_domains; i++) {
|
||||
device_link_del(priv->link[i]);
|
||||
dev_pm_domain_detach(priv->pd_dev[i], false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id fsl_dai_dt_ids[] = {
|
||||
{ .compatible = "fsl,esai-dai", },
|
||||
{ .compatible = "fsl,sai-dai", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_dai_dt_ids);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_dai_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_dai *priv = dev_get_drvdata(dev);
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < priv->num_clks; i++) {
|
||||
ret = clk_prepare_enable(priv->clks[i]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to enable clk %s\n",
|
||||
priv->clk_names[i]);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
out:
|
||||
while (--i >= 0)
|
||||
clk_disable_unprepare(priv->clks[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_dai_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_dai *priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->num_clks; i++)
|
||||
clk_disable_unprepare(priv->clks[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops fsl_dai_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(fsl_dai_runtime_suspend,
|
||||
fsl_dai_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver fsl_dai_driver = {
|
||||
.probe = fsl_dai_probe,
|
||||
.remove = fsl_dai_remove,
|
||||
.driver = {
|
||||
.name = "fsl-dai",
|
||||
.pm = &fsl_dai_pm_ops,
|
||||
.of_match_table = fsl_dai_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(fsl_dai_driver);
|
||||
|
||||
MODULE_ALIAS("platform:fsl-dai");
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
|
||||
MODULE_DESCRIPTION("FSL Generic DAI driver for DSP");
|
||||
MODULE_LICENSE("GPL v2");
|
58
sound/soc/fsl/fsl_dsd.h
Normal file
58
sound/soc/fsl/fsl_dsd.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2018 NXP
|
||||
*
|
||||
* 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 __FSL_DSD_H
|
||||
#define __FSL_DSD_H
|
||||
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
static bool fsl_is_dsd(struct snd_pcm_hw_params *params)
|
||||
{
|
||||
snd_pcm_format_t format = params_format(params);
|
||||
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_DSD_U8:
|
||||
case SNDRV_PCM_FORMAT_DSD_U16_LE:
|
||||
case SNDRV_PCM_FORMAT_DSD_U16_BE:
|
||||
case SNDRV_PCM_FORMAT_DSD_U32_LE:
|
||||
case SNDRV_PCM_FORMAT_DSD_U32_BE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct pinctrl_state *fsl_get_pins_state(struct pinctrl *pinctrl,
|
||||
struct snd_pcm_hw_params *params, u32 bclk)
|
||||
{
|
||||
struct pinctrl_state *state = 0;
|
||||
|
||||
if (fsl_is_dsd(params)) {
|
||||
/* DSD512@44.1kHz, DSD512@48kHz */
|
||||
if (bclk >= 22579200)
|
||||
state = pinctrl_lookup_state(pinctrl, "dsd512");
|
||||
|
||||
/* Get default DSD state */
|
||||
if (IS_ERR_OR_NULL(state))
|
||||
state = pinctrl_lookup_state(pinctrl, "dsd");
|
||||
} else {
|
||||
/* 706k32b2c, 768k32b2c, etc */
|
||||
if (bclk >= 45158400)
|
||||
state = pinctrl_lookup_state(pinctrl, "pcm_b2m");
|
||||
}
|
||||
|
||||
/* Get default state */
|
||||
if (IS_ERR_OR_NULL(state))
|
||||
state = pinctrl_lookup_state(pinctrl, "default");
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
#endif /* __FSL_DSD_H */
|
1176
sound/soc/fsl/fsl_dsp.c
Normal file
1176
sound/soc/fsl/fsl_dsp.c
Normal file
File diff suppressed because it is too large
Load Diff
143
sound/soc/fsl/fsl_dsp.h
Normal file
143
sound/soc/fsl/fsl_dsp.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT)*/
|
||||
/*
|
||||
* Copyright (C) 2017 Cadence Design Systems, Inc.
|
||||
* Copyright 2018 NXP
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FSL_DSP_H
|
||||
#define FSL_DSP_H
|
||||
#include <uapi/linux/mxc_dsp.h>
|
||||
#include <linux/firmware/imx/ipc.h>
|
||||
#include "fsl_dsp_proxy.h"
|
||||
#include "fsl_dsp_platform.h"
|
||||
|
||||
|
||||
#define FSL_DSP_COMP_NAME "fsl-dsp-component"
|
||||
|
||||
typedef void (*memcpy_func) (void *dest, const void *src, size_t n);
|
||||
typedef void (*memset_func) (void *s, int c, size_t n);
|
||||
|
||||
/* ...maximal number of IPC clients per proxy */
|
||||
#define XF_CFG_MAX_IPC_CLIENTS (1 << 4)
|
||||
|
||||
enum {
|
||||
DSP_IMX8QXP_TYPE = 0,
|
||||
DSP_IMX8QM_TYPE,
|
||||
};
|
||||
|
||||
/* ...proxy client data */
|
||||
struct xf_client {
|
||||
/* ...pointer to proxy interface */
|
||||
struct xf_proxy *proxy;
|
||||
|
||||
/* ...allocated proxy client id */
|
||||
u32 id;
|
||||
|
||||
/* ...pending response queue */
|
||||
struct xf_msg_queue queue;
|
||||
/* ...response waiting queue */
|
||||
wait_queue_head_t wait;
|
||||
|
||||
/* ...virtual memory mapping */
|
||||
unsigned long vm_start;
|
||||
/* ...counter of memory mappings (no real use of it yet - tbd) */
|
||||
atomic_t vm_use;
|
||||
|
||||
/* ...global structure pointer */
|
||||
void *global;
|
||||
struct xf_message m;
|
||||
|
||||
struct snd_compr_stream *cstream;
|
||||
|
||||
struct work_struct work;
|
||||
struct completion compr_complete;
|
||||
|
||||
int input_bytes;
|
||||
int consume_bytes;
|
||||
};
|
||||
|
||||
union xf_client_link {
|
||||
/* ...index of next client in free list */
|
||||
u32 next;
|
||||
|
||||
/* ...reference to proxy data for allocated client */
|
||||
struct xf_client *client;
|
||||
};
|
||||
|
||||
struct fsl_dsp {
|
||||
struct device *dev;
|
||||
const char *fw_name;
|
||||
void __iomem *regs;
|
||||
void __iomem *mu_base_virtaddr;
|
||||
struct imx_sc_ipc *dsp_ipcHandle;
|
||||
unsigned int dsp_mu_id;
|
||||
int dsp_mu_init;
|
||||
atomic_long_t refcnt;
|
||||
unsigned long paddr;
|
||||
unsigned long dram0;
|
||||
unsigned long dram1;
|
||||
unsigned long iram;
|
||||
unsigned long sram;
|
||||
void *sdram_vir_addr;
|
||||
unsigned long sdram_phys_addr;
|
||||
int sdram_reserved_size;
|
||||
void *msg_buf_virt;
|
||||
dma_addr_t msg_buf_phys;
|
||||
int msg_buf_size;
|
||||
void *scratch_buf_virt;
|
||||
dma_addr_t scratch_buf_phys;
|
||||
int scratch_buf_size;
|
||||
void *dsp_config_virt;
|
||||
dma_addr_t dsp_config_phys;
|
||||
int dsp_config_size;
|
||||
int dsp_board_type;
|
||||
unsigned int fixup_offset;
|
||||
|
||||
/* ...proxy data structures */
|
||||
struct xf_proxy proxy;
|
||||
|
||||
/* ...mutex lock */
|
||||
struct mutex dsp_mutex;
|
||||
|
||||
struct dsp_data dsp_data;
|
||||
|
||||
/* ...global clients pool (item[0] serves as list terminator) */
|
||||
union xf_client_link xf_client_map[XF_CFG_MAX_IPC_CLIENTS];
|
||||
|
||||
struct clk *esai_ipg_clk;
|
||||
struct clk *esai_mclk;
|
||||
struct clk *asrc_mem_clk;
|
||||
struct clk *asrc_ipg_clk;
|
||||
struct clk *asrck_clk[4];
|
||||
};
|
||||
|
||||
#define IRAM_OFFSET 0x10000
|
||||
#define IRAM_SIZE 2048
|
||||
|
||||
#define DRAM0_OFFSET 0x0
|
||||
#define DRAM0_SIZE 0x8000
|
||||
|
||||
#define DRAM1_OFFSET 0x8000
|
||||
#define DRAM1_SIZE 0x8000
|
||||
|
||||
#define SYSRAM_OFFSET 0x18000
|
||||
#define SYSRAM_SIZE 0x40000
|
||||
|
||||
#define SYSROM_OFFSET 0x58000
|
||||
#define SYSROM_SIZE 0x30000
|
||||
|
||||
#define MSG_BUF_SIZE 8192
|
||||
#define INPUT_BUF_SIZE 4096
|
||||
#define OUTPUT_BUF_SIZE 16384
|
||||
#define DSP_CONFIG_SIZE 4096
|
||||
|
||||
void *memcpy_dsp(void *dest, const void *src, size_t count);
|
||||
void *memset_dsp(void *dest, int c, size_t count);
|
||||
struct xf_client *xf_client_lookup(struct fsl_dsp *dsp_priv, u32 id);
|
||||
struct xf_client *xf_client_alloc(struct fsl_dsp *dsp_priv);
|
||||
|
||||
int fsl_dsp_open_func(struct fsl_dsp *dsp_priv, struct xf_client *client);
|
||||
int fsl_dsp_close_func(struct xf_client *client);
|
||||
|
||||
#endif
|
97
sound/soc/fsl/fsl_dsp_cpu.c
Normal file
97
sound/soc/fsl/fsl_dsp_cpu.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// DSP Audio platform driver
|
||||
//
|
||||
// Copyright 2018 NXP
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/compress_driver.h>
|
||||
|
||||
#include "fsl_dsp_cpu.h"
|
||||
|
||||
static int dsp_audio_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void dsp_audio_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai) {
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops dsp_audio_dai_ops = {
|
||||
.startup = dsp_audio_startup,
|
||||
.shutdown = dsp_audio_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver dsp_audio_dai = {
|
||||
.name = "dsp-audio-cpu-dai",
|
||||
.compress_new = snd_soc_new_compress,
|
||||
.ops = &dsp_audio_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "Compress Playback",
|
||||
.channels_min = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver audio_dsp_component = {
|
||||
.name = "audio-dsp",
|
||||
};
|
||||
|
||||
static int dsp_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_dsp_audio *dsp_audio;
|
||||
int ret;
|
||||
|
||||
dsp_audio = devm_kzalloc(&pdev->dev, sizeof(*dsp_audio), GFP_KERNEL);
|
||||
if (dsp_audio == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_dbg(&pdev->dev, "probing DSP device....\n");
|
||||
|
||||
/* intialise sof device */
|
||||
dev_set_drvdata(&pdev->dev, dsp_audio);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
/* now register audio DSP platform driver */
|
||||
ret = snd_soc_register_component(&pdev->dev, &audio_dsp_component,
|
||||
&dsp_audio_dai, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"error: failed to register DSP DAI driver %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dsp_audio_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct of_device_id dsp_audio_ids[] = {
|
||||
{ .compatible = "fsl,dsp-audio"},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dsp_audio_ids);
|
||||
|
||||
static struct platform_driver dsp_audio_driver = {
|
||||
.driver = {
|
||||
.name = "dsp-audio",
|
||||
.of_match_table = dsp_audio_ids,
|
||||
},
|
||||
.probe = dsp_audio_probe,
|
||||
.remove = dsp_audio_remove,
|
||||
};
|
||||
module_platform_driver(dsp_audio_driver);
|
16
sound/soc/fsl/fsl_dsp_cpu.h
Normal file
16
sound/soc/fsl/fsl_dsp_cpu.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* DSP Audio DAI header
|
||||
*
|
||||
* Copyright 2018 NXP
|
||||
*/
|
||||
|
||||
#ifndef __FSL_DSP_CPU_H
|
||||
#define __FSL_DSP_CPU_H
|
||||
|
||||
struct fsl_dsp_audio {
|
||||
struct platform_device *pdev;
|
||||
};
|
||||
|
||||
#endif /*__FSL_DSP_CPU_H*/
|
||||
|
642
sound/soc/fsl/fsl_dsp_library_load.c
Normal file
642
sound/soc/fsl/fsl_dsp_library_load.c
Normal file
|
@ -0,0 +1,642 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// Copyright 2018 NXP
|
||||
// Copyright (c) 2012-2013 by Tensilica Inc.
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/elf.h>
|
||||
|
||||
#include "fsl_dsp.h"
|
||||
#include "fsl_dsp_library_load.h"
|
||||
|
||||
static Elf32_Half xtlib_host_half(Elf32_Half v, int byteswap)
|
||||
{
|
||||
return (byteswap) ? (v >> 8) | (v << 8) : v;
|
||||
}
|
||||
|
||||
static Elf32_Word xtlib_host_word(Elf32_Word v, int byteswap)
|
||||
{
|
||||
if (byteswap) {
|
||||
v = ((v & 0x00FF00FF) << 8) | ((v & 0xFF00FF00) >> 8);
|
||||
v = (v >> 16) | (v << 16);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static int xtlib_verify_magic(Elf32_Ehdr *header,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
struct xtlib_loader_globals *xtlib_globals =
|
||||
&lib_info->xtlib_globals;
|
||||
Elf32_Byte magic_no;
|
||||
|
||||
magic_no = header->e_ident[EI_MAG0];
|
||||
if (magic_no != 0x7f)
|
||||
return -1;
|
||||
|
||||
magic_no = header->e_ident[EI_MAG1];
|
||||
if (magic_no != 'E')
|
||||
return -1;
|
||||
|
||||
magic_no = header->e_ident[EI_MAG2];
|
||||
if (magic_no != 'L')
|
||||
return -1;
|
||||
|
||||
magic_no = header->e_ident[EI_MAG3];
|
||||
if (magic_no != 'F')
|
||||
return -1;
|
||||
|
||||
if (header->e_ident[EI_CLASS] != ELFCLASS32)
|
||||
return -1;
|
||||
|
||||
{
|
||||
/* determine byte order */
|
||||
union {
|
||||
short s;
|
||||
char c[sizeof(short)];
|
||||
} u;
|
||||
|
||||
u.s = 1;
|
||||
|
||||
if (header->e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
xtlib_globals->byteswap = u.c[sizeof(short) - 1] == 1;
|
||||
else if (header->e_ident[EI_DATA] == ELFDATA2MSB)
|
||||
xtlib_globals->byteswap = u.c[0] == 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xtlib_load_seg(Elf32_Phdr *pheader, void *src_addr, xt_ptr dst_addr,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
struct xtlib_loader_globals *xtlib_globals =
|
||||
&lib_info->xtlib_globals;
|
||||
Elf32_Word bytes_to_copy = xtlib_host_word(pheader->p_filesz,
|
||||
xtlib_globals->byteswap);
|
||||
Elf32_Word bytes_to_zero = xtlib_host_word(pheader->p_memsz,
|
||||
xtlib_globals->byteswap)
|
||||
- bytes_to_copy;
|
||||
unsigned int i;
|
||||
char *src_back, *dst_back;
|
||||
|
||||
void *zero_addr = (void *)dst_addr + bytes_to_copy;
|
||||
|
||||
if (bytes_to_copy > 0) {
|
||||
// memcpy((void *)(dst_addr), src_addr, bytes_to_copy);
|
||||
src_back = (char *)src_addr;
|
||||
dst_back = (char *)dst_addr;
|
||||
for (i = 0; i < bytes_to_copy; i++)
|
||||
*dst_back++ = *src_back++;
|
||||
}
|
||||
|
||||
if (bytes_to_zero > 0) {
|
||||
// memset(zero_addr, 0, bytes_to_zero);
|
||||
dst_back = (char *)zero_addr;
|
||||
for (i = 0; i < bytes_to_zero; i++)
|
||||
*dst_back++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define xtlib_xt_half xtlib_host_half
|
||||
#define xtlib_xt_word xtlib_host_word
|
||||
|
||||
static xt_ptr align_ptr(xt_ptr ptr, xt_uint align)
|
||||
{
|
||||
return (xt_ptr)(((xt_uint)ptr + align - 1) & ~(align - 1));
|
||||
}
|
||||
|
||||
static xt_ptr xt_ptr_offs(xt_ptr base, Elf32_Word offs,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
struct xtlib_loader_globals *xtlib_globals =
|
||||
&lib_info->xtlib_globals;
|
||||
|
||||
return (xt_ptr)xtlib_xt_word((xt_uint)base +
|
||||
xtlib_host_word(offs, xtlib_globals->byteswap),
|
||||
xtlib_globals->byteswap);
|
||||
}
|
||||
|
||||
static Elf32_Dyn *find_dynamic_info(Elf32_Ehdr *eheader,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
char *base_addr = (char *)eheader;
|
||||
struct xtlib_loader_globals *xtlib_globals =
|
||||
&lib_info->xtlib_globals;
|
||||
Elf32_Phdr *pheader = (Elf32_Phdr *)(base_addr +
|
||||
xtlib_host_word(eheader->e_phoff,
|
||||
xtlib_globals->byteswap));
|
||||
|
||||
int seg = 0;
|
||||
int num = xtlib_host_half(eheader->e_phnum, xtlib_globals->byteswap);
|
||||
|
||||
while (seg < num) {
|
||||
if (xtlib_host_word(pheader[seg].p_type,
|
||||
xtlib_globals->byteswap) == PT_DYNAMIC) {
|
||||
return (Elf32_Dyn *)(base_addr +
|
||||
xtlib_host_word(pheader[seg].p_offset,
|
||||
xtlib_globals->byteswap));
|
||||
}
|
||||
seg++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_align(Elf32_Ehdr *header,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
struct xtlib_loader_globals *xtlib_globals =
|
||||
&lib_info->xtlib_globals;
|
||||
Elf32_Shdr *sheader = (Elf32_Shdr *) (((char *)header) +
|
||||
xtlib_host_word(header->e_shoff, xtlib_globals->byteswap));
|
||||
|
||||
int sec = 0;
|
||||
int num = xtlib_host_half(header->e_shnum, xtlib_globals->byteswap);
|
||||
|
||||
int align = 0;
|
||||
|
||||
while (sec < num) {
|
||||
if (sheader[sec].sh_type != SHT_NULL &&
|
||||
xtlib_host_word(sheader[sec].sh_size,
|
||||
xtlib_globals->byteswap) > 0) {
|
||||
int sec_align =
|
||||
xtlib_host_word(sheader[sec].sh_addralign,
|
||||
xtlib_globals->byteswap);
|
||||
if (sec_align > align)
|
||||
align = sec_align;
|
||||
}
|
||||
sec++;
|
||||
}
|
||||
|
||||
return align;
|
||||
}
|
||||
|
||||
static int validate_dynamic(Elf32_Ehdr *header,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
struct xtlib_loader_globals *xtlib_globals =
|
||||
&lib_info->xtlib_globals;
|
||||
|
||||
if (xtlib_verify_magic(header, lib_info) != 0)
|
||||
return XTLIB_NOT_ELF;
|
||||
|
||||
if (xtlib_host_half(header->e_type,
|
||||
xtlib_globals->byteswap) != ET_DYN)
|
||||
return XTLIB_NOT_DYNAMIC;
|
||||
|
||||
return XTLIB_NO_ERR;
|
||||
}
|
||||
|
||||
static int validate_dynamic_splitload(Elf32_Ehdr *header,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
struct xtlib_loader_globals *xtlib_globals =
|
||||
&lib_info->xtlib_globals;
|
||||
Elf32_Phdr *pheader;
|
||||
int err = validate_dynamic(header, lib_info);
|
||||
|
||||
if (err != XTLIB_NO_ERR)
|
||||
return err;
|
||||
|
||||
/* make sure it's split load pi library, expecting three headers,
|
||||
* code, data and dynamic, for example:
|
||||
*
|
||||
*LOAD off 0x00000094 vaddr 0x00000000 paddr 0x00000000 align 2**0
|
||||
* filesz 0x00000081 memsz 0x00000081 flags r-x
|
||||
*LOAD off 0x00000124 vaddr 0x00000084 paddr 0x00000084 align 2**0
|
||||
* filesz 0x000001ab memsz 0x000011bc flags rwx
|
||||
*DYNAMIC off 0x00000124 vaddr 0x00000084 paddr 0x00000084 align 2**2
|
||||
* filesz 0x000000a0 memsz 0x000000a0 flags rw-
|
||||
*/
|
||||
|
||||
if (xtlib_host_half(header->e_phnum, xtlib_globals->byteswap) != 3)
|
||||
return XTLIB_NOT_SPLITLOAD;
|
||||
|
||||
pheader = (Elf32_Phdr *)((char *)header +
|
||||
xtlib_host_word(header->e_phoff, xtlib_globals->byteswap));
|
||||
|
||||
/* LOAD R-X */
|
||||
if (xtlib_host_word(pheader[0].p_type,
|
||||
xtlib_globals->byteswap) != PT_LOAD ||
|
||||
(xtlib_host_word(pheader[0].p_flags,
|
||||
xtlib_globals->byteswap)
|
||||
& (PF_R | PF_W | PF_X)) != (PF_R | PF_X))
|
||||
return XTLIB_NOT_SPLITLOAD;
|
||||
|
||||
/* LOAD RWX */
|
||||
if (xtlib_host_word(pheader[1].p_type,
|
||||
xtlib_globals->byteswap) != PT_LOAD ||
|
||||
(xtlib_host_word(pheader[1].p_flags,
|
||||
xtlib_globals->byteswap)
|
||||
& (PF_R | PF_W | PF_X)) != (PF_R | PF_W | PF_X))
|
||||
return XTLIB_NOT_SPLITLOAD;
|
||||
|
||||
/* DYNAMIC RW- */
|
||||
if (xtlib_host_word(pheader[2].p_type,
|
||||
xtlib_globals->byteswap) != PT_DYNAMIC ||
|
||||
(xtlib_host_word(pheader[2].p_flags,
|
||||
xtlib_globals->byteswap)
|
||||
& (PF_R | PF_W | PF_X)) != (PF_R | PF_W))
|
||||
return XTLIB_NOT_SPLITLOAD;
|
||||
|
||||
return XTLIB_NO_ERR;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
xtlib_split_pi_library_size(struct xtlib_packaged_library *library,
|
||||
unsigned int *code_size,
|
||||
unsigned int *data_size,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
struct xtlib_loader_globals *xtlib_globals =
|
||||
&lib_info->xtlib_globals;
|
||||
Elf32_Phdr *pheader;
|
||||
Elf32_Ehdr *header = (Elf32_Ehdr *)library;
|
||||
int align;
|
||||
int err = validate_dynamic_splitload(header, lib_info);
|
||||
|
||||
if (err != XTLIB_NO_ERR) {
|
||||
xtlib_globals->err = err;
|
||||
return err;
|
||||
}
|
||||
|
||||
align = find_align(header, lib_info);
|
||||
|
||||
pheader = (Elf32_Phdr *)((char *)library +
|
||||
xtlib_host_word(header->e_phoff, xtlib_globals->byteswap));
|
||||
|
||||
*code_size = xtlib_host_word(pheader[0].p_memsz,
|
||||
xtlib_globals->byteswap) + align;
|
||||
*data_size = xtlib_host_word(pheader[1].p_memsz,
|
||||
xtlib_globals->byteswap) + align;
|
||||
|
||||
return XTLIB_NO_ERR;
|
||||
}
|
||||
|
||||
static int get_dyn_info(Elf32_Ehdr *eheader,
|
||||
xt_ptr dst_addr, xt_uint src_offs,
|
||||
xt_ptr dst_data_addr, xt_uint src_data_offs,
|
||||
struct xtlib_pil_info *info,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
unsigned int jmprel = 0;
|
||||
unsigned int pltrelsz = 0;
|
||||
struct xtlib_loader_globals *xtlib_globals =
|
||||
&lib_info->xtlib_globals;
|
||||
Elf32_Dyn *dyn_entry = find_dynamic_info(eheader, lib_info);
|
||||
|
||||
if (dyn_entry == 0)
|
||||
return XTLIB_NO_DYNAMIC_SEGMENT;
|
||||
|
||||
info->dst_addr = (xt_uint)xtlib_xt_word((Elf32_Word)dst_addr,
|
||||
xtlib_globals->byteswap);
|
||||
info->src_offs = xtlib_xt_word(src_offs, xtlib_globals->byteswap);
|
||||
info->dst_data_addr = (xt_uint)xtlib_xt_word(
|
||||
(Elf32_Word)dst_data_addr + src_data_offs,
|
||||
xtlib_globals->byteswap);
|
||||
info->src_data_offs = xtlib_xt_word(src_data_offs,
|
||||
xtlib_globals->byteswap);
|
||||
|
||||
dst_addr -= src_offs;
|
||||
dst_data_addr = dst_data_addr + src_data_offs - src_data_offs;
|
||||
|
||||
info->start_sym = xt_ptr_offs(dst_addr, eheader->e_entry, lib_info);
|
||||
|
||||
info->align = xtlib_xt_word(find_align(eheader, lib_info),
|
||||
xtlib_globals->byteswap);
|
||||
|
||||
info->text_addr = 0;
|
||||
|
||||
while (dyn_entry->d_tag != DT_NULL) {
|
||||
switch ((Elf32_Sword) xtlib_host_word(
|
||||
(Elf32_Word)dyn_entry->d_tag,
|
||||
xtlib_globals->byteswap)) {
|
||||
case DT_RELA:
|
||||
info->rel = xt_ptr_offs(dst_data_addr,
|
||||
dyn_entry->d_un.d_ptr, lib_info);
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
info->rela_count = xtlib_xt_word(
|
||||
xtlib_host_word(dyn_entry->d_un.d_val,
|
||||
xtlib_globals->byteswap) /
|
||||
sizeof(Elf32_Rela),
|
||||
xtlib_globals->byteswap);
|
||||
break;
|
||||
case DT_INIT:
|
||||
info->init = xt_ptr_offs(dst_addr,
|
||||
dyn_entry->d_un.d_ptr, lib_info);
|
||||
break;
|
||||
case DT_FINI:
|
||||
info->fini = xt_ptr_offs(dst_addr,
|
||||
dyn_entry->d_un.d_ptr, lib_info);
|
||||
break;
|
||||
case DT_HASH:
|
||||
info->hash = xt_ptr_offs(dst_data_addr,
|
||||
dyn_entry->d_un.d_ptr, lib_info);
|
||||
break;
|
||||
case DT_SYMTAB:
|
||||
info->symtab = xt_ptr_offs(dst_data_addr,
|
||||
dyn_entry->d_un.d_ptr, lib_info);
|
||||
break;
|
||||
case DT_STRTAB:
|
||||
info->strtab = xt_ptr_offs(dst_data_addr,
|
||||
dyn_entry->d_un.d_ptr, lib_info);
|
||||
break;
|
||||
case DT_JMPREL:
|
||||
jmprel = dyn_entry->d_un.d_val;
|
||||
break;
|
||||
case DT_PLTRELSZ:
|
||||
pltrelsz = dyn_entry->d_un.d_val;
|
||||
break;
|
||||
case DT_LOPROC + 2:
|
||||
info->text_addr = xt_ptr_offs(dst_addr,
|
||||
dyn_entry->d_un.d_ptr, lib_info);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
dyn_entry++;
|
||||
}
|
||||
|
||||
return XTLIB_NO_ERR;
|
||||
}
|
||||
|
||||
static xt_ptr
|
||||
xtlib_load_split_pi_library_common(struct xtlib_packaged_library *library,
|
||||
xt_ptr destination_code_address,
|
||||
xt_ptr destination_data_address,
|
||||
struct xtlib_pil_info *info,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
struct xtlib_loader_globals *xtlib_globals =
|
||||
&lib_info->xtlib_globals;
|
||||
Elf32_Ehdr *header = (Elf32_Ehdr *)library;
|
||||
Elf32_Phdr *pheader;
|
||||
unsigned int align;
|
||||
int err = validate_dynamic_splitload(header, lib_info);
|
||||
xt_ptr destination_code_address_back;
|
||||
xt_ptr destination_data_address_back;
|
||||
|
||||
if (err != XTLIB_NO_ERR) {
|
||||
xtlib_globals->err = err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
align = find_align(header, lib_info);
|
||||
|
||||
destination_code_address_back = destination_code_address;
|
||||
destination_data_address_back = destination_data_address;
|
||||
|
||||
destination_code_address = align_ptr(destination_code_address, align);
|
||||
destination_data_address = align_ptr(destination_data_address, align);
|
||||
lib_info->code_buf_virt += (destination_code_address -
|
||||
destination_code_address_back);
|
||||
lib_info->data_buf_virt += (destination_data_address -
|
||||
destination_data_address_back);
|
||||
|
||||
pheader = (Elf32_Phdr *)((char *)library +
|
||||
xtlib_host_word(header->e_phoff,
|
||||
xtlib_globals->byteswap));
|
||||
|
||||
err = get_dyn_info(header,
|
||||
destination_code_address,
|
||||
xtlib_host_word(pheader[0].p_paddr,
|
||||
xtlib_globals->byteswap),
|
||||
destination_data_address,
|
||||
xtlib_host_word(pheader[1].p_paddr,
|
||||
xtlib_globals->byteswap),
|
||||
info,
|
||||
lib_info);
|
||||
|
||||
if (err != XTLIB_NO_ERR) {
|
||||
xtlib_globals->err = err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* loading code */
|
||||
xtlib_load_seg(&pheader[0],
|
||||
(char *)library + xtlib_host_word(pheader[0].p_offset,
|
||||
xtlib_globals->byteswap),
|
||||
(xt_ptr)lib_info->code_buf_virt,
|
||||
lib_info);
|
||||
|
||||
if (info->text_addr == 0)
|
||||
info->text_addr =
|
||||
(xt_ptr)xtlib_xt_word((Elf32_Word)destination_code_address,
|
||||
xtlib_globals->byteswap);
|
||||
|
||||
/* loading data */
|
||||
xtlib_load_seg(&pheader[1],
|
||||
(char *)library + xtlib_host_word(pheader[1].p_offset,
|
||||
xtlib_globals->byteswap),
|
||||
(xt_ptr)lib_info->data_buf_virt +
|
||||
xtlib_host_word(pheader[1].p_paddr,
|
||||
xtlib_globals->byteswap),
|
||||
lib_info);
|
||||
|
||||
return (xt_ptr)xtlib_host_word((Elf32_Word)info->start_sym,
|
||||
xtlib_globals->byteswap);
|
||||
}
|
||||
|
||||
static xt_ptr
|
||||
xtlib_host_load_split_pi_library(struct xtlib_packaged_library *library,
|
||||
xt_ptr destination_code_address,
|
||||
xt_ptr destination_data_address,
|
||||
struct xtlib_pil_info *info,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
return xtlib_load_split_pi_library_common(library,
|
||||
destination_code_address,
|
||||
destination_data_address,
|
||||
info,
|
||||
lib_info);
|
||||
}
|
||||
|
||||
static long
|
||||
load_dpu_with_library(struct xf_client *client, struct xf_proxy *proxy,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy, struct fsl_dsp, proxy);
|
||||
unsigned char *srambuf;
|
||||
struct lib_dnld_info_t dpulib;
|
||||
struct file *file;
|
||||
struct xf_buffer *buf;
|
||||
Elf32_Phdr *pheader;
|
||||
Elf32_Ehdr *header;
|
||||
loff_t pos = 0;
|
||||
unsigned int align;
|
||||
int filesize = 0;
|
||||
long ret_val = 0;
|
||||
|
||||
file = filp_open(lib_info->filename, O_RDONLY, 0);
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
vfs_llseek(file, 0, SEEK_END);
|
||||
filesize = (int)file->f_pos;
|
||||
|
||||
srambuf = kmalloc(filesize, GFP_KERNEL);
|
||||
if (!srambuf)
|
||||
return -ENOMEM;
|
||||
|
||||
vfs_llseek(file, 0, SEEK_SET);
|
||||
ret_val = kernel_read(file, srambuf, filesize, &pos);
|
||||
if (ret_val < 0)
|
||||
return ret_val;
|
||||
filp_close(file, NULL);
|
||||
|
||||
ret_val = xtlib_split_pi_library_size(
|
||||
(struct xtlib_packaged_library *)(srambuf),
|
||||
(unsigned int *)&dpulib.size_code,
|
||||
(unsigned int *)&dpulib.size_data,
|
||||
lib_info);
|
||||
if (ret_val != XTLIB_NO_ERR)
|
||||
return -EINVAL;
|
||||
|
||||
lib_info->code_buf_size = dpulib.size_code;
|
||||
lib_info->data_buf_size = dpulib.size_data;
|
||||
|
||||
header = (Elf32_Ehdr *)srambuf;
|
||||
pheader = (Elf32_Phdr *)((char *)srambuf +
|
||||
xtlib_host_word(header->e_phoff,
|
||||
lib_info->xtlib_globals.byteswap));
|
||||
|
||||
align = find_align(header, lib_info);
|
||||
ret_val = xf_pool_alloc(client, proxy, 1, dpulib.size_code + align,
|
||||
XF_POOL_AUX, &lib_info->code_section_pool);
|
||||
if (ret_val) {
|
||||
kfree(srambuf);
|
||||
pr_err("Allocation failure for loading code section\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret_val = xf_pool_alloc(client, proxy, 1,
|
||||
dpulib.size_data + pheader[1].p_paddr + align,
|
||||
XF_POOL_AUX, &lib_info->data_section_pool);
|
||||
if (ret_val) {
|
||||
kfree(srambuf);
|
||||
pr_err("Allocation failure for loading data section\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
buf = xf_buffer_get(lib_info->code_section_pool);
|
||||
lib_info->code_buf_virt = xf_buffer_data(buf);
|
||||
lib_info->code_buf_phys = ((u64)xf_buffer_data(buf) -
|
||||
(u64)dsp_priv->scratch_buf_virt) +
|
||||
dsp_priv->scratch_buf_phys;
|
||||
lib_info->code_buf_size = dpulib.size_code + align;
|
||||
xf_buffer_put(buf);
|
||||
|
||||
buf = xf_buffer_get(lib_info->data_section_pool);
|
||||
lib_info->data_buf_virt = xf_buffer_data(buf);
|
||||
lib_info->data_buf_phys = ((u64)xf_buffer_data(buf) -
|
||||
(u64)dsp_priv->scratch_buf_virt) +
|
||||
dsp_priv->scratch_buf_phys;
|
||||
lib_info->data_buf_size = dpulib.size_data + align + pheader[1].p_paddr;
|
||||
xf_buffer_put(buf);
|
||||
|
||||
dpulib.pbuf_code = (unsigned long)lib_info->code_buf_phys;
|
||||
dpulib.pbuf_data = (unsigned long)lib_info->data_buf_phys;
|
||||
|
||||
dpulib.ppil_inf = &lib_info->pil_info;
|
||||
xtlib_host_load_split_pi_library((struct xtlib_packaged_library *)srambuf,
|
||||
(xt_ptr)(dpulib.pbuf_code),
|
||||
(xt_ptr)(dpulib.pbuf_data),
|
||||
(struct xtlib_pil_info *)dpulib.ppil_inf,
|
||||
(void *)lib_info);
|
||||
kfree(srambuf);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static long
|
||||
unload_dpu_with_library(struct xf_client *client, struct xf_proxy *proxy,
|
||||
struct lib_info *lib_info)
|
||||
{
|
||||
xf_pool_free(client, lib_info->code_section_pool);
|
||||
xf_pool_free(client, lib_info->data_section_pool);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long xf_load_lib(struct xf_client *client,
|
||||
struct xf_handle *handle, struct lib_info *lib_info)
|
||||
{
|
||||
void *b = xf_handle_aux(handle);
|
||||
struct icm_xtlib_pil_info icm_info;
|
||||
struct xf_proxy *proxy = handle->proxy;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
long ret_val;
|
||||
|
||||
ret_val = load_dpu_with_library(client, proxy, lib_info);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
|
||||
memcpy((void *)(&icm_info.pil_info), (void *)(&lib_info->pil_info),
|
||||
sizeof(struct xtlib_pil_info));
|
||||
|
||||
icm_info.lib_type = lib_info->lib_type;
|
||||
|
||||
/* ...set message parameters */
|
||||
msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), __XF_PORT_SPEC2(handle->id, 0));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_LOAD_LIB;
|
||||
msg.buffer = b;
|
||||
msg.length = sizeof(struct icm_xtlib_pil_info);
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...copy lib info */
|
||||
memcpy(b, (void *)&icm_info, xf_buffer_length(handle->aux));
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
if (IS_ERR(rmsg))
|
||||
return PTR_ERR(rmsg);
|
||||
|
||||
// xf_msg_free(proxy, rmsg);
|
||||
// xf_unlock(&proxy->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long xf_unload_lib(struct xf_client *client, struct xf_handle *handle, struct lib_info *lib_info)
|
||||
{
|
||||
void *b = xf_handle_aux(handle);
|
||||
struct xf_proxy *proxy = handle->proxy;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
struct icm_xtlib_pil_info icm_info;
|
||||
|
||||
memset((void *)&icm_info, 0, sizeof(struct icm_xtlib_pil_info));
|
||||
icm_info.lib_type = lib_info->lib_type;
|
||||
|
||||
/* ...set message parameters */
|
||||
msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0),__XF_PORT_SPEC2(handle->id, 0));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_UNLOAD_LIB;
|
||||
msg.buffer = b;
|
||||
msg.length = sizeof(struct icm_xtlib_pil_info);
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...copy lib info */
|
||||
memcpy(b, (void *)&icm_info, xf_buffer_length(handle->aux));
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
if (IS_ERR(rmsg))
|
||||
return PTR_ERR(rmsg);
|
||||
|
||||
// xf_msg_free(proxy, rmsg);
|
||||
// xf_unlock(&proxy->lock);
|
||||
|
||||
return unload_dpu_with_library(client, proxy, lib_info);
|
||||
}
|
92
sound/soc/fsl/fsl_dsp_library_load.h
Normal file
92
sound/soc/fsl/fsl_dsp_library_load.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// Copyright 2018 NXP
|
||||
// Copyright (c) 2012-2013 by Tensilica Inc.
|
||||
|
||||
#ifndef FSL_DSP_LIBRARY_LOAD_H
|
||||
#define FSL_DSP_LIBRARY_LOAD_H
|
||||
|
||||
#include "fsl_dsp_pool.h"
|
||||
|
||||
#define Elf32_Byte unsigned char
|
||||
#define xt_ptr unsigned long
|
||||
#define xt_int int
|
||||
#define xt_uint unsigned int
|
||||
#define xt_ulong unsigned long
|
||||
|
||||
struct xtlib_packaged_library;
|
||||
|
||||
enum {
|
||||
XTLIB_NO_ERR = 0,
|
||||
XTLIB_NOT_ELF = 1,
|
||||
XTLIB_NOT_DYNAMIC = 2,
|
||||
XTLIB_NOT_STATIC = 3,
|
||||
XTLIB_NO_DYNAMIC_SEGMENT = 4,
|
||||
XTLIB_UNKNOWN_SYMBOL = 5,
|
||||
XTLIB_NOT_ALIGNED = 6,
|
||||
XTLIB_NOT_SPLITLOAD = 7,
|
||||
XTLIB_RELOCATION_ERR = 8
|
||||
};
|
||||
|
||||
enum lib_type {
|
||||
DSP_CODEC_LIB = 1,
|
||||
DSP_CODEC_WRAP_LIB
|
||||
};
|
||||
|
||||
struct xtlib_loader_globals {
|
||||
int err;
|
||||
int byteswap;
|
||||
};
|
||||
|
||||
struct xtlib_pil_info {
|
||||
xt_uint dst_addr;
|
||||
xt_uint src_offs;
|
||||
xt_uint dst_data_addr;
|
||||
xt_uint src_data_offs;
|
||||
xt_uint start_sym;
|
||||
xt_uint text_addr;
|
||||
xt_uint init;
|
||||
xt_uint fini;
|
||||
xt_uint rel;
|
||||
xt_int rela_count;
|
||||
xt_uint hash;
|
||||
xt_uint symtab;
|
||||
xt_uint strtab;
|
||||
xt_int align;
|
||||
};
|
||||
|
||||
struct icm_xtlib_pil_info {
|
||||
struct xtlib_pil_info pil_info;
|
||||
unsigned int lib_type;
|
||||
};
|
||||
|
||||
struct lib_dnld_info_t {
|
||||
unsigned long pbuf_code;
|
||||
unsigned long pbuf_data;
|
||||
unsigned int size_code;
|
||||
unsigned int size_data;
|
||||
struct xtlib_pil_info *ppil_inf;
|
||||
unsigned int lib_on_dpu; /* 0: not loaded, 1: loaded. */
|
||||
};
|
||||
|
||||
struct lib_info {
|
||||
struct xtlib_pil_info pil_info;
|
||||
struct xtlib_loader_globals xtlib_globals;
|
||||
|
||||
struct xf_pool *code_section_pool;
|
||||
struct xf_pool *data_section_pool;
|
||||
|
||||
void *code_buf_virt;
|
||||
unsigned int code_buf_phys;
|
||||
unsigned int code_buf_size;
|
||||
void *data_buf_virt;
|
||||
unsigned int data_buf_phys;
|
||||
unsigned int data_buf_size;
|
||||
|
||||
const char *filename;
|
||||
unsigned int lib_type;
|
||||
};
|
||||
|
||||
long xf_load_lib(struct xf_client *client, struct xf_handle *handle, struct lib_info *lib_info);
|
||||
long xf_unload_lib(struct xf_client *client, struct xf_handle *handle, struct lib_info *lib_info);
|
||||
|
||||
#endif
|
21
sound/soc/fsl/fsl_dsp_platform.h
Normal file
21
sound/soc/fsl/fsl_dsp_platform.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2012-2013 by Tensilica Inc. ALL RIGHTS RESERVED.
|
||||
* Copyright 2018 NXP
|
||||
*/
|
||||
|
||||
#ifndef _FSL_DSP_PLATFORM_H
|
||||
#define _FSL_DSP_PLATFORM_H
|
||||
|
||||
#include "fsl_dsp_xaf_api.h"
|
||||
|
||||
struct dsp_data {
|
||||
struct xf_client *client;
|
||||
struct xaf_pipeline *p_pipe;
|
||||
struct xaf_pipeline pipeline;
|
||||
struct xaf_comp component[2];
|
||||
int codec_type;
|
||||
int status;
|
||||
};
|
||||
|
||||
#endif /*_FSL_DSP_PLATFORM_H*/
|
480
sound/soc/fsl/fsl_dsp_platform_compress.c
Normal file
480
sound/soc/fsl/fsl_dsp_platform_compress.c
Normal file
|
@ -0,0 +1,480 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// DSP driver compress implementation
|
||||
//
|
||||
// Copyright (c) 2012-2013 by Tensilica Inc. ALL RIGHTS RESERVED.
|
||||
// Copyright 2018 NXP
|
||||
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/compress_driver.h>
|
||||
|
||||
#include "fsl_dsp.h"
|
||||
#include "fsl_dsp_platform.h"
|
||||
#include "fsl_dsp_xaf_api.h"
|
||||
|
||||
#define NUM_CODEC 2
|
||||
#define MIN_FRAGMENT 1
|
||||
#define MAX_FRAGMENT 1
|
||||
#define MIN_FRAGMENT_SIZE (4 * 1024)
|
||||
#define MAX_FRAGMENT_SIZE (4 * 1024)
|
||||
|
||||
void dsp_platform_process(struct work_struct *w)
|
||||
{
|
||||
struct xf_client *client = container_of(w, struct xf_client, work);
|
||||
struct xf_proxy *proxy = client->proxy;
|
||||
struct xf_message *rmsg;
|
||||
|
||||
while (1) {
|
||||
rmsg = xf_cmd_recv(proxy, &client->wait, &client->queue, 1);
|
||||
|
||||
if (!proxy->is_active || IS_ERR(rmsg))
|
||||
return;
|
||||
if (rmsg->opcode == XF_EMPTY_THIS_BUFFER) {
|
||||
client->consume_bytes += rmsg->length;
|
||||
snd_compr_fragment_elapsed(client->cstream);
|
||||
|
||||
if (rmsg->buffer == NULL && rmsg->length == 0)
|
||||
snd_compr_drain_notify(client->cstream);
|
||||
|
||||
} else {
|
||||
memcpy(&client->m, rmsg, sizeof(struct xf_message));
|
||||
complete(&client->compr_complete);
|
||||
}
|
||||
|
||||
xf_msg_free(proxy, rmsg);
|
||||
xf_unlock(&proxy->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_open(struct snd_compr_stream *cstream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
|
||||
struct dsp_data *drv = &dsp_priv->dsp_data;
|
||||
|
||||
drv->client = xf_client_alloc(dsp_priv);
|
||||
if (IS_ERR(drv->client))
|
||||
return PTR_ERR(drv->client);
|
||||
|
||||
fsl_dsp_open_func(dsp_priv, drv->client);
|
||||
|
||||
drv->client->proxy = &dsp_priv->proxy;
|
||||
|
||||
cpu_dai->driver->ops->startup(NULL, cpu_dai);
|
||||
|
||||
drv->client->cstream = cstream;
|
||||
|
||||
INIT_WORK(&drv->client->work, dsp_platform_process);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_free(struct snd_compr_stream *cstream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
|
||||
struct dsp_data *drv = &dsp_priv->dsp_data;
|
||||
int ret;
|
||||
|
||||
if (cstream->runtime->state != SNDRV_PCM_STATE_PAUSED &&
|
||||
cstream->runtime->state != SNDRV_PCM_STATE_RUNNING &&
|
||||
cstream->runtime->state != SNDRV_PCM_STATE_DRAINING) {
|
||||
|
||||
ret = xaf_comp_delete(drv->client, &drv->component[1]);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "Fail to delete component, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = xaf_comp_delete(drv->client, &drv->component[0]);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "Fail to delete component, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
cpu_dai->driver->ops->shutdown(NULL, cpu_dai);
|
||||
|
||||
drv->client->proxy->is_active = 0;
|
||||
wake_up(&drv->client->wait);
|
||||
cancel_work_sync(&drv->client->work);
|
||||
|
||||
fsl_dsp_close_func(drv->client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_set_params(struct snd_compr_stream *cstream,
|
||||
struct snd_compr_params *params)
|
||||
{
|
||||
/* accroding to the params, load the library and create component*/
|
||||
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
|
||||
struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
|
||||
struct dsp_data *drv = &dsp_priv->dsp_data;
|
||||
struct xf_proxy *p_proxy = &dsp_priv->proxy;
|
||||
struct xf_set_param_msg s_param;
|
||||
int ret;
|
||||
|
||||
switch (params->codec.id) {
|
||||
case SND_AUDIOCODEC_MP3:
|
||||
drv->codec_type = CODEC_MP3_DEC;
|
||||
break;
|
||||
case SND_AUDIOCODEC_AAC:
|
||||
drv->codec_type = CODEC_AAC_DEC;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "codec not supported, id =%d\n", params->codec.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* ...create auxiliary buffers pool for control commands */
|
||||
ret = xf_pool_alloc(drv->client,
|
||||
p_proxy,
|
||||
XA_AUX_POOL_SIZE,
|
||||
XA_AUX_POOL_MSG_LENGTH,
|
||||
XF_POOL_AUX,
|
||||
&p_proxy->aux);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "xf_pool_alloc failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ...create pipeline */
|
||||
ret = xaf_pipeline_create(&drv->pipeline);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "create pipeline error\n");
|
||||
goto err_pool_alloc;
|
||||
}
|
||||
|
||||
/* ...create component */
|
||||
ret = xaf_comp_create(drv->client, p_proxy, &drv->component[0],
|
||||
drv->codec_type);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"create component failed type = %d, err = %d\n",
|
||||
drv->codec_type, ret);
|
||||
goto err_pool_alloc;
|
||||
}
|
||||
|
||||
ret = xaf_comp_create(drv->client, p_proxy, &drv->component[1],
|
||||
RENDER_ESAI);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"create component failed, type = %d, err = %d\n",
|
||||
RENDER_ESAI, ret);
|
||||
goto err_comp0_create;
|
||||
}
|
||||
|
||||
/* ...add component into pipeline */
|
||||
ret = xaf_comp_add(&drv->pipeline, &drv->component[0]);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"add component failed, type = %d, err = %d\n",
|
||||
drv->codec_type, ret);
|
||||
goto err_comp1_create;
|
||||
}
|
||||
|
||||
ret = xaf_comp_add(&drv->pipeline, &drv->component[1]);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"add component failed, type = %d, err = %d\n",
|
||||
drv->codec_type, ret);
|
||||
goto err_comp1_create;
|
||||
}
|
||||
|
||||
drv->client->input_bytes = 0;
|
||||
drv->client->consume_bytes = 0;
|
||||
|
||||
s_param.id = XA_RENDERER_CONFIG_PARAM_SAMPLE_RATE;
|
||||
s_param.mixData.value = params->codec.sample_rate;
|
||||
ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"set param[cmd:0x%x|val:0x%x] error, err = %d\n",
|
||||
s_param.id, s_param.mixData.value, ret);
|
||||
goto err_comp1_create;
|
||||
}
|
||||
|
||||
s_param.id = XA_RENDERER_CONFIG_PARAM_CHANNELS;
|
||||
s_param.mixData.value = params->codec.ch_out;
|
||||
ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"set param[cmd:0x%x|val:0x%x] error, err = %d\n",
|
||||
s_param.id, s_param.mixData.value, ret);
|
||||
goto err_comp1_create;
|
||||
}
|
||||
|
||||
s_param.id = XA_RENDERER_CONFIG_PARAM_PCM_WIDTH;
|
||||
s_param.mixData.value = 16;
|
||||
ret = xaf_comp_set_config(drv->client, &drv->component[1], 1, &s_param);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"set param[cmd:0x%x|val:0x%x] error, err = %d\n",
|
||||
s_param.id, s_param.mixData.value, ret);
|
||||
goto err_comp1_create;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_comp1_create:
|
||||
xaf_comp_delete(drv->client, &drv->component[1]);
|
||||
err_comp0_create:
|
||||
xaf_comp_delete(drv->client, &drv->component[0]);
|
||||
err_pool_alloc:
|
||||
xf_pool_free(drv->client, p_proxy->aux);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_trigger_start(struct snd_compr_stream *cstream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
|
||||
struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
|
||||
struct dsp_data *drv = &dsp_priv->dsp_data;
|
||||
struct xaf_comp *p_comp = &drv->component[0];
|
||||
int ret;
|
||||
|
||||
ret = xaf_comp_process(drv->client,
|
||||
p_comp,
|
||||
p_comp->inptr,
|
||||
drv->client->input_bytes,
|
||||
XF_EMPTY_THIS_BUFFER);
|
||||
|
||||
ret = xaf_connect(drv->client,
|
||||
&drv->component[0],
|
||||
&drv->component[1],
|
||||
1,
|
||||
OUTBUF_SIZE);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "Failed to connect component, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
schedule_work(&drv->client->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_trigger_stop(struct snd_compr_stream *cstream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
|
||||
struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
|
||||
struct dsp_data *drv = &dsp_priv->dsp_data;
|
||||
int ret;
|
||||
|
||||
ret = xaf_comp_flush(drv->client, &drv->component[0]);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "Fail to flush component, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = xaf_comp_flush(drv->client, &drv->component[1]);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "Fail to flush component, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = xaf_comp_delete(drv->client, &drv->component[0]);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "Fail to delete component, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = xaf_comp_delete(drv->client, &drv->component[1]);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "Fail to delete component, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_trigger_drain(struct snd_compr_stream *cstream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
|
||||
struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
|
||||
struct dsp_data *drv = &dsp_priv->dsp_data;
|
||||
struct xaf_comp *p_comp = &drv->component[0];
|
||||
int ret;
|
||||
|
||||
ret = xaf_comp_process(drv->client, p_comp, NULL, 0,
|
||||
XF_EMPTY_THIS_BUFFER);
|
||||
|
||||
schedule_work(&drv->client->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
ret = dsp_platform_compr_trigger_start(cstream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
ret = dsp_platform_compr_trigger_stop(cstream);
|
||||
break;
|
||||
case SND_COMPR_TRIGGER_DRAIN:
|
||||
ret = dsp_platform_compr_trigger_drain(cstream);
|
||||
break;
|
||||
case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
break;
|
||||
}
|
||||
|
||||
/*send command*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_pointer(struct snd_compr_stream *cstream,
|
||||
struct snd_compr_tstamp *tstamp)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
|
||||
struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
|
||||
struct dsp_data *drv = &dsp_priv->dsp_data;
|
||||
|
||||
tstamp->copied_total = drv->client->input_bytes;
|
||||
tstamp->byte_offset = drv->client->input_bytes;
|
||||
tstamp->pcm_frames = 0x900;
|
||||
tstamp->pcm_io_frames = 0,
|
||||
tstamp->sampling_rate = 48000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_copy(struct snd_compr_stream *cstream,
|
||||
char __user *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, FSL_DSP_COMP_NAME);
|
||||
struct fsl_dsp *dsp_priv = snd_soc_component_get_drvdata(component);
|
||||
struct dsp_data *drv = &dsp_priv->dsp_data;
|
||||
struct xaf_comp *p_comp = &drv->component[0];
|
||||
int copied = 0;
|
||||
int ret;
|
||||
|
||||
if (drv->client->input_bytes == drv->client->consume_bytes) {
|
||||
if (count > INBUF_SIZE){
|
||||
ret = copy_from_user(p_comp->inptr, buf, INBUF_SIZE);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "failed to get message from user space\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
copied = INBUF_SIZE;
|
||||
} else {
|
||||
ret = copy_from_user(p_comp->inptr, buf, count);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "failed to get message from user space\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
copied = count;
|
||||
}
|
||||
drv->client->input_bytes += copied;
|
||||
|
||||
if (cstream->runtime->state == SNDRV_PCM_STATE_RUNNING) {
|
||||
ret = xaf_comp_process(drv->client, p_comp,
|
||||
p_comp->inptr, copied,
|
||||
XF_EMPTY_THIS_BUFFER);
|
||||
schedule_work(&drv->client->work);
|
||||
}
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_get_caps(struct snd_compr_stream *cstream,
|
||||
struct snd_compr_caps *caps)
|
||||
{
|
||||
caps->num_codecs = NUM_CODEC;
|
||||
caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */
|
||||
caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */
|
||||
caps->min_fragments = MIN_FRAGMENT;
|
||||
caps->max_fragments = MAX_FRAGMENT;
|
||||
caps->codecs[0] = SND_AUDIOCODEC_MP3;
|
||||
caps->codecs[1] = SND_AUDIOCODEC_AAC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_compr_codec_caps caps_mp3 = {
|
||||
.num_descriptors = 1,
|
||||
.descriptor[0].max_ch = 2,
|
||||
.descriptor[0].sample_rates[0] = 48000,
|
||||
.descriptor[0].sample_rates[1] = 44100,
|
||||
.descriptor[0].sample_rates[2] = 32000,
|
||||
.descriptor[0].sample_rates[3] = 16000,
|
||||
.descriptor[0].sample_rates[4] = 8000,
|
||||
.descriptor[0].num_sample_rates = 5,
|
||||
.descriptor[0].bit_rate[0] = 320,
|
||||
.descriptor[0].bit_rate[1] = 192,
|
||||
.descriptor[0].num_bitrates = 2,
|
||||
.descriptor[0].profiles = 0,
|
||||
.descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
|
||||
.descriptor[0].formats = 0,
|
||||
};
|
||||
|
||||
static struct snd_compr_codec_caps caps_aac = {
|
||||
.num_descriptors = 2,
|
||||
.descriptor[1].max_ch = 2,
|
||||
.descriptor[0].sample_rates[0] = 48000,
|
||||
.descriptor[0].sample_rates[1] = 44100,
|
||||
.descriptor[0].sample_rates[2] = 32000,
|
||||
.descriptor[0].sample_rates[3] = 16000,
|
||||
.descriptor[0].sample_rates[4] = 8000,
|
||||
.descriptor[0].num_sample_rates = 5,
|
||||
.descriptor[1].bit_rate[0] = 320,
|
||||
.descriptor[1].bit_rate[1] = 192,
|
||||
.descriptor[1].num_bitrates = 2,
|
||||
.descriptor[1].profiles = 0,
|
||||
.descriptor[1].modes = 0,
|
||||
.descriptor[1].formats =
|
||||
(SND_AUDIOSTREAMFORMAT_MP4ADTS |
|
||||
SND_AUDIOSTREAMFORMAT_RAW),
|
||||
};
|
||||
|
||||
static int dsp_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
|
||||
struct snd_compr_codec_caps *codec)
|
||||
{
|
||||
if (codec->codec == SND_AUDIOCODEC_MP3)
|
||||
*codec = caps_mp3;
|
||||
else if (codec->codec == SND_AUDIOCODEC_AAC)
|
||||
*codec = caps_aac;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsp_platform_compr_set_metadata(struct snd_compr_stream *cstream,
|
||||
struct snd_compr_metadata *metadata)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct snd_compr_ops dsp_platform_compr_ops = {
|
||||
.open = dsp_platform_compr_open,
|
||||
.free = dsp_platform_compr_free,
|
||||
.set_params = dsp_platform_compr_set_params,
|
||||
.set_metadata = dsp_platform_compr_set_metadata,
|
||||
.trigger = dsp_platform_compr_trigger,
|
||||
.pointer = dsp_platform_compr_pointer,
|
||||
.copy = dsp_platform_compr_copy,
|
||||
.get_caps = dsp_platform_compr_get_caps,
|
||||
.get_codec_caps = dsp_platform_compr_get_codec_caps,
|
||||
};
|
150
sound/soc/fsl/fsl_dsp_pool.c
Normal file
150
sound/soc/fsl/fsl_dsp_pool.c
Normal file
|
@ -0,0 +1,150 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Xtensa buffer pool API
|
||||
//
|
||||
// Copyright 2018 NXP
|
||||
// Copyright (c) 2012-2013 by Tensilica Inc.
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "fsl_dsp_pool.h"
|
||||
#include "fsl_dsp.h"
|
||||
|
||||
/* ...allocate buffer pool */
|
||||
int xf_pool_alloc(struct xf_client *client, struct xf_proxy *proxy,
|
||||
u32 number, u32 length, xf_pool_type_t type,
|
||||
struct xf_pool **pool)
|
||||
{
|
||||
struct xf_pool *p;
|
||||
struct xf_buffer *b;
|
||||
void *data;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
|
||||
/* ...basic sanity checks; number of buffers is positive */
|
||||
if (number <=0)
|
||||
return -EINVAL;
|
||||
|
||||
/* ...get properly aligned buffer length */
|
||||
length = ALIGN(length, XF_PROXY_ALIGNMENT);
|
||||
|
||||
p = kzalloc(offsetof(struct xf_pool, buffer) +
|
||||
number * sizeof(struct xf_buffer), GFP_KERNEL);
|
||||
if(!p)
|
||||
return -ENOMEM;
|
||||
|
||||
/* ...prepare command parameters */
|
||||
msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_DSP_PROXY(0));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_ALLOC;
|
||||
msg.length = length * number;
|
||||
msg.buffer = NULL;
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
if (IS_ERR(rmsg)) {
|
||||
kfree(p);
|
||||
return PTR_ERR(rmsg);
|
||||
}
|
||||
|
||||
p->p = rmsg->buffer;
|
||||
/* TODO: review cleanup */
|
||||
/* xf_msg_free(proxy, rmsg);
|
||||
* xf_unlock(&proxy->lock); */
|
||||
|
||||
/* ...if operation is failed, do cleanup */
|
||||
/* ...set pool parameters */
|
||||
p->number = number, p->length = length;
|
||||
p->proxy = proxy;
|
||||
|
||||
/* ...create individual buffers and link them into free list */
|
||||
for (p->free = b = &p->buffer[0], data = p->p; number > 0;
|
||||
number--, b++) {
|
||||
/* ...set address of the buffer (no length there) */
|
||||
b->address = data;
|
||||
|
||||
/* ...file buffer into the free list */
|
||||
b->link.next = b + 1;
|
||||
|
||||
/* ...advance data pointer in contiguous buffer */
|
||||
data += length;
|
||||
}
|
||||
|
||||
/* ...terminate list of buffers (not too good - tbd) */
|
||||
b[-1].link.next = NULL;
|
||||
|
||||
/* ...return buffer pointer */
|
||||
*pool = p;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* ...buffer pool destruction */
|
||||
int xf_pool_free(struct xf_client *client, struct xf_pool *pool)
|
||||
{
|
||||
struct xf_proxy *proxy;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
|
||||
/* ...basic sanity checks; pool is positive */
|
||||
if (pool == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* ...get proxy pointer */
|
||||
if ((proxy = pool->proxy) == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* ...prepare command parameters */
|
||||
msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_DSP_PROXY(0));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_FREE;
|
||||
msg.length = pool->length * pool->number;
|
||||
msg.buffer = pool->p;
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
kfree(pool);
|
||||
if (IS_ERR(rmsg))
|
||||
return PTR_ERR(rmsg);
|
||||
|
||||
/* TODO: review cleanup */
|
||||
/* xf_msg_free(proxy, rmsg);
|
||||
* xf_unlock(&proxy->lock); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ...get new buffer from a pool */
|
||||
struct xf_buffer *xf_buffer_get(struct xf_pool *pool)
|
||||
{
|
||||
struct xf_buffer *b;
|
||||
|
||||
xf_lock(&pool->proxy->lock);
|
||||
/* ...take buffer from a head of the free list */
|
||||
b = pool->free;
|
||||
if (b) {
|
||||
/* ...advance free list head */
|
||||
pool->free = b->link.next, b->link.pool = pool;
|
||||
}
|
||||
|
||||
xf_unlock(&pool->proxy->lock);
|
||||
return b;
|
||||
}
|
||||
|
||||
/* ...return buffer back to pool */
|
||||
void xf_buffer_put(struct xf_buffer *buffer)
|
||||
{
|
||||
struct xf_pool *pool = buffer->link.pool;
|
||||
|
||||
xf_lock(&pool->proxy->lock);
|
||||
/* ...use global proxy lock for pool operations protection */
|
||||
/* ...put buffer back to a pool */
|
||||
buffer->link.next = pool->free, pool->free = buffer;
|
||||
|
||||
xf_unlock(&pool->proxy->lock);
|
||||
}
|
113
sound/soc/fsl/fsl_dsp_pool.h
Normal file
113
sound/soc/fsl/fsl_dsp_pool.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Xtensa buffer pool API header
|
||||
*
|
||||
* Copyright 2018 NXP
|
||||
* Copyright (c) 2012-2013 by Tensilica Inc
|
||||
*/
|
||||
#ifndef FSL_DSP_POOL_H
|
||||
#define FSL_DSP_POOL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "fsl_dsp_proxy.h"
|
||||
|
||||
/* ...buffer pool type */
|
||||
typedef u32 xf_pool_type_t;
|
||||
|
||||
/* ...previous declaration of struct */
|
||||
struct xf_buffer;
|
||||
struct xf_pool;
|
||||
struct xf_handle;
|
||||
struct xf_message;
|
||||
struct xf_client;
|
||||
|
||||
/* ...response callback */
|
||||
typedef void (*xf_response_cb)(struct xf_handle *h, struct xf_message *msg);
|
||||
|
||||
/* ...buffer pool type */
|
||||
enum xf_pool_type {
|
||||
XF_POOL_AUX = 0,
|
||||
XF_POOL_INPUT = 1,
|
||||
XF_POOL_OUTPUT = 2
|
||||
};
|
||||
|
||||
/* ...buffer link pointer */
|
||||
union xf_buffer_link {
|
||||
/* ...pointer to next free buffer in a pool (for free buffer) */
|
||||
struct xf_buffer *next;
|
||||
/* ...reference to a buffer pool (for allocated buffer) */
|
||||
struct xf_pool *pool;
|
||||
};
|
||||
|
||||
/* ...buffer descriptor */
|
||||
struct xf_buffer {
|
||||
/* ...virtual address of contiguous buffer */
|
||||
void *address;
|
||||
/* ...link pointer */
|
||||
union xf_buffer_link link;
|
||||
};
|
||||
|
||||
/* ...buffer pool */
|
||||
struct xf_pool {
|
||||
/* ...reference to proxy data */
|
||||
struct xf_proxy *proxy;
|
||||
/* ...length of individual buffer in a pool */
|
||||
u32 length;
|
||||
/* ...number of buffers in a pool */
|
||||
u32 number;
|
||||
/* ...pointer to pool memory */
|
||||
void *p;
|
||||
/* ...pointer to first free buffer in a pool */
|
||||
struct xf_buffer *free;
|
||||
/* ...individual buffers */
|
||||
struct xf_buffer buffer[0];
|
||||
};
|
||||
|
||||
/* component handle */
|
||||
struct xf_handle {
|
||||
/* ...reference to proxy data */
|
||||
struct xf_proxy *proxy;
|
||||
/* ...auxiliary control buffer for control transactions */
|
||||
struct xf_buffer *aux;
|
||||
/* ...global client-id of the component */
|
||||
u32 id;
|
||||
/* ...local client number (think about merging into "id" field - tbd) */
|
||||
u32 client;
|
||||
/* ...response processing hook */
|
||||
xf_response_cb response;
|
||||
};
|
||||
|
||||
/* ...accessor to buffer data */
|
||||
static inline void *xf_buffer_data(struct xf_buffer *buffer)
|
||||
{
|
||||
return buffer->address;
|
||||
}
|
||||
|
||||
/* ...length of buffer data */
|
||||
static inline size_t xf_buffer_length(struct xf_buffer *buffer)
|
||||
{
|
||||
struct xf_pool *pool = buffer->link.pool;
|
||||
|
||||
return (size_t)pool->length;
|
||||
}
|
||||
|
||||
/* ...component client-id (global scope) */
|
||||
static inline u32 xf_handle_id(struct xf_handle *handle)
|
||||
{
|
||||
return handle->id;
|
||||
}
|
||||
|
||||
/* ...pointer to auxiliary buffer */
|
||||
static inline void *xf_handle_aux(struct xf_handle *handle)
|
||||
{
|
||||
return xf_buffer_data(handle->aux);
|
||||
}
|
||||
|
||||
int xf_pool_alloc(struct xf_client *client, struct xf_proxy *proxy, u32 number,
|
||||
u32 length, xf_pool_type_t type, struct xf_pool **pool);
|
||||
int xf_pool_free(struct xf_client *client, struct xf_pool *pool);
|
||||
|
||||
struct xf_buffer *xf_buffer_get(struct xf_pool *pool);
|
||||
void xf_buffer_put(struct xf_buffer *buffer);
|
||||
|
||||
#endif /* FSL_DSP_POOL_H */
|
858
sound/soc/fsl/fsl_dsp_proxy.c
Normal file
858
sound/soc/fsl/fsl_dsp_proxy.c
Normal file
|
@ -0,0 +1,858 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
//
|
||||
// DSP proxy driver transfers messages between DSP driver and DSP framework
|
||||
//
|
||||
// Copyright 2018 NXP
|
||||
// Copyright (C) 2017 Cadence Design Systems, Inc.
|
||||
|
||||
#include "fsl_dsp_proxy.h"
|
||||
#include "fsl_dsp.h"
|
||||
|
||||
/* ...initialize message queue */
|
||||
void xf_msg_queue_init(struct xf_msg_queue *queue)
|
||||
{
|
||||
queue->head = queue->tail = NULL;
|
||||
}
|
||||
|
||||
/* ...get message queue head */
|
||||
struct xf_message *xf_msg_queue_head(struct xf_msg_queue *queue)
|
||||
{
|
||||
return queue->head;
|
||||
}
|
||||
|
||||
/* ...allocate new message from the pool */
|
||||
struct xf_message *xf_msg_alloc(struct xf_proxy *proxy)
|
||||
{
|
||||
struct xf_message *m = proxy->free;
|
||||
|
||||
/* ...make sure we have a free message item */
|
||||
if (m != NULL) {
|
||||
/* ...get message from the pool */
|
||||
proxy->free = m->next, m->next = NULL;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/* ...return message to the pool of free items */
|
||||
void xf_msg_free(struct xf_proxy *proxy, struct xf_message *m)
|
||||
{
|
||||
/* ...put message into the head of free items list */
|
||||
m->next = proxy->free, proxy->free = m;
|
||||
|
||||
/* ...notify potential client waiting for message */
|
||||
wake_up(&proxy->busy);
|
||||
}
|
||||
|
||||
/* ...return all messages from the queue to the pool of free items */
|
||||
void xf_msg_free_all(struct xf_proxy *proxy, struct xf_msg_queue *queue)
|
||||
{
|
||||
struct xf_message *m = queue->head;
|
||||
|
||||
/* ...check if there is anything in the queue */
|
||||
if (m != NULL) {
|
||||
queue->tail->next = proxy->free;
|
||||
proxy->free = queue->head;
|
||||
queue->head = queue->tail = NULL;
|
||||
|
||||
/* ...notify potential client waiting for message */
|
||||
wake_up(&proxy->busy);
|
||||
}
|
||||
}
|
||||
|
||||
/* ...submit message to a queue */
|
||||
int xf_msg_enqueue(struct xf_msg_queue *queue, struct xf_message *m)
|
||||
{
|
||||
int first = (queue->head == NULL);
|
||||
|
||||
/* ...set pointer to next item */
|
||||
m->next = NULL;
|
||||
|
||||
/* ...advance head/tail pointer as required */
|
||||
if (first)
|
||||
queue->head = m;
|
||||
else
|
||||
queue->tail->next = m;
|
||||
|
||||
/* ...new tail points to this message */
|
||||
queue->tail = m;
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
/* ...retrieve next message from the per-task queue */
|
||||
struct xf_message *xf_msg_dequeue(struct xf_msg_queue *queue)
|
||||
{
|
||||
struct xf_message *m = queue->head;
|
||||
|
||||
/* ...check if there is anything in the queue */
|
||||
if (m != NULL) {
|
||||
/* ...pop message from the head of the list */
|
||||
queue->head = m->next;
|
||||
if (queue->head == NULL)
|
||||
queue->tail = NULL;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/* ...helper function for requesting execution message from a pool */
|
||||
struct xf_message *xf_msg_available(struct xf_proxy *proxy)
|
||||
{
|
||||
struct xf_message *m;
|
||||
|
||||
/* ...acquire global lock */
|
||||
xf_lock(&proxy->lock);
|
||||
|
||||
/* ...try to allocate the message */
|
||||
m = xf_msg_alloc(proxy);
|
||||
if (m == NULL) {
|
||||
/* ...failed to allocate message; release lock */
|
||||
xf_unlock(&proxy->lock);
|
||||
}
|
||||
|
||||
/* ...if successfully allocated */
|
||||
return m;
|
||||
}
|
||||
|
||||
/* ...helper function for receiving a message from per-client queue */
|
||||
struct xf_message *xf_msg_received(struct xf_proxy *proxy,
|
||||
struct xf_msg_queue *queue)
|
||||
{
|
||||
struct xf_message *m;
|
||||
|
||||
/* ...acquire global lock */
|
||||
xf_lock(&proxy->lock);
|
||||
|
||||
/* ...try to peek message from the queue */
|
||||
m = xf_msg_dequeue(queue);
|
||||
if (m == NULL) {
|
||||
/* ...queue is empty; release lock */
|
||||
xf_unlock(&proxy->lock);
|
||||
}
|
||||
|
||||
/* ...if message is non-null, lock is held */
|
||||
return m;
|
||||
}
|
||||
|
||||
/*
|
||||
* MU related functions
|
||||
*/
|
||||
u32 icm_intr_send(struct xf_proxy *proxy, u32 msg)
|
||||
{
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy,
|
||||
struct fsl_dsp, proxy);
|
||||
|
||||
MU_SendMessage(dsp_priv->mu_base_virtaddr, 0, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icm_intr_extended_send(struct xf_proxy *proxy,
|
||||
u32 msg,
|
||||
struct dsp_ext_msg *ext_msg)
|
||||
{
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy,
|
||||
struct fsl_dsp, proxy);
|
||||
struct device *dev = dsp_priv->dev;
|
||||
union icm_header_t msghdr;
|
||||
|
||||
msghdr.allbits = msg;
|
||||
if (msghdr.size != 8)
|
||||
dev_err(dev, "too much ext msg\n");
|
||||
|
||||
MU_SendMessage(dsp_priv->mu_base_virtaddr, 1, ext_msg->phys);
|
||||
MU_SendMessage(dsp_priv->mu_base_virtaddr, 2, ext_msg->size);
|
||||
MU_SendMessage(dsp_priv->mu_base_virtaddr, 0, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int send_dpu_ext_msg_addr(struct xf_proxy *proxy)
|
||||
{
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy,
|
||||
struct fsl_dsp, proxy);
|
||||
union icm_header_t msghdr;
|
||||
struct dsp_ext_msg ext_msg;
|
||||
struct dsp_mem_msg *dpu_ext_msg =
|
||||
(struct dsp_mem_msg *)((unsigned char *)dsp_priv->msg_buf_virt
|
||||
+ (MSG_BUF_SIZE / 2));
|
||||
int ret_val = 0;
|
||||
|
||||
msghdr.allbits = 0; /* clear all bits; */
|
||||
msghdr.ack = 0;
|
||||
msghdr.intr = 1;
|
||||
msghdr.msg = ICM_CORE_INIT;
|
||||
msghdr.size = 8;
|
||||
ext_msg.phys = dsp_priv->msg_buf_phys + (MSG_BUF_SIZE / 2);
|
||||
ext_msg.size = sizeof(struct dsp_mem_msg);
|
||||
|
||||
dpu_ext_msg->ext_msg_phys = dsp_priv->msg_buf_phys;
|
||||
dpu_ext_msg->ext_msg_size = MSG_BUF_SIZE;
|
||||
dpu_ext_msg->scratch_phys = dsp_priv->scratch_buf_phys;
|
||||
dpu_ext_msg->scratch_size = dsp_priv->scratch_buf_size;
|
||||
dpu_ext_msg->dsp_config_phys = dsp_priv->dsp_config_phys;
|
||||
dpu_ext_msg->dsp_config_size = dsp_priv->dsp_config_size;
|
||||
dpu_ext_msg->dsp_board_type = dsp_priv->dsp_board_type;
|
||||
|
||||
icm_intr_extended_send(proxy, msghdr.allbits, &ext_msg);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
long icm_ack_wait(struct xf_proxy *proxy, u32 msg)
|
||||
{
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy,
|
||||
struct fsl_dsp, proxy);
|
||||
struct device *dev = dsp_priv->dev;
|
||||
union icm_header_t msghdr;
|
||||
int err;
|
||||
|
||||
msghdr.allbits = msg;
|
||||
/* wait response from mu */
|
||||
err = wait_for_completion_timeout(&proxy->cmd_complete,
|
||||
msecs_to_jiffies(1000));
|
||||
if (!err) {
|
||||
dev_err(dev, "icm ack timeout! %x\n", msg);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Ack recd for message 0x%08x\n", msghdr.allbits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
irqreturn_t fsl_dsp_mu_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct xf_proxy *proxy = dev_id;
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy,
|
||||
struct fsl_dsp, proxy);
|
||||
struct device *dev = dsp_priv->dev;
|
||||
union icm_header_t msghdr;
|
||||
u32 reg;
|
||||
|
||||
MU_ReceiveMsg(dsp_priv->mu_base_virtaddr, 0, ®);
|
||||
msghdr = (union icm_header_t)reg;
|
||||
|
||||
if (msghdr.intr == 1) {
|
||||
dev_dbg(dev, "INTR: Received ICM intr, msg 0x%08x\n",
|
||||
msghdr.allbits);
|
||||
switch (msghdr.msg) {
|
||||
case ICM_CORE_EXIT:
|
||||
break;
|
||||
case ICM_CORE_READY:
|
||||
send_dpu_ext_msg_addr(proxy);
|
||||
proxy->is_ready = 1;
|
||||
complete(&proxy->cmd_complete);
|
||||
break;
|
||||
case XF_SUSPEND:
|
||||
case XF_RESUME:
|
||||
complete(&proxy->cmd_complete);
|
||||
break;
|
||||
default:
|
||||
schedule_work(&proxy->work);
|
||||
break;
|
||||
}
|
||||
} else if (msghdr.ack == 1) {
|
||||
dev_dbg(dev, "INTR: Received ICM ack 0x%08x\n", msghdr.size);
|
||||
msghdr.ack = 0;
|
||||
} else {
|
||||
dev_dbg(dev, "Received false ICM intr 0x%08x\n",
|
||||
msghdr.allbits);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Proxy related functions
|
||||
*/
|
||||
/* ...NULL-address specification */
|
||||
#define XF_PROXY_NULL (~0U)
|
||||
|
||||
#define XF_PROXY_BADADDR (dsp_priv->scratch_buf_size)
|
||||
|
||||
/* ...shared memory translation - kernel virtual address to shared address */
|
||||
u32 xf_proxy_b2a(struct xf_proxy *proxy, void *b)
|
||||
{
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy,
|
||||
struct fsl_dsp, proxy);
|
||||
|
||||
if (b == NULL)
|
||||
return XF_PROXY_NULL;
|
||||
else if ((u32)(b - dsp_priv->scratch_buf_virt) <
|
||||
dsp_priv->scratch_buf_size)
|
||||
return (u32)(b - dsp_priv->scratch_buf_virt);
|
||||
else
|
||||
return XF_PROXY_BADADDR;
|
||||
}
|
||||
|
||||
/* ...shared memory translation - shared address to kernel virtual address */
|
||||
void *xf_proxy_a2b(struct xf_proxy *proxy, u32 address)
|
||||
{
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy,
|
||||
struct fsl_dsp, proxy);
|
||||
|
||||
if (address < dsp_priv->scratch_buf_size)
|
||||
return dsp_priv->scratch_buf_virt + address;
|
||||
else if (address == XF_PROXY_NULL)
|
||||
return NULL;
|
||||
else
|
||||
return (void *) -1;
|
||||
}
|
||||
|
||||
/* ...process association between response received and intended client */
|
||||
static void xf_cmap(struct xf_proxy *proxy, struct xf_message *m)
|
||||
{
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy,
|
||||
struct fsl_dsp, proxy);
|
||||
u32 id = XF_AP_IPC_CLIENT(m->id);
|
||||
struct xf_client *client;
|
||||
|
||||
/* ...process messages addressed to proxy itself */
|
||||
if (id == 0) {
|
||||
/* ...place message into local response queue */
|
||||
xf_msg_enqueue(&proxy->response, m);
|
||||
wake_up(&proxy->wait);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ...make sure the client ID is sane */
|
||||
client = xf_client_lookup(dsp_priv, id);
|
||||
if (!client) {
|
||||
pr_err("rsp[id:%08x]: client lookup failed", m->id);
|
||||
xf_msg_free(proxy, m);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ...make sure client is bound to this proxy interface */
|
||||
if (client->proxy != proxy) {
|
||||
pr_err("rsp[id:%08x]: wrong proxy interface", m->id);
|
||||
xf_msg_free(proxy, m);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ...place message into local response queue */
|
||||
if (xf_msg_enqueue(&client->queue, m))
|
||||
wake_up(&client->wait);
|
||||
}
|
||||
|
||||
/* ...retrieve pending responses from shared memory ring-buffer */
|
||||
static u32 xf_shmem_process_responses(struct xf_proxy *proxy)
|
||||
{
|
||||
struct xf_message *m;
|
||||
u32 read_idx, write_idx;
|
||||
int status;
|
||||
|
||||
status = 0;
|
||||
|
||||
/* ...get current values of read/write pointers in response queue */
|
||||
read_idx = XF_PROXY_READ(proxy, rsp_read_idx);
|
||||
write_idx = XF_PROXY_READ(proxy, rsp_write_idx);
|
||||
|
||||
/* ...process all committed responses */
|
||||
while (!XF_QUEUE_EMPTY(read_idx, write_idx)) {
|
||||
struct xf_proxy_message *response;
|
||||
|
||||
/* ...allocate execution message */
|
||||
m = xf_msg_alloc(proxy);
|
||||
if (m == NULL)
|
||||
break;
|
||||
|
||||
/* ...mark the interface status has changed */
|
||||
status |= (XF_QUEUE_FULL(read_idx, write_idx) ? 0x3 : 0x1);
|
||||
|
||||
/* ...get oldest not yet processed response */
|
||||
response = XF_PROXY_RESPONSE(proxy, XF_QUEUE_IDX(read_idx));
|
||||
|
||||
/* ...fill message parameters */
|
||||
m->id = response->session_id;
|
||||
m->opcode = response->opcode;
|
||||
m->length = response->length;
|
||||
m->buffer = xf_proxy_a2b(proxy, response->address);
|
||||
m->ret = response->ret;
|
||||
|
||||
/* ...advance local reading index copy */
|
||||
read_idx = XF_QUEUE_ADVANCE_IDX(read_idx);
|
||||
|
||||
/* ...update shadow copy of reading index */
|
||||
XF_PROXY_WRITE(proxy, rsp_read_idx, read_idx);
|
||||
|
||||
/* ...submit message to proper client */
|
||||
xf_cmap(proxy, m);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* ...put pending commands into shared memory ring-buffer */
|
||||
static u32 xf_shmem_process_commands(struct xf_proxy *proxy)
|
||||
{
|
||||
struct xf_message *m;
|
||||
u32 read_idx, write_idx;
|
||||
int status = 0;
|
||||
|
||||
/* ...get current value of peer read pointer */
|
||||
write_idx = XF_PROXY_READ(proxy, cmd_write_idx);
|
||||
read_idx = XF_PROXY_READ(proxy, cmd_read_idx);
|
||||
|
||||
/* ...submit any pending commands */
|
||||
while (!XF_QUEUE_FULL(read_idx, write_idx)) {
|
||||
struct xf_proxy_message *command;
|
||||
|
||||
/* ...check if we have a pending command */
|
||||
m = xf_msg_dequeue(&proxy->command);
|
||||
if (m == NULL)
|
||||
break;
|
||||
|
||||
/* ...always mark the interface status has changed */
|
||||
status |= 0x3;
|
||||
|
||||
/* ...select the place for the command */
|
||||
command = XF_PROXY_COMMAND(proxy, XF_QUEUE_IDX(write_idx));
|
||||
|
||||
/* ...put the response message fields */
|
||||
command->session_id = m->id;
|
||||
command->opcode = m->opcode;
|
||||
command->length = m->length;
|
||||
command->address = xf_proxy_b2a(proxy, m->buffer);
|
||||
command->ret = m->ret;
|
||||
|
||||
/* ...return message back to the pool */
|
||||
xf_msg_free(proxy, m);
|
||||
|
||||
/* ...advance local writing index copy */
|
||||
write_idx = XF_QUEUE_ADVANCE_IDX(write_idx);
|
||||
|
||||
/* ...update shared copy of queue write pointer */
|
||||
XF_PROXY_WRITE(proxy, cmd_write_idx, write_idx);
|
||||
}
|
||||
|
||||
if (status)
|
||||
icm_intr_send(proxy, 0);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* ...shared memory interface maintenance routine */
|
||||
void xf_proxy_process(struct work_struct *w)
|
||||
{
|
||||
struct xf_proxy *proxy = container_of(w, struct xf_proxy, work);
|
||||
int status = 0;
|
||||
|
||||
/* ...get exclusive access to internal data */
|
||||
xf_lock(&proxy->lock);
|
||||
|
||||
do {
|
||||
/* ...process outgoing commands first */
|
||||
status = xf_shmem_process_commands(proxy);
|
||||
|
||||
/* ...process all pending responses */
|
||||
status |= xf_shmem_process_responses(proxy);
|
||||
|
||||
} while (status);
|
||||
|
||||
/* ...unlock internal proxy data */
|
||||
xf_unlock(&proxy->lock);
|
||||
}
|
||||
|
||||
/* ...initialize shared memory interface */
|
||||
int xf_proxy_init(struct xf_proxy *proxy)
|
||||
{
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy,
|
||||
struct fsl_dsp, proxy);
|
||||
struct xf_message *m;
|
||||
int i;
|
||||
|
||||
/* ...create a list of all messages in a pool; set head pointer */
|
||||
proxy->free = &proxy->pool[0];
|
||||
|
||||
/* ...put all messages into a single-linked list */
|
||||
for (i = 0, m = proxy->free; i < XF_CFG_MESSAGE_POOL_SIZE - 1; i++, m++)
|
||||
m->next = m + 1;
|
||||
|
||||
/* ...set list tail pointer */
|
||||
m->next = NULL;
|
||||
|
||||
/* ...initialize proxy lock */
|
||||
xf_lock_init(&proxy->lock);
|
||||
|
||||
/* ...initialize proxy thread message queues */
|
||||
xf_msg_queue_init(&proxy->command);
|
||||
xf_msg_queue_init(&proxy->response);
|
||||
|
||||
/* ...initialize global busy queue */
|
||||
init_waitqueue_head(&proxy->busy);
|
||||
init_waitqueue_head(&proxy->wait);
|
||||
|
||||
/* ...create work structure */
|
||||
INIT_WORK(&proxy->work, xf_proxy_process);
|
||||
|
||||
/* ...set pointer to shared memory */
|
||||
proxy->ipc.shmem = (struct xf_shmem_data *)dsp_priv->msg_buf_virt;
|
||||
|
||||
/* ...initialize shared memory interface */
|
||||
XF_PROXY_WRITE(proxy, cmd_read_idx, 0);
|
||||
XF_PROXY_WRITE(proxy, cmd_write_idx, 0);
|
||||
XF_PROXY_WRITE(proxy, cmd_invalid, 0);
|
||||
XF_PROXY_WRITE(proxy, rsp_read_idx, 0);
|
||||
XF_PROXY_WRITE(proxy, rsp_write_idx, 0);
|
||||
XF_PROXY_WRITE(proxy, rsp_invalid, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ...trigger shared memory interface processing */
|
||||
void xf_proxy_notify(struct xf_proxy *proxy)
|
||||
{
|
||||
schedule_work(&proxy->work);
|
||||
}
|
||||
|
||||
/* ...submit a command to proxy pending queue (lock released upon return) */
|
||||
void xf_proxy_command(struct xf_proxy *proxy, struct xf_message *m)
|
||||
{
|
||||
int first;
|
||||
|
||||
/* ...submit message to proxy thread */
|
||||
first = xf_msg_enqueue(&proxy->command, m);
|
||||
|
||||
/* ...release the lock */
|
||||
xf_unlock(&proxy->lock);
|
||||
|
||||
/* ...notify thread about command reception */
|
||||
(first ? xf_proxy_notify(proxy), 1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Proxy cmd send and receive functions
|
||||
*/
|
||||
int xf_cmd_send(struct xf_proxy *proxy,
|
||||
u32 id,
|
||||
u32 opcode,
|
||||
void *buffer,
|
||||
u32 length)
|
||||
{
|
||||
struct xf_message *m;
|
||||
int ret;
|
||||
|
||||
/* ...retrieve message handle (take the lock on success) */
|
||||
ret = wait_event_interruptible(proxy->busy,
|
||||
(m = xf_msg_available(proxy)) != NULL);
|
||||
if (ret)
|
||||
return -EINTR;
|
||||
|
||||
/* ...fill-in message parameters (lock is taken) */
|
||||
m->id = id;
|
||||
m->opcode = opcode;
|
||||
m->length = length;
|
||||
m->buffer = buffer;
|
||||
m->ret = 0;
|
||||
|
||||
/* ...submit command to the proxy */
|
||||
xf_proxy_command(proxy, m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct xf_message *xf_cmd_recv(struct xf_proxy *proxy,
|
||||
wait_queue_head_t *wq,
|
||||
struct xf_msg_queue *queue,
|
||||
int wait)
|
||||
{
|
||||
struct xf_message *m = NULL;
|
||||
int ret;
|
||||
|
||||
/* ...wait for message reception (take lock on success) */
|
||||
ret = wait_event_interruptible(*wq,
|
||||
(m = xf_msg_received(proxy, queue)) != NULL || !wait
|
||||
|| !proxy->is_active);
|
||||
if (ret)
|
||||
return ERR_PTR(-EINTR);
|
||||
|
||||
/* ...return message with a lock taken */
|
||||
return m;
|
||||
}
|
||||
|
||||
struct xf_message *xf_cmd_recv_timeout(struct xf_proxy *proxy,
|
||||
wait_queue_head_t *wq,
|
||||
struct xf_msg_queue *queue, int wait)
|
||||
{
|
||||
struct xf_message *m;
|
||||
int ret;
|
||||
|
||||
/* ...wait for message reception (take lock on success) */
|
||||
ret = wait_event_interruptible_timeout(*wq,
|
||||
(m = xf_msg_received(proxy, queue)) != NULL || !wait,
|
||||
msecs_to_jiffies(1000));
|
||||
if (ret < 0)
|
||||
return ERR_PTR(-EINTR);
|
||||
|
||||
if (ret == 0)
|
||||
return ERR_PTR(-ETIMEDOUT);
|
||||
|
||||
/* ...return message with a lock taken */
|
||||
return m;
|
||||
}
|
||||
|
||||
/* ...helper function for synchronous command execution */
|
||||
struct xf_message *xf_cmd_send_recv(struct xf_proxy *proxy,
|
||||
u32 id, u32 opcode,
|
||||
void *buffer,
|
||||
u32 length)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* ...send command to remote proxy */
|
||||
ret = xf_cmd_send(proxy, id, opcode, buffer, length);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
/* ...wait for message delivery */
|
||||
return xf_cmd_recv(proxy, &proxy->wait, &proxy->response, 1);
|
||||
}
|
||||
|
||||
struct xf_message *xf_cmd_send_recv_wq(struct xf_proxy *proxy, u32 id,
|
||||
u32 opcode, void *buffer, u32 length,
|
||||
wait_queue_head_t *wq,
|
||||
struct xf_msg_queue *queue)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* ...send command to remote proxy */
|
||||
ret = xf_cmd_send(proxy, id, opcode, buffer, length);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
/* ...wait for message delivery */
|
||||
return xf_cmd_recv(proxy, wq, queue, 1);
|
||||
}
|
||||
|
||||
struct xf_message *xf_cmd_send_recv_complete(struct xf_client *client,
|
||||
struct xf_proxy *proxy,
|
||||
u32 id, u32 opcode, void *buffer,
|
||||
u32 length,
|
||||
struct work_struct *work,
|
||||
struct completion *completion)
|
||||
{
|
||||
struct xf_message *m;
|
||||
int ret;
|
||||
|
||||
/* ...retrieve message handle (take the lock on success) */
|
||||
m = xf_msg_available(proxy);
|
||||
if (!m)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
/* ...fill-in message parameters (lock is taken) */
|
||||
m->id = id;
|
||||
m->opcode = opcode;
|
||||
m->length = length;
|
||||
m->buffer = buffer;
|
||||
m->ret = 0;
|
||||
|
||||
init_completion(completion);
|
||||
|
||||
/* ...submit command to the proxy */
|
||||
xf_proxy_command(proxy, m);
|
||||
|
||||
schedule_work(work);
|
||||
|
||||
/* ...wait for message reception (take lock on success) */
|
||||
ret = wait_for_completion_timeout(completion,
|
||||
msecs_to_jiffies(1000));
|
||||
if (!ret)
|
||||
return ERR_PTR(-ETIMEDOUT);
|
||||
|
||||
m = &client->m;
|
||||
|
||||
/* ...return message with a lock taken */
|
||||
return m;
|
||||
}
|
||||
/*
|
||||
* Proxy allocate and free memory functions
|
||||
*/
|
||||
/* ...allocate memory buffer for kernel use */
|
||||
int xf_cmd_alloc(struct xf_proxy *proxy, void **buffer, u32 length)
|
||||
{
|
||||
struct xf_message *m;
|
||||
u32 id = 0;
|
||||
int ret;
|
||||
|
||||
/* ...send command to remote proxy */
|
||||
m = xf_cmd_send_recv(proxy, id, XF_ALLOC, NULL, length);
|
||||
if (IS_ERR(m)) {
|
||||
xf_unlock(&proxy->lock);
|
||||
ret = PTR_ERR(m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ...check if response is expected */
|
||||
if (m->opcode == XF_ALLOC && m->buffer != NULL) {
|
||||
*buffer = m->buffer;
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -ENOMEM;
|
||||
}
|
||||
|
||||
/* ...free message and release proxy lock */
|
||||
xf_msg_free(proxy, m);
|
||||
xf_unlock(&proxy->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ...free memory buffer */
|
||||
int xf_cmd_free(struct xf_proxy *proxy, void *buffer, u32 length)
|
||||
{
|
||||
struct xf_message *m;
|
||||
u32 id = 0;
|
||||
int ret;
|
||||
|
||||
/* ...synchronously execute freeing command */
|
||||
m = xf_cmd_send_recv(proxy, id, XF_FREE, buffer, length);
|
||||
if (IS_ERR(m)) {
|
||||
xf_unlock(&proxy->lock);
|
||||
ret = PTR_ERR(m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ...check if response is expected */
|
||||
if (m->opcode == XF_FREE)
|
||||
ret = 0;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
/* ...free message and release proxy lock */
|
||||
xf_msg_free(proxy, m);
|
||||
xf_unlock(&proxy->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* suspend & resume functions
|
||||
*/
|
||||
int xf_cmd_send_suspend(struct xf_proxy *proxy)
|
||||
{
|
||||
union icm_header_t msghdr;
|
||||
int ret = 0;
|
||||
|
||||
init_completion(&proxy->cmd_complete);
|
||||
|
||||
msghdr.allbits = 0; /* clear all bits; */
|
||||
msghdr.ack = 0;
|
||||
msghdr.intr = 1;
|
||||
msghdr.msg = XF_SUSPEND;
|
||||
msghdr.size = 0;
|
||||
icm_intr_send(proxy, msghdr.allbits);
|
||||
|
||||
/* wait for response here */
|
||||
ret = icm_ack_wait(proxy, msghdr.allbits);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xf_cmd_send_resume(struct xf_proxy *proxy)
|
||||
{
|
||||
union icm_header_t msghdr;
|
||||
int ret = 0;
|
||||
|
||||
init_completion(&proxy->cmd_complete);
|
||||
|
||||
msghdr.allbits = 0; /* clear all bits; */
|
||||
msghdr.ack = 0;
|
||||
msghdr.intr = 1;
|
||||
msghdr.msg = XF_RESUME;
|
||||
msghdr.size = 0;
|
||||
icm_intr_send(proxy, msghdr.allbits);
|
||||
|
||||
/* wait for response here */
|
||||
ret = icm_ack_wait(proxy, msghdr.allbits);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ...open component handle */
|
||||
int xf_open(struct xf_client *client, struct xf_proxy *proxy,
|
||||
struct xf_handle *handle, const char *id, u32 core,
|
||||
xf_response_cb response)
|
||||
{
|
||||
void *b;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
|
||||
/* ...retrieve auxiliary control buffer from proxy - need I */
|
||||
handle->aux = xf_buffer_get(proxy->aux);
|
||||
|
||||
b = xf_handle_aux(handle);
|
||||
|
||||
msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_DSP_PROXY(0));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_REGISTER;
|
||||
msg.buffer = b;
|
||||
msg.length = strlen(id) + 1;
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...copy component identifier */
|
||||
memcpy(b, (void *)id, xf_buffer_length(handle->aux));
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
|
||||
if (IS_ERR(rmsg)) {
|
||||
xf_buffer_put(handle->aux), handle->aux = NULL;
|
||||
return PTR_ERR(rmsg);
|
||||
}
|
||||
/* ...save received component global client-id */
|
||||
handle->id = XF_MSG_SRC(rmsg->id);
|
||||
/* TODO: review cleanup */
|
||||
/* xf_msg_free(proxy, rmsg);
|
||||
* xf_unlock(&proxy->lock); */
|
||||
|
||||
/* ...if failed, release buffer handle */
|
||||
/* ...operation completed successfully; assign handle data */
|
||||
handle->response = response;
|
||||
handle->proxy = proxy;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ...close component handle */
|
||||
int xf_close(struct xf_client *client, struct xf_handle *handle)
|
||||
{
|
||||
struct xf_proxy *proxy = handle->proxy;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
|
||||
/* ...do I need to take component lock here? guess no - tbd */
|
||||
|
||||
/* ...buffers and stuff? - tbd */
|
||||
|
||||
/* ...acquire global proxy lock */
|
||||
/* ...unregister component from remote DSP proxy (ignore result code) */
|
||||
|
||||
msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), handle->id);
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_UNREGISTER;
|
||||
msg.buffer = NULL;
|
||||
msg.length = 0;
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
|
||||
if (IS_ERR(rmsg)) {
|
||||
xf_buffer_put(handle->aux), handle->aux = NULL;
|
||||
return PTR_ERR(rmsg);
|
||||
}
|
||||
/* TODO: review cleanup */
|
||||
/* xf_msg_free(proxy, rmsg);
|
||||
* xf_unlock(&proxy->lock); */
|
||||
|
||||
/* ...wipe out proxy pointer */
|
||||
handle->proxy = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
520
sound/soc/fsl/fsl_dsp_proxy.h
Normal file
520
sound/soc/fsl/fsl_dsp_proxy.h
Normal file
|
@ -0,0 +1,520 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
||||
/*
|
||||
* DSP proxy header - commands/responses from DSP driver to DSP ramework
|
||||
*
|
||||
* Copyright 2018 NXP
|
||||
* Copyright (c) 2017 Cadence Design Systems, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __FSL_DSP_PROXY_H
|
||||
#define __FSL_DSP_PROXY_H
|
||||
|
||||
#include <linux/wait.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/platform_data/dma-imx.h>
|
||||
#include <linux/mx8_mu.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "fsl_dsp_pool.h"
|
||||
#define XF_CFG_MESSAGE_POOL_SIZE 256
|
||||
|
||||
struct xf_client;
|
||||
|
||||
/*******************************************************************************
|
||||
* Local proxy data
|
||||
******************************************************************************/
|
||||
|
||||
struct xf_message;
|
||||
struct xf_handle;
|
||||
typedef void (*xf_response_cb)(struct xf_handle *h, struct xf_message *msg);
|
||||
|
||||
/* ...execution message */
|
||||
struct xf_message {
|
||||
/* ...pointer to next message in a list */
|
||||
struct xf_message *next;
|
||||
|
||||
/* ...session-id */
|
||||
u32 id;
|
||||
|
||||
/* ...operation code */
|
||||
u32 opcode;
|
||||
|
||||
/* ...length of data buffer */
|
||||
u32 length;
|
||||
|
||||
/* ...translated data pointer */
|
||||
void *buffer;
|
||||
|
||||
/* ...return message status */
|
||||
u32 ret;
|
||||
};
|
||||
|
||||
/* ...message queue */
|
||||
struct xf_msg_queue {
|
||||
/* ...pointer to list head */
|
||||
struct xf_message *head;
|
||||
|
||||
/* ...pointer to list tail */
|
||||
struct xf_message *tail;
|
||||
};
|
||||
|
||||
struct xf_proxy_message {
|
||||
/* ...session ID */
|
||||
u32 session_id;
|
||||
|
||||
/* ...proxy API command/response code */
|
||||
u32 opcode;
|
||||
|
||||
/* ...length of attached buffer */
|
||||
u32 length;
|
||||
|
||||
/* ...physical address of message buffer */
|
||||
u32 address;
|
||||
|
||||
/* ...return message status */
|
||||
u32 ret;
|
||||
};
|
||||
/**********************************************************************/
|
||||
|
||||
enum icm_action_t {
|
||||
ICM_CORE_READY = 1,
|
||||
ICM_CORE_INIT,
|
||||
ICM_CORE_EXIT,
|
||||
};
|
||||
|
||||
/* ...adjust IPC client of message going from user-space */
|
||||
#define XF_MSG_AP_FROM_USER(id, client) (((id) & ~(0xF << 2)) | (client << 2))
|
||||
|
||||
|
||||
#define __XF_PORT_SPEC(core, id, port) ((core) | ((id) << 2) | ((port) << 8))
|
||||
#define __XF_PORT_SPEC2(id, port) ((id) | ((port) << 8))
|
||||
|
||||
|
||||
/* ...wipe out IPC client from message going to user-space */
|
||||
#define XF_MSG_AP_TO_USER(id) ((id) & ~(0xF << 18))
|
||||
#define __XF_AP_PROXY(core) ((core) | 0x8000)
|
||||
#define __XF_DSP_PROXY(core) ((core) | 0x8000)
|
||||
|
||||
/* ...message id contains source and destination ports specification */
|
||||
#define __XF_MSG_ID(src, dst) (((src) & 0xFFFF) | (((dst) & 0xFFFF) << 16))
|
||||
#define XF_MSG_SRC(id) (((id) >> 0) & 0xFFFF)
|
||||
#define XF_MSG_SRC_CORE(id) (((id) >> 0) & 0x3)
|
||||
#define XF_MSG_SRC_CLIENT(id) (((id) >> 2) & 0x3F)
|
||||
#define XF_MSG_DST_CLIENT(id) (((id) >> 18) & 0x3F)
|
||||
|
||||
/* ...special treatment of AP-proxy destination field */
|
||||
#define XF_AP_IPC_CLIENT(id) (((id) >> 18) & 0xF)
|
||||
#define XF_AP_CLIENT(id) (((id) >> 22) & 0x1FF)
|
||||
#define __XF_AP_PROXY(core) ((core) | 0x8000)
|
||||
#define __XF_DSP_PROXY(core) ((core) | 0x8000)
|
||||
#define __XF_AP_CLIENT(core, client) ((core) | ((client) << 6) | 0x8000)
|
||||
|
||||
/* ...opcode composition with command/response data tags */
|
||||
#define __XF_OPCODE(c, r, op) (((c) << 31) | ((r) << 30) | ((op) & 0x3F))
|
||||
|
||||
/* ...shared buffer allocation */
|
||||
#define XF_ALLOC __XF_OPCODE(0, 0, 4)
|
||||
|
||||
/* ...shared buffer freeing */
|
||||
#define XF_FREE __XF_OPCODE(0, 0, 5)
|
||||
|
||||
/* ...resume component operation */
|
||||
#define XF_RESUME __XF_OPCODE(0, 0, 14)
|
||||
|
||||
/* ...resume component operation */
|
||||
#define XF_SUSPEND __XF_OPCODE(0, 0, 15)
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Ring buffer support
|
||||
******************************************************************************/
|
||||
/* ...cache-line size on DSP */
|
||||
#define XF_PROXY_ALIGNMENT 64
|
||||
|
||||
/* ...total length of shared memory queue (for commands and responses) */
|
||||
#define XF_PROXY_MESSAGE_QUEUE_LENGTH (1 << 6)
|
||||
|
||||
/* ...index mask */
|
||||
#define XF_PROXY_MESSAGE_QUEUE_MASK 0x3F
|
||||
|
||||
/* ...ring-buffer index */
|
||||
#define __XF_QUEUE_IDX(idx, counter) \
|
||||
(((idx) & XF_PROXY_MESSAGE_QUEUE_MASK) | ((counter) << 16))
|
||||
|
||||
/* ...retrieve ring-buffer index */
|
||||
#define XF_QUEUE_IDX(idx) \
|
||||
((idx) & XF_PROXY_MESSAGE_QUEUE_MASK)
|
||||
|
||||
/* ...increment ring-buffer index */
|
||||
#define XF_QUEUE_ADVANCE_IDX(idx) \
|
||||
(((idx) + 0x10001) & (0xFFFF0000 | XF_PROXY_MESSAGE_QUEUE_MASK))
|
||||
|
||||
/* ...test if ring buffer is empty */
|
||||
#define XF_QUEUE_EMPTY(read, write) \
|
||||
((read) == (write))
|
||||
|
||||
/* ...test if ring buffer is full */
|
||||
#define XF_QUEUE_FULL(read, write) \
|
||||
((write) == (read) + (XF_PROXY_MESSAGE_QUEUE_LENGTH << 16))
|
||||
|
||||
/* ...basic cache operations */
|
||||
#define XF_PROXY_INVALIDATE(addr, len) { }
|
||||
|
||||
#define XF_PROXY_FLUSH(addr, len) { }
|
||||
|
||||
/* ...data managed by host CPU (remote) - in case of shunt it is a IPC layer */
|
||||
struct xf_proxy_host_data {
|
||||
/* ...command queue */
|
||||
struct xf_proxy_message command[XF_PROXY_MESSAGE_QUEUE_LENGTH];
|
||||
|
||||
/* ...writing index into command queue */
|
||||
u32 cmd_write_idx;
|
||||
|
||||
/* ...reading index for response queue */
|
||||
u32 rsp_read_idx;
|
||||
|
||||
/* ...indicate command queue is valid or not */
|
||||
u32 cmd_invalid;
|
||||
};
|
||||
|
||||
/* ...data managed by DSP (local) */
|
||||
struct xf_proxy_dsp_data {
|
||||
/* ...response queue */
|
||||
struct xf_proxy_message response[XF_PROXY_MESSAGE_QUEUE_LENGTH];
|
||||
|
||||
/* ...writing index into response queue */
|
||||
u32 rsp_write_idx;
|
||||
|
||||
/* ...reading index for command queue */
|
||||
u32 cmd_read_idx;
|
||||
|
||||
/* ...indicate response queue is valid or not */
|
||||
u32 rsp_invalid;
|
||||
};
|
||||
|
||||
/* ...shared memory data */
|
||||
struct xf_shmem_data {
|
||||
/* ...ingoing data (maintained by DSP (local side)) */
|
||||
struct xf_proxy_host_data local;
|
||||
|
||||
/* ...outgoing data (maintained by host CPU (remote side)) */
|
||||
struct xf_proxy_dsp_data remote;
|
||||
|
||||
};
|
||||
|
||||
/* ...shared memory data accessor */
|
||||
#define XF_SHMEM_DATA(proxy) \
|
||||
((proxy)->ipc.shmem)
|
||||
|
||||
/* ...atomic reading */
|
||||
#define __XF_PROXY_READ_ATOMIC(var) \
|
||||
({ XF_PROXY_INVALIDATE(&(var), sizeof(var)); \
|
||||
*(u32 *)&(var); })
|
||||
|
||||
/* ...atomic writing */
|
||||
#define __XF_PROXY_WRITE_ATOMIC(var, value) \
|
||||
({*(u32 *)&(var) = (value); \
|
||||
XF_PROXY_FLUSH(&(var), sizeof(var)); \
|
||||
(value); })
|
||||
|
||||
/* ...accessors */
|
||||
#define XF_PROXY_READ(proxy, field) \
|
||||
__XF_PROXY_READ_##field(XF_SHMEM_DATA(proxy))
|
||||
|
||||
#define XF_PROXY_WRITE(proxy, field, v) \
|
||||
__XF_PROXY_WRITE_##field(XF_SHMEM_DATA(proxy), (v))
|
||||
|
||||
/* ...individual fields reading */
|
||||
#define __XF_PROXY_READ_cmd_write_idx(shmem) \
|
||||
__XF_PROXY_READ_ATOMIC(shmem->local.cmd_write_idx)
|
||||
|
||||
#define __XF_PROXY_READ_cmd_read_idx(shmem) \
|
||||
shmem->remote.cmd_read_idx
|
||||
|
||||
#define __XF_PROXY_READ_cmd_invalid(shmem) \
|
||||
__XF_PROXY_READ_ATOMIC(shmem->local.cmd_invalid)
|
||||
|
||||
#define __XF_PROXY_READ_rsp_write_idx(shmem) \
|
||||
__XF_PROXY_READ_ATOMIC(shmem->remote.rsp_write_idx)
|
||||
|
||||
#define __XF_PROXY_READ_rsp_read_idx(shmem) \
|
||||
shmem->local.rsp_read_idx
|
||||
|
||||
#define __XF_PROXY_READ_rsp_invalid(shmem) \
|
||||
__XF_PROXY_READ_ATOMIC(shmem->remote.rsp_invalid)
|
||||
|
||||
/* ...individual fields writings */
|
||||
#define __XF_PROXY_WRITE_cmd_write_idx(shmem, v) \
|
||||
__XF_PROXY_WRITE_ATOMIC(shmem->local.cmd_write_idx, v)
|
||||
|
||||
#define __XF_PROXY_WRITE_cmd_read_idx(shmem, v) \
|
||||
__XF_PROXY_WRITE_ATOMIC(shmem->remote.cmd_read_idx, v)
|
||||
|
||||
#define __XF_PROXY_WRITE_cmd_invalid(shmem, v) \
|
||||
__XF_PROXY_WRITE_ATOMIC(shmem->local.cmd_invalid, v)
|
||||
|
||||
#define __XF_PROXY_WRITE_rsp_read_idx(shmem, v) \
|
||||
__XF_PROXY_WRITE_ATOMIC(shmem->local.rsp_read_idx, v)
|
||||
|
||||
#define __XF_PROXY_WRITE_rsp_write_idx(shmem, v) \
|
||||
__XF_PROXY_WRITE_ATOMIC(shmem->remote.rsp_write_idx, v)
|
||||
|
||||
#define __XF_PROXY_WRITE_rsp_invalid(shmem, v) \
|
||||
__XF_PROXY_WRITE_ATOMIC(shmem->remote.rsp_invalid, v)
|
||||
|
||||
/* ...command buffer accessor */
|
||||
#define XF_PROXY_COMMAND(proxy, idx) \
|
||||
(&XF_SHMEM_DATA(proxy)->local.command[(idx)])
|
||||
|
||||
/* ...response buffer accessor */
|
||||
#define XF_PROXY_RESPONSE(proxy, idx) \
|
||||
(&XF_SHMEM_DATA(proxy)->remote.response[(idx)])
|
||||
|
||||
/*******************************************************************************
|
||||
* Local proxy data
|
||||
******************************************************************************/
|
||||
|
||||
struct xf_proxy_ipc_data {
|
||||
/* ...shared memory data pointer */
|
||||
struct xf_shmem_data __iomem *shmem;
|
||||
|
||||
/* ...core identifier */
|
||||
u32 core;
|
||||
|
||||
/* ...IPC registers memory */
|
||||
void __iomem *regs;
|
||||
};
|
||||
|
||||
/* ...proxy data */
|
||||
struct xf_proxy {
|
||||
/* ...IPC layer data */
|
||||
struct xf_proxy_ipc_data ipc;
|
||||
|
||||
/* ...shared memory status change processing item */
|
||||
struct work_struct work;
|
||||
|
||||
struct completion cmd_complete;
|
||||
int is_ready;
|
||||
int is_active;
|
||||
|
||||
/* ...internal lock */
|
||||
spinlock_t lock;
|
||||
|
||||
/* ...busy queue (for clients waiting ON NOTIFIcation) */
|
||||
wait_queue_head_t busy;
|
||||
|
||||
/* ...waiting queue for synchronous proxy operations */
|
||||
wait_queue_head_t wait;
|
||||
|
||||
/* ...submitted commands queue */
|
||||
struct xf_msg_queue command;
|
||||
|
||||
/* ...pending responses queue */
|
||||
struct xf_msg_queue response;
|
||||
|
||||
/* ...global message pool */
|
||||
struct xf_message pool[XF_CFG_MESSAGE_POOL_SIZE];
|
||||
|
||||
/* ...pointer to first free message in the pool */
|
||||
struct xf_message *free;
|
||||
|
||||
/* ...auxiliary buffer pool for clients */
|
||||
struct xf_pool *aux;
|
||||
};
|
||||
|
||||
union icm_header_t {
|
||||
struct {
|
||||
u32 msg:6;
|
||||
u32 sub_msg:6; // sub_msg will have ICM_MSG
|
||||
u32 rsvd:3; /* reserved */
|
||||
u32 intr:1; /* intr = 1 when sending msg. */
|
||||
u32 size:15; /* =size in bytes (excluding header) */
|
||||
u32 ack:1; /* response message when ack=1 */
|
||||
};
|
||||
u32 allbits;
|
||||
};
|
||||
|
||||
struct dsp_ext_msg {
|
||||
u32 phys;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct dsp_mem_msg {
|
||||
u32 ext_msg_phys;
|
||||
u32 ext_msg_size;
|
||||
u32 scratch_phys;
|
||||
u32 scratch_size;
|
||||
u32 dsp_config_phys;
|
||||
u32 dsp_config_size;
|
||||
u32 dsp_board_type;
|
||||
};
|
||||
|
||||
static inline void xf_lock_init(spinlock_t *lock)
|
||||
{
|
||||
spin_lock_init(lock);
|
||||
}
|
||||
|
||||
static inline void xf_lock(spinlock_t *lock)
|
||||
{
|
||||
spin_lock(lock);
|
||||
}
|
||||
|
||||
static inline void xf_unlock(spinlock_t *lock)
|
||||
{
|
||||
spin_unlock(lock);
|
||||
}
|
||||
|
||||
/* ...init proxy */
|
||||
int xf_proxy_init(struct xf_proxy *proxy);
|
||||
|
||||
/* ...send message to proxy */
|
||||
int xf_cmd_send(struct xf_proxy *proxy,
|
||||
u32 id,
|
||||
u32 opcode,
|
||||
void *buffer,
|
||||
u32 length);
|
||||
|
||||
/* ...get message from proxy */
|
||||
struct xf_message *xf_cmd_recv(struct xf_proxy *proxy,
|
||||
wait_queue_head_t *wq,
|
||||
struct xf_msg_queue *queue,
|
||||
int wait);
|
||||
|
||||
struct xf_message*
|
||||
xf_cmd_recv_timeout(struct xf_proxy *proxy, wait_queue_head_t *wq,
|
||||
struct xf_msg_queue *queue, int wait);
|
||||
|
||||
struct xf_message*
|
||||
xf_cmd_send_recv(struct xf_proxy *proxy, u32 id, u32 opcode,
|
||||
void *buffer, u32 length);
|
||||
|
||||
struct xf_message*
|
||||
xf_cmd_send_recv_wq(struct xf_proxy *proxy, u32 id, u32 opcode, void *buffer,
|
||||
u32 length, wait_queue_head_t *wq,
|
||||
struct xf_msg_queue *queue);
|
||||
|
||||
struct xf_message*
|
||||
xf_cmd_send_recv_complete(struct xf_client *client, struct xf_proxy *proxy,
|
||||
u32 id, u32 opcode, void *buffer, u32 length,
|
||||
struct work_struct *work,
|
||||
struct completion *completion);
|
||||
|
||||
/* ...mu interrupt handle */
|
||||
irqreturn_t fsl_dsp_mu_isr(int irq, void *dev_id);
|
||||
|
||||
/* ...initialize client pending message queue */
|
||||
void xf_msg_queue_init(struct xf_msg_queue *queue);
|
||||
|
||||
/* ...return current queue state */
|
||||
struct xf_message *xf_msg_queue_head(struct xf_msg_queue *queue);
|
||||
|
||||
/* ...return the message back to a pool */
|
||||
void xf_msg_free(struct xf_proxy *proxy, struct xf_message *m);
|
||||
|
||||
/* ...release all pending messages */
|
||||
void xf_msg_free_all(struct xf_proxy *proxy, struct xf_msg_queue *queue);
|
||||
|
||||
/* ...wait mu interrupt */
|
||||
long icm_ack_wait(struct xf_proxy *proxy, u32 msg);
|
||||
|
||||
/* ...shared memory translation - kernel virtual address to shared address */
|
||||
u32 xf_proxy_b2a(struct xf_proxy *proxy, void *b);
|
||||
|
||||
/* ...shared memory translation - shared address to kernel virtual address */
|
||||
void *xf_proxy_a2b(struct xf_proxy *proxy, u32 address);
|
||||
|
||||
int xf_cmd_send_suspend(struct xf_proxy *proxy);
|
||||
int xf_cmd_send_resume(struct xf_proxy *proxy);
|
||||
|
||||
int xf_cmd_alloc(struct xf_proxy *proxy, void **buffer, u32 length);
|
||||
int xf_cmd_free(struct xf_proxy *proxy, void *buffer, u32 length);
|
||||
|
||||
int xf_open(struct xf_client *client, struct xf_proxy *proxy,
|
||||
struct xf_handle *handle, const char *id, u32 core,
|
||||
xf_response_cb response);
|
||||
|
||||
int xf_close(struct xf_client *client, struct xf_handle *handle);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Opcode composition
|
||||
******************************************************************************/
|
||||
|
||||
/* ...opcode composition with command/response data tags */
|
||||
#define __XF_OPCODE(c, r, op) (((c) << 31) | ((r) << 30) | ((op) & 0x3F))
|
||||
|
||||
/* ...accessors */
|
||||
#define XF_OPCODE_CDATA(opcode) ((opcode) & (1 << 31))
|
||||
#define XF_OPCODE_RDATA(opcode) ((opcode) & (1 << 30))
|
||||
#define XF_OPCODE_TYPE(opcode) ((opcode) & (0x3F))
|
||||
|
||||
/*******************************************************************************
|
||||
* Opcode types
|
||||
******************************************************************************/
|
||||
|
||||
/* ...unregister client */
|
||||
#define XF_UNREGISTER __XF_OPCODE(0, 0, 0)
|
||||
|
||||
/* ...register client at proxy */
|
||||
#define XF_REGISTER __XF_OPCODE(1, 0, 1)
|
||||
|
||||
/* ...port routing command */
|
||||
#define XF_ROUTE __XF_OPCODE(1, 0, 2)
|
||||
|
||||
/* ...port unrouting command */
|
||||
#define XF_UNROUTE __XF_OPCODE(1, 0, 3)
|
||||
|
||||
/* ...shared buffer allocation */
|
||||
#define XF_ALLOC __XF_OPCODE(0, 0, 4)
|
||||
|
||||
/* ...shared buffer freeing */
|
||||
#define XF_FREE __XF_OPCODE(0, 0, 5)
|
||||
|
||||
/* ...set component parameters */
|
||||
#define XF_SET_PARAM __XF_OPCODE(1, 0, 6)
|
||||
|
||||
/* ...get component parameters */
|
||||
#define XF_GET_PARAM __XF_OPCODE(1, 1, 7)
|
||||
|
||||
/* ...input buffer reception */
|
||||
#define XF_EMPTY_THIS_BUFFER __XF_OPCODE(1, 0, 8)
|
||||
|
||||
/* ...output buffer reception */
|
||||
#define XF_FILL_THIS_BUFFER __XF_OPCODE(0, 1, 9)
|
||||
|
||||
/* ...flush specific port */
|
||||
#define XF_FLUSH __XF_OPCODE(0, 0, 10)
|
||||
|
||||
/* ...start component operation */
|
||||
#define XF_START __XF_OPCODE(0, 0, 11)
|
||||
|
||||
/* ...stop component operation */
|
||||
#define XF_STOP __XF_OPCODE(0, 0, 12)
|
||||
|
||||
/* ...pause component operation */
|
||||
#define XF_PAUSE __XF_OPCODE(0, 0, 13)
|
||||
|
||||
/* ...resume component operation */
|
||||
#define XF_RESUME __XF_OPCODE(0, 0, 14)
|
||||
|
||||
/* ...resume component operation */
|
||||
#define XF_SUSPEND __XF_OPCODE(0, 0, 15)
|
||||
|
||||
/* ...load lib for component operation */
|
||||
#define XF_LOAD_LIB __XF_OPCODE(0, 0, 16)
|
||||
|
||||
/* ...unload lib for component operation */
|
||||
#define XF_UNLOAD_LIB __XF_OPCODE(0, 0, 17)
|
||||
|
||||
/* ...component output eos operation */
|
||||
#define XF_OUTPUT_EOS __XF_OPCODE(0, 0, 18)
|
||||
|
||||
/* ...total amount of supported decoder commands */
|
||||
#define __XF_OP_NUM 19
|
||||
|
||||
#endif
|
490
sound/soc/fsl/fsl_dsp_xaf_api.c
Normal file
490
sound/soc/fsl/fsl_dsp_xaf_api.c
Normal file
|
@ -0,0 +1,490 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
//
|
||||
// Xtensa Audio Framework API for communication with DSP
|
||||
//
|
||||
// Copyright (C) 2017 Cadence Design Systems, Inc.
|
||||
// Copyright 2018 NXP
|
||||
|
||||
#include "fsl_dsp.h"
|
||||
#include "fsl_dsp_xaf_api.h"
|
||||
|
||||
/* ...send a command message to component */
|
||||
int xf_command(struct xf_client *client, struct xf_handle *handle,
|
||||
u32 port, u32 opcode, void *buffer, u32 length)
|
||||
{
|
||||
struct xf_proxy *proxy = handle->proxy;
|
||||
struct xf_message msg;
|
||||
|
||||
/* ...fill-in message parameters */
|
||||
msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0),
|
||||
__XF_PORT_SPEC2(handle->id, port));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = opcode;
|
||||
msg.length = length;
|
||||
msg.buffer = buffer;
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...execute command synchronously */
|
||||
return xf_cmd_send(proxy, msg.id, msg.opcode, msg.buffer, msg.length);
|
||||
}
|
||||
|
||||
int xaf_comp_set_config(struct xf_client *client, struct xaf_comp *p_comp,
|
||||
u32 num_param, void *p_param)
|
||||
{
|
||||
struct xf_handle *p_handle;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
struct xf_set_param_msg *smsg;
|
||||
struct xf_set_param_msg *param = (struct xf_set_param_msg *)p_param;
|
||||
struct xf_proxy *proxy;
|
||||
u32 i;
|
||||
|
||||
p_handle = &p_comp->handle;
|
||||
proxy = p_handle->proxy;
|
||||
|
||||
/* ...set persistent stream characteristics */
|
||||
smsg = xf_buffer_data(p_handle->aux);
|
||||
|
||||
for (i = 0; i < num_param; i++) {
|
||||
smsg[i].id = param[i].id;
|
||||
smsg[i].mixData.value = param[i].mixData.value;
|
||||
}
|
||||
|
||||
/* ...set command parameters */
|
||||
msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0),
|
||||
__XF_PORT_SPEC2(p_handle->id, 0));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_SET_PARAM;
|
||||
msg.length = sizeof(*smsg) * num_param;
|
||||
msg.buffer = smsg;
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
|
||||
if(IS_ERR(rmsg))
|
||||
return PTR_ERR(rmsg);
|
||||
/* ...save received component global client-id */
|
||||
/* TODO: review cleanup */
|
||||
/* xf_msg_free(proxy, rmsg);
|
||||
* xf_unlock(&proxy->lock);
|
||||
*/
|
||||
|
||||
/* ...make sure response is expected */
|
||||
if ((rmsg->opcode != XF_SET_PARAM) || (rmsg->buffer != smsg)) {
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xaf_comp_get_config(struct xf_client *client, struct xaf_comp *p_comp,
|
||||
u32 num_param, void *p_param)
|
||||
{
|
||||
|
||||
struct xf_handle *p_handle;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
struct xf_get_param_msg *smsg;
|
||||
struct xf_get_param_msg *param = (struct xf_get_param_msg *)p_param;
|
||||
struct xf_proxy *proxy;
|
||||
u32 i;
|
||||
|
||||
p_handle = &p_comp->handle;
|
||||
proxy = p_handle->proxy;
|
||||
|
||||
/* ...set persistent stream characteristics */
|
||||
smsg = xf_buffer_data(p_handle->aux);
|
||||
|
||||
for (i = 0; i < num_param; i++)
|
||||
smsg[i].id = param[i].id;
|
||||
|
||||
|
||||
msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0),
|
||||
__XF_PORT_SPEC2(p_handle->id, 0));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_GET_PARAM;
|
||||
msg.length = sizeof(*smsg) * num_param;
|
||||
msg.buffer = smsg;
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
|
||||
/* ...save received component global client-id */
|
||||
if(IS_ERR(rmsg))
|
||||
return PTR_ERR(rmsg);
|
||||
|
||||
/* TODO: review cleanup */
|
||||
/* xf_msg_free(proxy, rmsg);
|
||||
* xf_unlock(&proxy->lock); */
|
||||
|
||||
/* ...make sure response is expected */
|
||||
if ((rmsg->opcode != (u32)XF_GET_PARAM) || (rmsg->buffer != smsg)) {
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_param; i++)
|
||||
param[i].mixData.value = smsg[i].mixData.value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xaf_comp_flush(struct xf_client *client, struct xaf_comp *p_comp)
|
||||
{
|
||||
|
||||
struct xf_handle *p_handle;
|
||||
struct xf_proxy *proxy;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
|
||||
p_handle = &p_comp->handle;
|
||||
proxy = p_handle->proxy;
|
||||
|
||||
msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0),
|
||||
__XF_PORT_SPEC2(p_handle->id, 0));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_FLUSH;
|
||||
msg.length = 0;
|
||||
msg.buffer = NULL;
|
||||
msg.ret = 0;
|
||||
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
|
||||
if(IS_ERR(rmsg))
|
||||
return PTR_ERR(rmsg);
|
||||
|
||||
/* ...make sure response is expected */
|
||||
if ((rmsg->opcode != (u32)XF_FLUSH) || rmsg->buffer) {
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xaf_comp_create(struct xf_client *client, struct xf_proxy *proxy,
|
||||
struct xaf_comp *p_comp, int comp_type)
|
||||
{
|
||||
struct fsl_dsp *dsp_priv = container_of(proxy, struct fsl_dsp, proxy);
|
||||
char lib_path[200];
|
||||
char lib_wrap_path[200];
|
||||
struct xf_handle *p_handle;
|
||||
struct xf_buffer *buf;
|
||||
int ret = 0;
|
||||
bool loadlib = true;
|
||||
|
||||
memset((void *)p_comp, 0, sizeof(struct xaf_comp));
|
||||
|
||||
strcpy(lib_path, "/usr/lib/imx-mm/audio-codec/dsp/");
|
||||
strcpy(lib_wrap_path, "/usr/lib/imx-mm/audio-codec/dsp/");
|
||||
|
||||
p_handle = &p_comp->handle;
|
||||
|
||||
p_comp->comp_type = comp_type;
|
||||
|
||||
if (comp_type == RENDER_ESAI)
|
||||
loadlib = false;
|
||||
|
||||
if (loadlib) {
|
||||
p_comp->codec_lib.filename = lib_path;
|
||||
p_comp->codec_wrap_lib.filename = lib_wrap_path;
|
||||
p_comp->codec_lib.lib_type = DSP_CODEC_LIB;
|
||||
}
|
||||
|
||||
switch (comp_type) {
|
||||
case CODEC_MP3_DEC:
|
||||
p_comp->dec_id = "audio-decoder/mp3";
|
||||
strcat(lib_path, "lib_dsp_mp3_dec.so");
|
||||
break;
|
||||
case CODEC_AAC_DEC:
|
||||
p_comp->dec_id = "audio-decoder/aac";
|
||||
strcat(lib_path, "lib_dsp_aac_dec.so");
|
||||
break;
|
||||
case RENDER_ESAI:
|
||||
p_comp->dec_id = "renderer/esai";
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* ...create decoder component instance (select core-0) */
|
||||
ret = xf_open(client, proxy, p_handle, p_comp->dec_id, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(dsp_priv->dev, "create (%s) component error: %d\n",
|
||||
p_comp->dec_id, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (loadlib) {
|
||||
strcat(lib_wrap_path, "lib_dsp_codec_wrap.so");
|
||||
p_comp->codec_wrap_lib.lib_type = DSP_CODEC_WRAP_LIB;
|
||||
|
||||
/* ...load codec wrapper lib */
|
||||
ret = xf_load_lib(client, p_handle, &p_comp->codec_wrap_lib);
|
||||
if (ret) {
|
||||
dev_err(dsp_priv->dev, "load codec wrap lib error\n");
|
||||
goto err_wrap_load;
|
||||
}
|
||||
|
||||
/* ...load codec lib */
|
||||
ret = xf_load_lib(client, p_handle, &p_comp->codec_lib);
|
||||
if (ret) {
|
||||
dev_err(dsp_priv->dev, "load codec lib error\n");
|
||||
goto err_codec_load;
|
||||
}
|
||||
|
||||
/* ...allocate input buffer */
|
||||
ret = xf_pool_alloc(client, proxy, 1, INBUF_SIZE,
|
||||
XF_POOL_INPUT, &p_comp->inpool);
|
||||
if (ret) {
|
||||
dev_err(dsp_priv->dev, "alloc input buf error\n");
|
||||
goto err_pool_alloc;
|
||||
}
|
||||
|
||||
/* ...initialize input buffer pointer */
|
||||
buf = xf_buffer_get(p_comp->inpool);
|
||||
p_comp->inptr = xf_buffer_data(buf);
|
||||
}
|
||||
|
||||
p_comp->active = true;
|
||||
|
||||
return ret;
|
||||
|
||||
err_pool_alloc:
|
||||
xf_unload_lib(client, p_handle, &p_comp->codec_lib);
|
||||
err_codec_load:
|
||||
xf_unload_lib(client, p_handle, &p_comp->codec_wrap_lib);
|
||||
err_wrap_load:
|
||||
xf_close(client, p_handle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xaf_comp_delete(struct xf_client *client, struct xaf_comp *p_comp)
|
||||
{
|
||||
|
||||
struct xf_handle *p_handle;
|
||||
bool loadlib = true;
|
||||
u32 ret = 0;
|
||||
|
||||
if (!p_comp->active)
|
||||
return ret;
|
||||
|
||||
/* mark component as unusable from this point */
|
||||
p_comp->active = false;
|
||||
|
||||
if (p_comp->comp_type == RENDER_ESAI)
|
||||
loadlib = false;
|
||||
|
||||
p_handle = &p_comp->handle;
|
||||
|
||||
if (loadlib) {
|
||||
/* ...unload codec wrapper library */
|
||||
xf_unload_lib(client, p_handle, &p_comp->codec_wrap_lib);
|
||||
|
||||
/* ...unload codec library */
|
||||
xf_unload_lib(client, p_handle, &p_comp->codec_lib);
|
||||
|
||||
xf_pool_free(client, p_comp->inpool);
|
||||
}
|
||||
|
||||
/* ...delete component */
|
||||
xf_close(client, p_handle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xaf_comp_process(struct xf_client *client, struct xaf_comp *p_comp, void *p_buf, u32 length, u32 flag)
|
||||
{
|
||||
struct xf_handle *p_handle;
|
||||
u32 ret = 0;
|
||||
|
||||
p_handle = &p_comp->handle;
|
||||
|
||||
switch (flag) {
|
||||
case XF_FILL_THIS_BUFFER:
|
||||
/* ...send message to component output port (port-id=1) */
|
||||
ret = xf_command(client, p_handle, 1, XF_FILL_THIS_BUFFER,
|
||||
p_buf, length);
|
||||
break;
|
||||
case XF_EMPTY_THIS_BUFFER:
|
||||
/* ...send message to component input port (port-id=0) */
|
||||
ret = xf_command(client, p_handle, 0, XF_EMPTY_THIS_BUFFER,
|
||||
p_buf, length);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ...port binding function */
|
||||
int xf_route(struct xf_client *client, struct xf_handle *src, u32 src_port,
|
||||
struct xf_handle *dst, u32 dst_port, u32 num, u32 size, u32 align)
|
||||
{
|
||||
struct xf_proxy *proxy = src->proxy;
|
||||
struct xf_buffer *b;
|
||||
struct xf_route_port_msg *m;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
|
||||
/* ...sanity checks - proxy pointers are same */
|
||||
if (proxy != dst->proxy)
|
||||
return -EINVAL;
|
||||
|
||||
/* ...buffer data is sane */
|
||||
if (!(num && size && xf_is_power_of_two(align)))
|
||||
return -EINVAL;
|
||||
|
||||
/* ...get control buffer */
|
||||
if ((b = xf_buffer_get(proxy->aux)) == NULL)
|
||||
return -EBUSY;
|
||||
|
||||
/* ...get message buffer */
|
||||
m = xf_buffer_data(b);
|
||||
|
||||
/* ...fill-in message parameters */
|
||||
m->src = __XF_PORT_SPEC2(src->id, src_port);
|
||||
m->dst = __XF_PORT_SPEC2(dst->id, dst_port);
|
||||
m->alloc_number = num;
|
||||
m->alloc_size = size;
|
||||
m->alloc_align = align;
|
||||
|
||||
/* ...set command parameters */
|
||||
msg.id = __XF_MSG_ID(__XF_AP_PROXY(0),
|
||||
__XF_PORT_SPEC2(src->id, src_port));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_ROUTE;
|
||||
msg.length = sizeof(*m);
|
||||
msg.buffer = m;
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
if(IS_ERR(rmsg))
|
||||
return PTR_ERR(rmsg);
|
||||
|
||||
/* ...save received component global client-id */
|
||||
/* TODO: review cleanup */
|
||||
/* xf_msg_free(proxy, rmsg);
|
||||
* xf_unlock(&proxy->lock); */
|
||||
|
||||
|
||||
/* ...synchronously execute command on remote DSP */
|
||||
/* XF_CHK_API(xf_proxy_cmd_exec(proxy, &msg)); */
|
||||
|
||||
/* ...return buffer to proxy */
|
||||
xf_buffer_put(b);
|
||||
|
||||
/* ...check result is successful */
|
||||
/* XF_CHK_ERR(msg.opcode == XF_ROUTE, -ENOMEM); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ...port unbinding function */
|
||||
int xf_unroute(struct xf_client *client, struct xf_handle *src, u32 src_port)
|
||||
{
|
||||
struct xf_proxy *proxy = src->proxy;
|
||||
struct xf_buffer *b;
|
||||
struct xf_unroute_port_msg *m;
|
||||
struct xf_message msg;
|
||||
struct xf_message *rmsg;
|
||||
int r = 0;
|
||||
|
||||
/* ...get control buffer */
|
||||
if((b = xf_buffer_get(proxy->aux)) == NULL)
|
||||
return -EBUSY;
|
||||
|
||||
/* ...get message buffer */
|
||||
m = xf_buffer_data(b);
|
||||
|
||||
/* ...fill-in message parameters */
|
||||
m->src = __XF_PORT_SPEC2(src->id, src_port);
|
||||
|
||||
/* ...set command parameters */
|
||||
msg.id = __XF_MSG_ID(__XF_AP_PROXY(0),
|
||||
__XF_PORT_SPEC2(src->id, src_port));
|
||||
msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
|
||||
msg.opcode = XF_UNROUTE;
|
||||
msg.length = sizeof(*m);
|
||||
msg.buffer = m;
|
||||
msg.ret = 0;
|
||||
|
||||
/* ...execute command synchronously */
|
||||
rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
|
||||
msg.buffer, msg.length, &client->work,
|
||||
&client->compr_complete);
|
||||
if (IS_ERR(rmsg))
|
||||
return PTR_ERR(rmsg);
|
||||
/* ...save received component global client-id */
|
||||
|
||||
/*TODO: review cleanup */
|
||||
/* xf_msg_free(proxy, rmsg); */
|
||||
/* xf_unlock(&proxy->lock); */
|
||||
|
||||
/* ...return buffer to proxy */
|
||||
xf_buffer_put(b);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int xaf_connect(struct xf_client *client,
|
||||
struct xaf_comp *p_src,
|
||||
struct xaf_comp *p_dest,
|
||||
u32 num_buf,
|
||||
u32 buf_length)
|
||||
{
|
||||
/* ...connect p_src output port with p_dest input port */
|
||||
return xf_route(client, &p_src->handle, 0, &p_dest->handle, 0,
|
||||
num_buf, buf_length, 8);
|
||||
}
|
||||
|
||||
int xaf_disconnect(struct xf_client *client, struct xaf_comp *p_comp)
|
||||
{
|
||||
/* ...disconnect p_src output port with p_dest input port */
|
||||
return xf_unroute(client, &p_comp->handle, 0);
|
||||
|
||||
}
|
||||
|
||||
int xaf_comp_add(struct xaf_pipeline *p_pipe, struct xaf_comp *p_comp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
p_comp->next = p_pipe->comp_chain;
|
||||
p_comp->pipeline = p_pipe;
|
||||
p_pipe->comp_chain = p_comp;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xaf_pipeline_create(struct xaf_pipeline *p_pipe)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
memset(p_pipe, 0, sizeof(struct xaf_pipeline));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xaf_pipeline_delete(struct xaf_pipeline *p_pipe)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
memset(p_pipe, 0, sizeof(struct xaf_pipeline));
|
||||
|
||||
return ret;
|
||||
}
|
160
sound/soc/fsl/fsl_dsp_xaf_api.h
Normal file
160
sound/soc/fsl/fsl_dsp_xaf_api.h
Normal file
|
@ -0,0 +1,160 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT)*/
|
||||
/*
|
||||
* Xtensa Audio Framework API for communication with DSP
|
||||
*
|
||||
* Copyright (C) 2017 Cadence Design Systems, Inc.
|
||||
* Copyright 2018 NXP
|
||||
*/
|
||||
#ifndef FSL_DSP_XAF_API_H
|
||||
#define FSL_DSP_XAF_API_H
|
||||
|
||||
#include "fsl_dsp_library_load.h"
|
||||
|
||||
/* ...size of auxiliary pool for communication with DSP */
|
||||
#define XA_AUX_POOL_SIZE 32
|
||||
|
||||
/* ...length of auxiliary pool messages */
|
||||
#define XA_AUX_POOL_MSG_LENGTH 128
|
||||
|
||||
/* ...number of max input buffers */
|
||||
#define INBUF_SIZE 4096
|
||||
#define OUTBUF_SIZE 16384
|
||||
|
||||
struct xaf_pipeline;
|
||||
|
||||
struct xaf_info_s {
|
||||
u32 opcode;
|
||||
void *buf;
|
||||
u32 length;
|
||||
u32 ret;
|
||||
};
|
||||
|
||||
struct xaf_comp {
|
||||
struct xaf_comp *next;
|
||||
|
||||
struct xaf_pipeline *pipeline;
|
||||
struct xf_handle handle;
|
||||
|
||||
const char *dec_id;
|
||||
int comp_type;
|
||||
|
||||
struct xf_pool *inpool;
|
||||
struct xf_pool *outpool;
|
||||
void *inptr;
|
||||
void *outptr;
|
||||
|
||||
struct lib_info codec_lib;
|
||||
struct lib_info codec_wrap_lib;
|
||||
|
||||
int active; /* component fully initialized */
|
||||
};
|
||||
|
||||
struct xaf_pipeline {
|
||||
struct xaf_comp *comp_chain;
|
||||
|
||||
u32 input_eos;
|
||||
u32 output_eos;
|
||||
};
|
||||
|
||||
int xaf_comp_create(struct xf_client *client, struct xf_proxy *p_proxy,
|
||||
struct xaf_comp *p_comp, int comp_type);
|
||||
int xaf_comp_delete(struct xf_client *client, struct xaf_comp *p_comp);
|
||||
int xaf_comp_flush(struct xf_client *client, struct xaf_comp *p_comp);
|
||||
|
||||
int xaf_comp_set_config(struct xf_client *client,struct xaf_comp *p_comp,
|
||||
u32 num_param, void *p_param);
|
||||
int xaf_comp_get_config(struct xf_client *client,struct xaf_comp *p_comp,
|
||||
u32 num_param, void *p_param);
|
||||
|
||||
int xaf_comp_add(struct xaf_pipeline *p_pipe, struct xaf_comp *p_comp);
|
||||
int xaf_comp_process(struct xf_client *client, struct xaf_comp *p_comp,
|
||||
void *p_buf, u32 length, u32 flag);
|
||||
int xaf_comp_get_status(struct xaf_comp *p_comp, struct xaf_info_s *p_info);
|
||||
int xaf_comp_get_msg_count(struct xaf_comp *p_comp);
|
||||
|
||||
int xaf_connect(struct xf_client *client,struct xaf_comp *p_src,
|
||||
struct xaf_comp *p_dest, u32 num_buf, u32 buf_length);
|
||||
int xaf_disconnect(struct xf_client *client,struct xaf_comp *p_comp);
|
||||
|
||||
int xaf_pipeline_create(struct xaf_pipeline *p_pipe);
|
||||
int xaf_pipeline_delete(struct xaf_pipeline *p_pipe);
|
||||
|
||||
int xaf_pipeline_send_eos(struct xaf_pipeline *p_pipe);
|
||||
|
||||
/* ...port routing command */
|
||||
struct __attribute__((__packed__)) xf_route_port_msg {
|
||||
/* ...source port specification */
|
||||
u32 src;
|
||||
/* ...destination port specification */
|
||||
u32 dst;
|
||||
/* ...number of buffers to allocate */
|
||||
u32 alloc_number;
|
||||
/* ...length of buffer to allocate */
|
||||
u32 alloc_size;
|
||||
/* ...alignment restriction for a buffer */
|
||||
u32 alloc_align;
|
||||
};
|
||||
|
||||
/* ...port unrouting command */
|
||||
struct __attribute__((__packed__)) xf_unroute_port_msg {
|
||||
/* ...source port specification */
|
||||
u32 src;
|
||||
/* ...destination port specification */
|
||||
u32 dst;
|
||||
};
|
||||
|
||||
/* ...check if non-zero value is a power-of-two */
|
||||
#define xf_is_power_of_two(v) (((v) & ((v) - 1)) == 0)
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* bascial message
|
||||
******************************************************************************/
|
||||
typedef union DATA {
|
||||
u32 value;
|
||||
|
||||
struct {
|
||||
u32 size;
|
||||
u32 channel_table[10];
|
||||
} chan_map_tab;
|
||||
|
||||
struct {
|
||||
u32 samplerate;
|
||||
u32 width;
|
||||
u32 depth;
|
||||
u32 channels;
|
||||
u32 endian;
|
||||
u32 interleave;
|
||||
u32 layout[12];
|
||||
u32 chan_pos_set; // indicate if channel position is set outside or use codec default
|
||||
} outputFormat;
|
||||
} data_t;
|
||||
|
||||
/* ...component initialization parameter */
|
||||
struct __attribute__((__packed__)) xf_set_param_msg {
|
||||
/* ...index of parameter passed to SET_CONFIG_PARAM call */
|
||||
u32 id;
|
||||
/* ...value of parameter */
|
||||
data_t mixData;
|
||||
};
|
||||
|
||||
/* ...message body (command/response) */
|
||||
struct __attribute__((__packed__)) xf_get_param_msg {
|
||||
/* ...array of parameters requested */
|
||||
u32 id;
|
||||
/* ...array of parameters values */
|
||||
data_t mixData;
|
||||
};
|
||||
|
||||
/* ...renderer-specific configuration parameters */
|
||||
enum xa_config_param_renderer {
|
||||
XA_RENDERER_CONFIG_PARAM_CB = 0,
|
||||
XA_RENDERER_CONFIG_PARAM_STATE = 1,
|
||||
XA_RENDERER_CONFIG_PARAM_PCM_WIDTH = 2,
|
||||
XA_RENDERER_CONFIG_PARAM_CHANNELS = 3,
|
||||
XA_RENDERER_CONFIG_PARAM_SAMPLE_RATE = 4,
|
||||
XA_RENDERER_CONFIG_PARAM_FRAME_SIZE = 5,
|
||||
XA_RENDERER_CONFIG_PARAM_NUM = 6,
|
||||
};
|
||||
|
||||
#endif /* FSL_DSP_XAF_API_H */
|
2533
sound/soc/fsl/fsl_easrc.c
Normal file
2533
sound/soc/fsl/fsl_easrc.c
Normal file
File diff suppressed because it is too large
Load Diff
698
sound/soc/fsl/fsl_easrc.h
Normal file
698
sound/soc/fsl/fsl_easrc.h
Normal file
|
@ -0,0 +1,698 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2019 NXP
|
||||
*/
|
||||
|
||||
#ifndef _FSL_EASRC_H
|
||||
#define _FSL_EASRC_H
|
||||
|
||||
#include <sound/asound.h>
|
||||
#include <uapi/linux/mxc_asrc.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/platform_data/dma-imx.h>
|
||||
|
||||
/* EASRC Register Map */
|
||||
|
||||
/* ASRC Input Write FIFO */
|
||||
#define REG_EASRC_WRFIFO(ctx) (0x000 + 4 * (ctx))
|
||||
/* ASRC Output Read FIFO */
|
||||
#define REG_EASRC_RDFIFO(ctx) (0x010 + 4 * (ctx))
|
||||
/* ASRC Context Control */
|
||||
#define REG_EASRC_CC(ctx) (0x020 + 4 * (ctx))
|
||||
/* ASRC Context Control Extended 1 */
|
||||
#define REG_EASRC_CCE1(ctx) (0x030 + 4 * (ctx))
|
||||
/* ASRC Context Control Extended 2 */
|
||||
#define REG_EASRC_CCE2(ctx) (0x040 + 4 * (ctx))
|
||||
/* ASRC Control Input Access */
|
||||
#define REG_EASRC_CIA(ctx) (0x050 + 4 * (ctx))
|
||||
/* ASRC Datapath Processor Control Slot0 */
|
||||
#define REG_EASRC_DPCS0R0(ctx) (0x060 + 4 * (ctx))
|
||||
#define REG_EASRC_DPCS0R1(ctx) (0x070 + 4 * (ctx))
|
||||
#define REG_EASRC_DPCS0R2(ctx) (0x080 + 4 * (ctx))
|
||||
#define REG_EASRC_DPCS0R3(ctx) (0x090 + 4 * (ctx))
|
||||
/* ASRC Datapath Processor Control Slot1 */
|
||||
#define REG_EASRC_DPCS1R0(ctx) (0x0A0 + 4 * (ctx))
|
||||
#define REG_EASRC_DPCS1R1(ctx) (0x0B0 + 4 * (ctx))
|
||||
#define REG_EASRC_DPCS1R2(ctx) (0x0C0 + 4 * (ctx))
|
||||
#define REG_EASRC_DPCS1R3(ctx) (0x0D0 + 4 * (ctx))
|
||||
/* ASRC Context Output Control */
|
||||
#define REG_EASRC_COC(ctx) (0x0E0 + 4 * (ctx))
|
||||
/* ASRC Control Output Access */
|
||||
#define REG_EASRC_COA(ctx) (0x0F0 + 4 * (ctx))
|
||||
/* ASRC Sample FIFO Status */
|
||||
#define REG_EASRC_SFS(ctx) (0x100 + 4 * (ctx))
|
||||
/* ASRC Resampling Ratio Low */
|
||||
#define REG_EASRC_RRL(ctx) (0x110 + 8 * (ctx))
|
||||
/* ASRC Resampling Ratio High */
|
||||
#define REG_EASRC_RRH(ctx) (0x114 + 8 * (ctx))
|
||||
/* ASRC Resampling Ratio Update Control */
|
||||
#define REG_EASRC_RUC(ctx) (0x130 + 4 * (ctx))
|
||||
/* ASRC Resampling Ratio Update Rate */
|
||||
#define REG_EASRC_RUR(ctx) (0x140 + 4 * (ctx))
|
||||
/* ASRC Resampling Center Tap Coefficient Low */
|
||||
#define REG_EASRC_RCTCL (0x150)
|
||||
/* ASRC Resampling Center Tap Coefficient High */
|
||||
#define REG_EASRC_RCTCH (0x154)
|
||||
/* ASRC Prefilter Coefficient FIFO */
|
||||
#define REG_EASRC_PCF(ctx) (0x160 + 4 * (ctx))
|
||||
/* ASRC Context Resampling Coefficient Memory */
|
||||
#define REG_EASRC_CRCM 0x170
|
||||
/* ASRC Context Resampling Coefficient Control*/
|
||||
#define REG_EASRC_CRCC 0x174
|
||||
/* ASRC Interrupt Control */
|
||||
#define REG_EASRC_IRQC 0x178
|
||||
/* ASRC Interrupt Status Flags */
|
||||
#define REG_EASRC_IRQF 0x17C
|
||||
/* ASRC Channel Status 0 */
|
||||
#define REG_EASRC_CS0(ctx) (0x180 + 4 * (ctx))
|
||||
/* ASRC Channel Status 1 */
|
||||
#define REG_EASRC_CS1(ctx) (0x190 + 4 * (ctx))
|
||||
/* ASRC Channel Status 2 */
|
||||
#define REG_EASRC_CS2(ctx) (0x1A0 + 4 * (ctx))
|
||||
/* ASRC Channel Status 3 */
|
||||
#define REG_EASRC_CS3(ctx) (0x1B0 + 4 * (ctx))
|
||||
/* ASRC Channel Status 4 */
|
||||
#define REG_EASRC_CS4(ctx) (0x1C0 + 4 * (ctx))
|
||||
/* ASRC Channel Status 5 */
|
||||
#define REG_EASRC_CS5(ctx) (0x1D0 + 4 * (ctx))
|
||||
/* ASRC Debug Control Register */
|
||||
#define REG_EASRC_DBGC 0x1E0
|
||||
/* ASRC Debug Status Register */
|
||||
#define REG_EASRC_DBGS 0x1E4
|
||||
|
||||
#define REG_EASRC_FIFO(x, ctx) (x == IN ? REG_EASRC_WRFIFO(ctx) \
|
||||
: REG_EASRC_RDFIFO(ctx))
|
||||
|
||||
/* ASRC Context Control (CC) */
|
||||
#define EASRC_CC_EN_SHIFT 31
|
||||
#define EASRC_CC_EN_MASK BIT(EASRC_CC_EN_SHIFT)
|
||||
#define EASRC_CC_EN BIT(EASRC_CC_EN_SHIFT)
|
||||
#define EASRC_CC_STOP_SHIFT 29
|
||||
#define EASRC_CC_STOP_MASK BIT(EASRC_CC_STOP_SHIFT)
|
||||
#define EASRC_CC_STOP BIT(EASRC_CC_STOP_SHIFT)
|
||||
#define EASRC_CC_FWMDE_SHIFT 28
|
||||
#define EASRC_CC_FWMDE_MASK BIT(EASRC_CC_FWMDE_SHIFT)
|
||||
#define EASRC_CC_FWMDE BIT(EASRC_CC_FWMDE_SHIFT)
|
||||
#define EASRC_CC_FIFO_WTMK_SHIFT 16
|
||||
#define EASRC_CC_FIFO_WTMK_WIDTH 7
|
||||
#define EASRC_CC_FIFO_WTMK_MASK ((BIT(EASRC_CC_FIFO_WTMK_WIDTH) - 1) \
|
||||
<< EASRC_CC_FIFO_WTMK_SHIFT)
|
||||
#define EASRC_CC_FIFO_WTMK(v) (((v) << EASRC_CC_FIFO_WTMK_SHIFT) \
|
||||
& EASRC_CC_FIFO_WTMK_MASK)
|
||||
#define EASRC_CC_SAMPLE_POS_SHIFT 11
|
||||
#define EASRC_CC_SAMPLE_POS_WIDTH 5
|
||||
#define EASRC_CC_SAMPLE_POS_MASK ((BIT(EASRC_CC_SAMPLE_POS_WIDTH) - 1) \
|
||||
<< EASRC_CC_SAMPLE_POS_SHIFT)
|
||||
#define EASRC_CC_SAMPLE_POS(v) (((v) << EASRC_CC_SAMPLE_POS_SHIFT) \
|
||||
& EASRC_CC_SAMPLE_POS_MASK)
|
||||
#define EASRC_CC_ENDIANNESS_SHIFT 10
|
||||
#define EASRC_CC_ENDIANNESS_MASK BIT(EASRC_CC_ENDIANNESS_SHIFT)
|
||||
#define EASRC_CC_ENDIANNESS BIT(EASRC_CC_ENDIANNESS_SHIFT)
|
||||
#define EASRC_CC_BPS_SHIFT 8
|
||||
#define EASRC_CC_BPS_WIDTH 2
|
||||
#define EASRC_CC_BPS_MASK ((BIT(EASRC_CC_BPS_WIDTH) - 1) \
|
||||
<< EASRC_CC_BPS_SHIFT)
|
||||
#define EASRC_CC_BPS(v) (((v) << EASRC_CC_BPS_SHIFT) \
|
||||
& EASRC_CC_BPS_MASK)
|
||||
#define EASRC_CC_FMT_SHIFT 7
|
||||
#define EASRC_CC_FMT_MASK BIT(EASRC_CC_FMT_SHIFT)
|
||||
#define EASRC_CC_FMT BIT(EASRC_CC_FMT_SHIFT)
|
||||
#define EASRC_CC_INSIGN_SHIFT 6
|
||||
#define EASRC_CC_INSIGN_MASK BIT(EASRC_CC_INSIGN_SHIFT)
|
||||
#define EASRC_CC_INSIGN BIT(EASRC_CC_INSIGN_SHIFT)
|
||||
#define EASRC_CC_CHEN_SHIFT 0
|
||||
#define EASRC_CC_CHEN_WIDTH 5
|
||||
#define EASRC_CC_CHEN_MASK ((BIT(EASRC_CC_CHEN_WIDTH) - 1) \
|
||||
<< EASRC_CC_CHEN_SHIFT)
|
||||
#define EASRC_CC_CHEN(v) (((v) << EASRC_CC_CHEN_SHIFT) \
|
||||
& EASRC_CC_CHEN_MASK)
|
||||
|
||||
/* ASRC Context Control Extended 1 (CCE1) */
|
||||
#define EASRC_CCE1_COEF_WS_SHIFT 25
|
||||
#define EASRC_CCE1_COEF_WS_MASK BIT(EASRC_CCE1_COEF_WS_SHIFT)
|
||||
#define EASRC_CCE1_COEF_WS BIT(EASRC_CCE1_COEF_WS_SHIFT)
|
||||
#define EASRC_CCE1_COEF_MEM_RST_SHIFT 24
|
||||
#define EASRC_CCE1_COEF_MEM_RST_MASK BIT(EASRC_CCE1_COEF_MEM_RST_SHIFT)
|
||||
#define EASRC_CCE1_COEF_MEM_RST BIT(EASRC_CCE1_COEF_MEM_RST_SHIFT)
|
||||
#define EASRC_CCE1_PF_EXP_SHIFT 16
|
||||
#define EASRC_CCE1_PF_EXP_WIDTH 8
|
||||
#define EASRC_CCE1_PF_EXP_MASK ((BIT(EASRC_CCE1_PF_EXP_WIDTH) - 1) \
|
||||
<< EASRC_CCE1_PF_EXP_SHIFT)
|
||||
#define EASRC_CCE1_PF_EXP(v) (((v) << EASRC_CCE1_PF_EXP_SHIFT) \
|
||||
& EASRC_CCE1_PF_EXP_MASK)
|
||||
#define EASRC_CCE1_PF_ST1_WBFP_SHIFT 9
|
||||
#define EASRC_CCE1_PF_ST1_WBFP_MASK BIT(EASRC_CCE1_PF_ST1_WBFP_SHIFT)
|
||||
#define EASRC_CCE1_PF_ST1_WBFP BIT(EASRC_CCE1_PF_ST1_WBFP_SHIFT)
|
||||
#define EASRC_CCE1_PF_TSEN_SHIFT 8
|
||||
#define EASRC_CCE1_PF_TSEN_MASK BIT(EASRC_CCE1_PF_TSEN_SHIFT)
|
||||
#define EASRC_CCE1_PF_TSEN BIT(EASRC_CCE1_PF_TSEN_SHIFT)
|
||||
#define EASRC_CCE1_RS_BYPASS_SHIFT 7
|
||||
#define EASRC_CCE1_RS_BYPASS_MASK BIT(EASRC_CCE1_RS_BYPASS_SHIFT)
|
||||
#define EASRC_CCE1_RS_BYPASS BIT(EASRC_CCE1_RS_BYPASS_SHIFT)
|
||||
#define EASRC_CCE1_PF_BYPASS_SHIFT 6
|
||||
#define EASRC_CCE1_PF_BYPASS_MASK BIT(EASRC_CCE1_PF_BYPASS_SHIFT)
|
||||
#define EASRC_CCE1_PF_BYPASS BIT(EASRC_CCE1_PF_BYPASS_SHIFT)
|
||||
#define EASRC_CCE1_RS_STOP_SHIFT 5
|
||||
#define EASRC_CCE1_RS_STOP_MASK BIT(EASRC_CCE1_RS_STOP_SHIFT)
|
||||
#define EASRC_CCE1_RS_STOP BIT(EASRC_CCE1_RS_STOP_SHIFT)
|
||||
#define EASRC_CCE1_PF_STOP_SHIFT 4
|
||||
#define EASRC_CCE1_PF_STOP_MASK BIT(EASRC_CCE1_PF_STOP_SHIFT)
|
||||
#define EASRC_CCE1_PF_STOP BIT(EASRC_CCE1_PF_STOP_SHIFT)
|
||||
#define EASRC_CCE1_RS_INIT_SHIFT 2
|
||||
#define EASRC_CCE1_RS_INIT_WIDTH 2
|
||||
#define EASRC_CCE1_RS_INIT_MASK ((BIT(EASRC_CCE1_RS_INIT_WIDTH) - 1) \
|
||||
<< EASRC_CCE1_RS_INIT_SHIFT)
|
||||
#define EASRC_CCE1_RS_INIT(v) (((v) << EASRC_CCE1_RS_INIT_SHIFT) \
|
||||
& EASRC_CCE1_RS_INIT_MASK)
|
||||
#define EASRC_CCE1_PF_INIT_SHIFT 0
|
||||
#define EASRC_CCE1_PF_INIT_WIDTH 2
|
||||
#define EASRC_CCE1_PF_INIT_MASK ((BIT(EASRC_CCE1_PF_INIT_WIDTH) - 1) \
|
||||
<< EASRC_CCE1_PF_INIT_SHIFT)
|
||||
#define EASRC_CCE1_PF_INIT(v) (((v) << EASRC_CCE1_PF_INIT_SHIFT) \
|
||||
& EASRC_CCE1_PF_INIT_MASK)
|
||||
|
||||
/* ASRC Context Control Extended 2 (CCE2) */
|
||||
#define EASRC_CCE2_ST2_TAPS_SHIFT 16
|
||||
#define EASRC_CCE2_ST2_TAPS_WIDTH 9
|
||||
#define EASRC_CCE2_ST2_TAPS_MASK ((BIT(EASRC_CCE2_ST2_TAPS_WIDTH) - 1) \
|
||||
<< EASRC_CCE2_ST2_TAPS_SHIFT)
|
||||
#define EASRC_CCE2_ST2_TAPS(v) (((v) << EASRC_CCE2_ST2_TAPS_SHIFT) \
|
||||
& EASRC_CCE2_ST2_TAPS_MASK)
|
||||
#define EASRC_CCE2_ST1_TAPS_SHIFT 0
|
||||
#define EASRC_CCE2_ST1_TAPS_WIDTH 9
|
||||
#define EASRC_CCE2_ST1_TAPS_MASK ((BIT(EASRC_CCE2_ST1_TAPS_WIDTH) - 1) \
|
||||
<< EASRC_CCE2_ST1_TAPS_SHIFT)
|
||||
#define EASRC_CCE2_ST1_TAPS(v) (((v) << EASRC_CCE2_ST1_TAPS_SHIFT) \
|
||||
& EASRC_CCE2_ST1_TAPS_MASK)
|
||||
|
||||
/* ASRC Control Input Access (CIA) */
|
||||
#define EASRC_CIA_ITER_SHIFT 16
|
||||
#define EASRC_CIA_ITER_WIDTH 6
|
||||
#define EASRC_CIA_ITER_MASK ((BIT(EASRC_CIA_ITER_WIDTH) - 1) \
|
||||
<< EASRC_CIA_ITER_SHIFT)
|
||||
#define EASRC_CIA_ITER(v) (((v) << EASRC_CIA_ITER_SHIFT) \
|
||||
& EASRC_CIA_ITER_MASK)
|
||||
#define EASRC_CIA_GRLEN_SHIFT 8
|
||||
#define EASRC_CIA_GRLEN_WIDTH 6
|
||||
#define EASRC_CIA_GRLEN_MASK ((BIT(EASRC_CIA_GRLEN_WIDTH) - 1) \
|
||||
<< EASRC_CIA_GRLEN_SHIFT)
|
||||
#define EASRC_CIA_GRLEN(v) (((v) << EASRC_CIA_GRLEN_SHIFT) \
|
||||
& EASRC_CIA_GRLEN_MASK)
|
||||
#define EASRC_CIA_ACCLEN_SHIFT 0
|
||||
#define EASRC_CIA_ACCLEN_WIDTH 6
|
||||
#define EASRC_CIA_ACCLEN_MASK ((BIT(EASRC_CIA_ACCLEN_WIDTH) - 1) \
|
||||
<< EASRC_CIA_ACCLEN_SHIFT)
|
||||
#define EASRC_CIA_ACCLEN(v) (((v) << EASRC_CIA_ACCLEN_SHIFT) \
|
||||
& EASRC_CIA_ACCLEN_MASK)
|
||||
|
||||
/* ASRC Datapath Processor Control Slot0 Register0 (DPCS0R0) */
|
||||
#define EASRC_DPCS0R0_MAXCH_SHIFT 24
|
||||
#define EASRC_DPCS0R0_MAXCH_WIDTH 5
|
||||
#define EASRC_DPCS0R0_MAXCH_MASK ((BIT(EASRC_DPCS0R0_MAXCH_WIDTH) - 1) \
|
||||
<< EASRC_DPCS0R0_MAXCH_SHIFT)
|
||||
#define EASRC_DPCS0R0_MAXCH(v) (((v) << EASRC_DPCS0R0_MAXCH_SHIFT) \
|
||||
& EASRC_DPCS0R0_MAXCH_MASK)
|
||||
#define EASRC_DPCS0R0_MINCH_SHIFT 16
|
||||
#define EASRC_DPCS0R0_MINCH_WIDTH 5
|
||||
#define EASRC_DPCS0R0_MINCH_MASK ((BIT(EASRC_DPCS0R0_MINCH_WIDTH) - 1) \
|
||||
<< EASRC_DPCS0R0_MINCH_SHIFT)
|
||||
#define EASRC_DPCS0R0_MINCH(v) (((v) << EASRC_DPCS0R0_MINCH_SHIFT) \
|
||||
& EASRC_DPCS0R0_MINCH_MASK)
|
||||
#define EASRC_DPCS0R0_NUMCH_SHIFT 8
|
||||
#define EASRC_DPCS0R0_NUMCH_WIDTH 5
|
||||
#define EASRC_DPCS0R0_NUMCH_MASK ((BIT(EASRC_DPCS0R0_NUMCH_WIDTH) - 1) \
|
||||
<< EASRC_DPCS0R0_NUMCH_SHIFT)
|
||||
#define EASRC_DPCS0R0_NUMCH(v) (((v) << EASRC_DPCS0R0_NUMCH_SHIFT) \
|
||||
& EASRC_DPCS0R0_NUMCH_MASK)
|
||||
#define EASRC_DPCS0R0_CTXNUM_SHIFT 1
|
||||
#define EASRC_DPCS0R0_CTXNUM_WIDTH 2
|
||||
#define EASRC_DPCS0R0_CTXNUM_MASK ((BIT(EASRC_DPCS0R0_CTXNUM_WIDTH) - 1) \
|
||||
<< EASRC_DPCS0R0_CTXNUM_SHIFT)
|
||||
#define EASRC_DPCS0R0_CTXNUM(v) (((v) << EASRC_DPCS0R0_CTXNUM_SHIFT) \
|
||||
& EASRC_DPCS0R0_CTXNUM_MASK)
|
||||
#define EASRC_DPCS0R0_EN_SHIFT 0
|
||||
#define EASRC_DPCS0R0_EN_MASK BIT(EASRC_DPCS0R0_EN_SHIFT)
|
||||
#define EASRC_DPCS0R0_EN BIT(EASRC_DPCS0R0_EN_SHIFT)
|
||||
|
||||
/* ASRC Datapath Processor Control Slot0 Register1 (DPCS0R1) */
|
||||
#define EASRC_DPCS0R1_ST1_EXP_SHIFT 0
|
||||
#define EASRC_DPCS0R1_ST1_EXP_WIDTH 13
|
||||
#define EASRC_DPCS0R1_ST1_EXP_MASK ((BIT(EASRC_DPCS0R1_ST1_EXP_WIDTH) - 1) \
|
||||
<< EASRC_DPCS0R1_ST1_EXP_SHIFT)
|
||||
#define EASRC_DPCS0R1_ST1_EXP(v) (((v) << EASRC_DPCS0R1_ST1_EXP_SHIFT) \
|
||||
& EASRC_DPCS0R1_ST1_EXP_MASK)
|
||||
|
||||
/* ASRC Datapath Processor Control Slot0 Register2 (DPCS0R2) */
|
||||
#define EASRC_DPCS0R2_ST1_MA_SHIFT 16
|
||||
#define EASRC_DPCS0R2_ST1_MA_WIDTH 13
|
||||
#define EASRC_DPCS0R2_ST1_MA_MASK ((BIT(EASRC_DPCS0R2_ST1_MA_WIDTH) - 1) \
|
||||
<< EASRC_DPCS0R2_ST1_MA_SHIFT)
|
||||
#define EASRC_DPCS0R2_ST1_MA(v) (((v) << EASRC_DPCS0R2_ST1_MA_SHIFT) \
|
||||
& EASRC_DPCS0R2_ST1_MA_MASK)
|
||||
#define EASRC_DPCS0R2_ST1_SA_SHIFT 0
|
||||
#define EASRC_DPCS0R2_ST1_SA_WIDTH 13
|
||||
#define EASRC_DPCS0R2_ST1_SA_MASK ((BIT(EASRC_DPCS0R2_ST1_SA_WIDTH) - 1) \
|
||||
<< EASRC_DPCS0R2_ST1_SA_SHIFT)
|
||||
#define EASRC_DPCS0R2_ST1_SA(v) (((v) << EASRC_DPCS0R2_ST1_SA_SHIFT) \
|
||||
& EASRC_DPCS0R2_ST1_SA_MASK)
|
||||
|
||||
/* ASRC Datapath Processor Control Slot0 Register3 (DPCS0R3) */
|
||||
#define EASRC_DPCS0R3_ST2_MA_SHIFT 16
|
||||
#define EASRC_DPCS0R3_ST2_MA_WIDTH 13
|
||||
#define EASRC_DPCS0R3_ST2_MA_MASK ((BIT(EASRC_DPCS0R3_ST2_MA_WIDTH) - 1) \
|
||||
<< EASRC_DPCS0R3_ST2_MA_SHIFT)
|
||||
#define EASRC_DPCS0R3_ST2_MA(v) (((v) << EASRC_DPCS0R3_ST2_MA_SHIFT) \
|
||||
& EASRC_DPCS0R3_ST2_MA_MASK)
|
||||
#define EASRC_DPCS0R3_ST2_SA_SHIFT 0
|
||||
#define EASRC_DPCS0R3_ST2_SA_WIDTH 13
|
||||
#define EASRC_DPCS0R3_ST2_SA_MASK ((BIT(EASRC_DPCS0R3_ST2_SA_WIDTH) - 1) \
|
||||
<< EASRC_DPCS0R3_ST2_SA_SHIFT)
|
||||
#define EASRC_DPCS0R3_ST2_SA(v) (((v) << EASRC_DPCS0R3_ST2_SA_SHIFT) \
|
||||
& EASRC_DPCS0R3_ST2_SA_MASK)
|
||||
|
||||
/* ASRC Context Output Control (COC) */
|
||||
#define EASRC_COC_FWMDE_SHIFT 28
|
||||
#define EASRC_COC_FWMDE_MASK BIT(EASRC_COC_FWMDE_SHIFT)
|
||||
#define EASRC_COC_FWMDE BIT(EASRC_COC_FWMDE_SHIFT)
|
||||
#define EASRC_COC_FIFO_WTMK_SHIFT 16
|
||||
#define EASRC_COC_FIFO_WTMK_WIDTH 7
|
||||
#define EASRC_COC_FIFO_WTMK_MASK ((BIT(EASRC_COC_FIFO_WTMK_WIDTH) - 1) \
|
||||
<< EASRC_COC_FIFO_WTMK_SHIFT)
|
||||
#define EASRC_COC_FIFO_WTMK(v) (((v) << EASRC_COC_FIFO_WTMK_SHIFT) \
|
||||
& EASRC_COC_FIFO_WTMK_MASK)
|
||||
#define EASRC_COC_SAMPLE_POS_SHIFT 11
|
||||
#define EASRC_COC_SAMPLE_POS_WIDTH 5
|
||||
#define EASRC_COC_SAMPLE_POS_MASK ((BIT(EASRC_COC_SAMPLE_POS_WIDTH) - 1) \
|
||||
<< EASRC_COC_SAMPLE_POS_SHIFT)
|
||||
#define EASRC_COC_SAMPLE_POS(v) (((v) << EASRC_COC_SAMPLE_POS_SHIFT) \
|
||||
& EASRC_COC_SAMPLE_POS_MASK)
|
||||
#define EASRC_COC_ENDIANNESS_SHIFT 10
|
||||
#define EASRC_COC_ENDIANNESS_MASK BIT(EASRC_COC_ENDIANNESS_SHIFT)
|
||||
#define EASRC_COC_ENDIANNESS BIT(EASRC_COC_ENDIANNESS_SHIFT)
|
||||
#define EASRC_COC_BPS_SHIFT 8
|
||||
#define EASRC_COC_BPS_WIDTH 2
|
||||
#define EASRC_COC_BPS_MASK ((BIT(EASRC_COC_BPS_WIDTH) - 1) \
|
||||
<< EASRC_COC_BPS_SHIFT)
|
||||
#define EASRC_COC_BPS(v) (((v) << EASRC_COC_BPS_SHIFT) \
|
||||
& EASRC_COC_BPS_MASK)
|
||||
#define EASRC_COC_FMT_SHIFT 7
|
||||
#define EASRC_COC_FMT_MASK BIT(EASRC_COC_FMT_SHIFT)
|
||||
#define EASRC_COC_FMT BIT(EASRC_COC_FMT_SHIFT)
|
||||
#define EASRC_COC_OUTSIGN_SHIFT 6
|
||||
#define EASRC_COC_OUTSIGN_MASK BIT(EASRC_COC_OUTSIGN_SHIFT)
|
||||
#define EASRC_COC_OUTSIGN_OUT BIT(EASRC_COC_OUTSIGN_SHIFT)
|
||||
#define EASRC_COC_IEC_VDATA_SHIFT 2
|
||||
#define EASRC_COC_IEC_VDATA_MASK BIT(EASRC_COC_IEC_VDATA_SHIFT)
|
||||
#define EASRC_COC_IEC_VDATA BIT(EASRC_COC_IEC_VDATA_SHIFT)
|
||||
#define EASRC_COC_IEC_EN_SHIFT 1
|
||||
#define EASRC_COC_IEC_EN_MASK BIT(EASRC_COC_IEC_EN_SHIFT)
|
||||
#define EASRC_COC_IEC_EN BIT(EASRC_COC_IEC_EN_SHIFT)
|
||||
#define EASRC_COC_DITHER_EN_SHIFT 0
|
||||
#define EASRC_COC_DITHER_EN_MASK BIT(EASRC_COC_DITHER_EN_SHIFT)
|
||||
#define EASRC_COC_DITHER_EN BIT(EASRC_COC_DITHER_EN_SHIFT)
|
||||
|
||||
/* ASRC Control Output Access (COA) */
|
||||
#define EASRC_COA_ITER_SHIFT 16
|
||||
#define EASRC_COA_ITER_WIDTH 6
|
||||
#define EASRC_COA_ITER_MASK ((BIT(EASRC_COA_ITER_WIDTH) - 1) \
|
||||
<< EASRC_COA_ITER_SHIFT)
|
||||
#define EASRC_COA_ITER(v) (((v) << EASRC_COA_ITER_SHIFT) \
|
||||
& EASRC_COA_ITER_MASK)
|
||||
#define EASRC_COA_GRLEN_SHIFT 8
|
||||
#define EASRC_COA_GRLEN_WIDTH 6
|
||||
#define EASRC_COA_GRLEN_MASK ((BIT(EASRC_COA_GRLEN_WIDTH) - 1) \
|
||||
<< EASRC_COA_GRLEN_SHIFT)
|
||||
#define EASRC_COA_GRLEN(v) (((v) << EASRC_COA_GRLEN_SHIFT) \
|
||||
& EASRC_COA_GRLEN_MASK)
|
||||
#define EASRC_COA_ACCLEN_SHIFT 0
|
||||
#define EASRC_COA_ACCLEN_WIDTH 6
|
||||
#define EASRC_COA_ACCLEN_MASK ((BIT(EASRC_COA_ACCLEN_WIDTH) - 1) \
|
||||
<< EASRC_COA_ACCLEN_SHIFT)
|
||||
#define EASRC_COA_ACCLEN(v) (((v) << EASRC_COA_ACCLEN_SHIFT) \
|
||||
& EASRC_COA_ACCLEN_MASK)
|
||||
|
||||
/* ASRC Sample FIFO Status (SFS) */
|
||||
#define EASRC_SFS_IWTMK_SHIFT 23
|
||||
#define EASRC_SFS_IWTMK_MASK BIT(EASRC_SFS_IWTMK_SHIFT)
|
||||
#define EASRC_SFS_IWTMK BIT(EASRC_SFS_IWTMK_SHIFT)
|
||||
#define EASRC_SFS_NSGI_SHIFT 16
|
||||
#define EASRC_SFS_NSGI_WIDTH 7
|
||||
#define EASRC_SFS_NSGI_MASK ((BIT(EASRC_SFS_NSGI_WIDTH) - 1) \
|
||||
<< EASRC_SFS_NSGI_SHIFT)
|
||||
#define EASRC_SFS_NSGI(v) (((v) << EASRC_SFS_NSGI_SHIFT) \
|
||||
& EASRC_SFS_NSGI_MASK)
|
||||
#define EASRC_SFS_OWTMK_SHIFT 7
|
||||
#define EASRC_SFS_OWTMK_MASK BIT(EASRC_SFS_OWTMK_SHIFT)
|
||||
#define EASRC_SFS_OWTMK BIT(EASRC_SFS_OWTMK_SHIFT)
|
||||
#define EASRC_SFS_NSGO_SHIFT 0
|
||||
#define EASRC_SFS_NSGO_WIDTH 7
|
||||
#define EASRC_SFS_NSGO_MASK ((BIT(EASRC_SFS_NSGO_WIDTH) - 1) \
|
||||
<< EASRC_SFS_NSGO_SHIFT)
|
||||
#define EASRC_SFS_NSGO(v) (((v) << EASRC_SFS_NSGO_SHIFT) \
|
||||
& EASRC_SFS_NSGO_MASK)
|
||||
|
||||
/* ASRC Resampling Ratio Low (RRL) */
|
||||
#define EASRC_RRL_RS_RL_SHIFT 0
|
||||
#define EASRC_RRL_RS_RL_WIDTH 32
|
||||
#define EASRC_RRL_RS_RL_MASK ((BIT(EASRC_RRL_RS_RL_WIDTH) - 1) \
|
||||
<< EASRC_RRL_RS_RL_SHIFT)
|
||||
#define EASRC_RRL_RS_RL(v) (((v) << EASRC_RRL_RS_RL_SHIFT) \
|
||||
& EASRC_RRL_RS_RL_MASK)
|
||||
|
||||
/* ASRC Resampling Ratio High (RRH) */
|
||||
#define EASRC_RRH_RS_VLD_SHIFT 31
|
||||
#define EASRC_RRH_RS_VLD_MASK BIT(EASRC_RRH_RS_VLD_SHIFT)
|
||||
#define EASRC_RRH_RS_VLD BIT(EASRC_RRH_RS_VLD_SHIFT)
|
||||
#define EASRC_RRH_RS_RH_SHIFT 0
|
||||
#define EASRC_RRH_RS_RH_WIDTH 12
|
||||
#define EASRC_RRH_RS_RH_MASK ((BIT(EASRC_RRH_RS_RH_WIDTH) - 1) \
|
||||
<< EASRC_RRH_RS_RH_SHIFT)
|
||||
#define EASRC_RRH_RS_RH(v) (((v) << EASRC_RRH_RS_RH_SHIFT) \
|
||||
& EASRC_RRH_RS_RH_MASK)
|
||||
|
||||
/* ASRC Resampling Ratio Update Control (RSUC) */
|
||||
#define EASRC_RSUC_RS_RM_SHIFT 0
|
||||
#define EASRC_RSUC_RS_RM_WIDTH 32
|
||||
#define EASRC_RSUC_RS_RM_MASK ((BIT(EASRC_RSUC_RS_RM_WIDTH) - 1) \
|
||||
<< EASRC_RSUC_RS_RM_SHIFT)
|
||||
#define EASRC_RSUC_RS_RM(v) (((v) << EASRC_RSUC_RS_RM_SHIFT) \
|
||||
& EASRC_RSUC_RS_RM_MASK)
|
||||
|
||||
/* ASRC Resampling Ratio Update Rate (RRUR) */
|
||||
#define EASRC_RRUR_RRR_SHIFT 0
|
||||
#define EASRC_RRUR_RRR_WIDTH 31
|
||||
#define EASRC_RRUR_RRR_MASK ((BIT(EASRC_RRUR_RRR_WIDTH) - 1) \
|
||||
<< EASRC_RRUR_RRR_SHIFT)
|
||||
#define EASRC_RRUR_RRR(v) (((v) << EASRC_RRUR_RRR_SHIFT) \
|
||||
& EASRC_RRUR_RRR_MASK)
|
||||
|
||||
/* ASRC Resampling Center Tap Coefficient Low (RCTCL) */
|
||||
#define EASRC_RCTCL_RS_CL_SHIFT 0
|
||||
#define EASRC_RCTCL_RS_CL_WIDTH 32
|
||||
#define EASRC_RCTCL_RS_CL_MASK ((BIT(EASRC_RCTCL_RS_CL_WIDTH) - 1) \
|
||||
<< EASRC_RCTCL_RS_CL_SHIFT)
|
||||
#define EASRC_RCTCL_RS_CL(v) (((v) << EASRC_RCTCL_RS_CL_SHIFT) \
|
||||
& EASRC_RCTCL_RS_CL_MASK)
|
||||
|
||||
/* ASRC Resampling Center Tap Coefficient High (RCTCH) */
|
||||
#define EASRC_RCTCH_RS_CH_SHIFT 0
|
||||
#define EASRC_RCTCH_RS_CH_WIDTH 32
|
||||
#define EASRC_RCTCH_RS_CH_MASK ((BIT(EASRC_RCTCH_RS_CH_WIDTH) - 1) \
|
||||
<< EASRC_RCTCH_RS_CH_SHIFT)
|
||||
#define EASRC_RCTCH_RS_CH(v) (((v) << EASRC_RCTCH_RS_CH_SHIFT) \
|
||||
& EASRC_RCTCH_RS_CH_MASK)
|
||||
|
||||
/* ASRC Prefilter Coefficient FIFO (PCF) */
|
||||
#define EASRC_PCF_CD_SHIFT 0
|
||||
#define EASRC_PCF_CD_WIDTH 32
|
||||
#define EASRC_PCF_CD_MASK ((BIT(EASRC_PCF_CD_WIDTH) - 1) \
|
||||
<< EASRC_PCF_CD_SHIFT)
|
||||
#define EASRC_PCF_CD(v) (((v) << EASRC_PCF_CD_SHIFT) \
|
||||
& EASRC_PCF_CD_MASK)
|
||||
|
||||
/* ASRC Context Resampling Coefficient Memory (CRCM) */
|
||||
#define EASRC_CRCM_RS_CWD_SHIFT 0
|
||||
#define EASRC_CRCM_RS_CWD_WIDTH 32
|
||||
#define EASRC_CRCM_RS_CWD_MASK ((BIT(EASRC_CRCM_RS_CWD_WIDTH) - 1) \
|
||||
<< EASRC_CRCM_RS_CWD_SHIFT)
|
||||
#define EASRC_CRCM_RS_CWD(v) (((v) << EASRC_CRCM_RS_CWD_SHIFT) \
|
||||
& EASRC_CRCM_RS_CWD_MASK)
|
||||
|
||||
/* ASRC Context Resampling Coefficient Control (CRCC) */
|
||||
#define EASRC_CRCC_RS_CA_SHIFT 16
|
||||
#define EASRC_CRCC_RS_CA_WIDTH 11
|
||||
#define EASRC_CRCC_RS_CA_MASK ((BIT(EASRC_CRCC_RS_CA_WIDTH) - 1) \
|
||||
<< EASRC_CRCC_RS_CA_SHIFT)
|
||||
#define EASRC_CRCC_RS_CA(v) (((v) << EASRC_CRCC_RS_CA_SHIFT) \
|
||||
& EASRC_CRCC_RS_CA_MASK)
|
||||
#define EASRC_CRCC_RS_TAPS_SHIFT 1
|
||||
#define EASRC_CRCC_RS_TAPS_WIDTH 2
|
||||
#define EASRC_CRCC_RS_TAPS_MASK ((BIT(EASRC_CRCC_RS_TAPS_WIDTH) - 1) \
|
||||
<< EASRC_CRCC_RS_TAPS_SHIFT)
|
||||
#define EASRC_CRCC_RS_TAPS(v) (((v) << EASRC_CRCC_RS_TAPS_SHIFT) \
|
||||
& EASRC_CRCC_RS_TAPS_MASK)
|
||||
#define EASRC_CRCC_RS_CPR_SHIFT 0
|
||||
#define EASRC_CRCC_RS_CPR_MASK BIT(EASRC_CRCC_RS_CPR_SHIFT)
|
||||
#define EASRC_CRCC_RS_CPR BIT(EASRC_CRCC_RS_CPR_SHIFT)
|
||||
|
||||
/* ASRC Interrupt_Control (IC) */
|
||||
#define EASRC_IRQC_RSDM_SHIFT 8
|
||||
#define EASRC_IRQC_RSDM_WIDTH 4
|
||||
#define EASRC_IRQC_RSDM_MASK ((BIT(EASRC_IRQC_RSDM_WIDTH) - 1) \
|
||||
<< EASRC_IRQC_RSDM_SHIFT)
|
||||
#define EASRC_IRQC_RSDM(v) (((v) << EASRC_IRQC_RSDM_SHIFT) \
|
||||
& EASRC_IRQC_RSDM_MASK)
|
||||
#define EASRC_IRQC_OERM_SHIFT 4
|
||||
#define EASRC_IRQC_OERM_WIDTH 4
|
||||
#define EASRC_IRQC_OERM_MASK ((BIT(EASRC_IRQC_OERM_WIDTH) - 1) \
|
||||
<< EASRC_IRQC_OERM_SHIFT)
|
||||
#define EASRC_IRQC_OERM(v) (((v) << EASRC_IRQC_OERM_SHIFT) \
|
||||
& EASRC_IEQC_OERM_MASK)
|
||||
#define EASRC_IRQC_IOM_SHIFT 0
|
||||
#define EASRC_IRQC_IOM_WIDTH 4
|
||||
#define EASRC_IRQC_IOM_MASK ((BIT(EASRC_IRQC_IOM_WIDTH) - 1) \
|
||||
<< EASRC_IRQC_IOM_SHIFT)
|
||||
#define EASRC_IRQC_IOM(v) (((v) << EASRC_IRQC_IOM_SHIFT) \
|
||||
& EASRC_IRQC_IOM_MASK)
|
||||
|
||||
/* ASRC Interrupt Status Flags (ISF) */
|
||||
#define EASRC_IRQF_RSD_SHIFT 8
|
||||
#define EASRC_IRQF_RSD_WIDTH 4
|
||||
#define EASRC_IRQF_RSD_MASK ((BIT(EASRC_IRQF_RSD_WIDTH) - 1) \
|
||||
<< EASRC_IRQF_RSD_SHIFT)
|
||||
#define EASRC_IRQF_RSD(v) (((v) << EASRC_IRQF_RSD_SHIFT) \
|
||||
& EASRC_IRQF_RSD_MASK)
|
||||
#define EASRC_IRQF_OER_SHIFT 4
|
||||
#define EASRC_IRQF_OER_WIDTH 4
|
||||
#define EASRC_IRQF_OER_MASK ((BIT(EASRC_IRQF_OER_WIDTH) - 1) \
|
||||
<< EASRC_IRQF_OER_SHIFT)
|
||||
#define EASRC_IRQF_OER(v) (((v) << EASRC_IRQF_OER_SHIFT) \
|
||||
& EASRC_IRQF_OER_MASK)
|
||||
#define EASRC_IRQF_IFO_SHIFT 0
|
||||
#define EASRC_IRQF_IFO_WIDTH 4
|
||||
#define EASRC_IRQF_IFO_MASK ((BIT(EASRC_IRQF_IFO_WIDTH) - 1) \
|
||||
<< EASRC_IRQF_IFO_SHIFT)
|
||||
#define EASRC_IRQF_IFO(v) (((v) << EASRC_IRQF_IFO_SHIFT) \
|
||||
& EASRC_IRQF_IFO_MASK)
|
||||
|
||||
/* ASRC Context Channel STAT */
|
||||
#define EASRC_CSx_CSx_SHIFT 0
|
||||
#define EASRC_CSx_CSx_WIDTH 32
|
||||
#define EASRC_CSx_CSx_MASK ((BIT(EASRC_CSx_CSx_WIDTH) - 1) \
|
||||
<< EASRC_CSx_CSx_SHIFT)
|
||||
#define EASRC_CSx_CSx(v) (((v) << EASRC_CSx_CSx_SHIFT) \
|
||||
& EASRC_CSx_CSx_MASK)
|
||||
|
||||
/* ASRC Debug Control Register */
|
||||
#define EASRC_DBGC_DMS_SHIFT 0
|
||||
#define EASRC_DBGC_DMS_WIDTH 6
|
||||
#define EASRC_DBGC_DMS_MASK ((BIT(EASRC_DBGC_DMS_WIDTH) - 1) \
|
||||
<< EASRC_DBGC_DMS_SHIFT)
|
||||
#define EASRC_DBGC_DMS(v) (((v) << EASRC_DBGC_DMS_SHIFT) \
|
||||
& EASRC_DBGC_DMS_MASK)
|
||||
|
||||
/* ASRC Debug Status Register */
|
||||
#define EASRC_DBGS_DS_SHIFT 0
|
||||
#define EASRC_DBGS_DS_WIDTH 32
|
||||
#define EASRC_DBGS_DS_MASK ((BIT(EASRC_DBGS_DS_WIDTH) - 1) \
|
||||
<< EASRC_DBGS_DS_SHIFT)
|
||||
#define EASRC_DBGS_DS(v) (((v) << EASRC_DBGS_DS_SHIFT) \
|
||||
& EASRC_DBGS_DS_MASK)
|
||||
|
||||
/* General Constants */
|
||||
#define EASRC_CTX_MAX_NUM 4
|
||||
#define EASRC_32b_MASK (BIT(32) - 1)
|
||||
#define EASRC_64b_MASK (BIT(64) - 1)
|
||||
#define EASRC_RS_COEFF_MEM 0
|
||||
#define EASRC_PF_COEFF_MEM 1
|
||||
|
||||
/* Prefilter constants */
|
||||
#define EASRC_PF_ST1_ONLY 0
|
||||
#define EASRC_PF_TWO_STAGE_MODE 1
|
||||
#define EASRC_PF_ST1_COEFF_WR 0
|
||||
#define EASRC_PF_ST2_COEFF_WR 1
|
||||
#define EASRC_MAX_PF_TAPS 384
|
||||
|
||||
/* Resampling constants */
|
||||
#define EASRC_RS_32_TAPS 0
|
||||
#define EASRC_RS_64_TAPS 1
|
||||
#define EASRC_RS_128_TAPS 2
|
||||
|
||||
/* Initialization mode */
|
||||
#define EASRC_INIT_MODE_SW_CONTROL 0
|
||||
#define EASRC_INIT_MODE_REPLICATE 1
|
||||
#define EASRC_INIT_MODE_ZERO_FILL 2
|
||||
|
||||
/* directions */
|
||||
#define IN 0
|
||||
#define OUT 1
|
||||
|
||||
/* FIFO watermarks */
|
||||
#define FSL_EASRC_INPUTFIFO_WML 0x4
|
||||
#define FSL_EASRC_OUTPUTFIFO_WML 0x1
|
||||
|
||||
#define EASRC_INPUTFIFO_THRESHOLD_MIN 0
|
||||
#define EASRC_INPUTFIFO_THRESHOLD_MAX 127
|
||||
#define EASRC_OUTPUTFIFO_THRESHOLD_MIN 0
|
||||
#define EASRC_OUTPUTFIFO_THRESHOLD_MAX 63
|
||||
|
||||
#define EASRC_DMA_BUFFER_SIZE (1024 * 48 * 9)
|
||||
#define EASRC_MAX_BUFFER_SIZE (1024 * 48)
|
||||
|
||||
#define FIRMWARE_MAGIC 0xDEAD
|
||||
#define FIRMWARE_VERSION 1
|
||||
|
||||
enum easrc_word_width {
|
||||
EASRC_WIDTH_16_BIT = 0,
|
||||
EASRC_WIDTH_20_BIT = 1,
|
||||
EASRC_WIDTH_24_BIT = 2,
|
||||
EASRC_WIDTH_32_BIT = 3,
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) asrc_firmware_hdr {
|
||||
u32 magic;
|
||||
u32 interp_scen;
|
||||
u32 prefil_scen;
|
||||
u32 firmware_version;
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) interp_params {
|
||||
u32 magic;
|
||||
u32 num_taps;
|
||||
u32 num_phases;
|
||||
u64 center_tap;
|
||||
u64 coeff[8192];
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) prefil_params {
|
||||
u32 magic;
|
||||
u32 insr;
|
||||
u32 outsr;
|
||||
u32 st1_taps;
|
||||
u32 st2_taps;
|
||||
u32 st1_exp;
|
||||
u64 coeff[256];
|
||||
};
|
||||
|
||||
struct dma_block {
|
||||
void *dma_vaddr;
|
||||
unsigned int length;
|
||||
unsigned int max_buf_size;
|
||||
};
|
||||
|
||||
struct fsl_easrc_data_fmt {
|
||||
unsigned int width : 2;
|
||||
unsigned int endianness : 1;
|
||||
unsigned int unsign : 1;
|
||||
unsigned int floating_point : 1;
|
||||
unsigned int iec958: 1;
|
||||
unsigned int sample_pos: 5;
|
||||
unsigned int addexp;
|
||||
};
|
||||
|
||||
struct fsl_easrc_io_params {
|
||||
struct fsl_easrc_data_fmt fmt;
|
||||
unsigned int group_len;
|
||||
unsigned int iterations;
|
||||
unsigned int access_len;
|
||||
unsigned int fifo_wtmk;
|
||||
unsigned int sample_rate;
|
||||
unsigned int sample_format;
|
||||
unsigned int norm_rate;
|
||||
};
|
||||
|
||||
struct fsl_easrc_slot {
|
||||
bool busy;
|
||||
int ctx_index;
|
||||
int num_channel; /*maximum is 8*/
|
||||
int min_channel;
|
||||
int max_channel;
|
||||
int pf_mem_used;
|
||||
};
|
||||
|
||||
struct fsl_easrc_context {
|
||||
enum asrc_pair_index index;
|
||||
struct fsl_easrc *easrc;
|
||||
struct dma_chan *dma_chan[2];
|
||||
struct dma_async_tx_descriptor *desc[2];
|
||||
struct fsl_easrc_io_params in_params;
|
||||
struct fsl_easrc_io_params out_params;
|
||||
struct imx_dma_data dma_data;
|
||||
unsigned int channels;
|
||||
unsigned int st1_num_taps;
|
||||
unsigned int st2_num_taps;
|
||||
unsigned int st1_num_exp;
|
||||
unsigned int pf_init_mode;
|
||||
unsigned int rs_init_mode;
|
||||
unsigned int ctx_streams;
|
||||
unsigned int pos;
|
||||
u64 rs_ratio;
|
||||
u64 *st1_coeff;
|
||||
u64 *st2_coeff;
|
||||
int in_filled_sample;
|
||||
int out_missed_sample;
|
||||
int st1_addexp;
|
||||
int st2_addexp;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* fsl_easrc: EASRC private data
|
||||
*
|
||||
* name : name of EASRC device
|
||||
* @pdev: platform device pointer
|
||||
* @regmap: regmap handler
|
||||
* @dma_params_rx: DMA parameters for receive channel
|
||||
* @dma_params_tx: DMA parameters for transmit channel
|
||||
* @ctx: context pointer
|
||||
* @slot: slot setting
|
||||
* @mem_clk: clock source to access register
|
||||
* @firmware_hdr: the header of firmware
|
||||
* @interp: pointer to interpolation filter coeff
|
||||
* @prefil: pointer to prefilter coeff
|
||||
* @fw: firmware of coeff table
|
||||
* @fw_name: firmware name
|
||||
* @paddr: physical address to the base address of registers
|
||||
* @rs_num_taps: resample filter taps, 32, 64, or 128
|
||||
* @bps_i2c958: bits per sample of iec958
|
||||
* @chn_avail: available channels, maximum 32
|
||||
* @lock: spin lock for resource protection
|
||||
* @easrc_rate: default sample rate for ASoC Back-Ends
|
||||
* @easrc_format: default sample format for ASoC Back-Ends
|
||||
*/
|
||||
|
||||
struct fsl_easrc {
|
||||
char name[32];
|
||||
struct platform_device *pdev;
|
||||
struct regmap *regmap;
|
||||
struct miscdevice easrc_miscdev;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
struct fsl_easrc_context *ctx[EASRC_CTX_MAX_NUM];
|
||||
struct fsl_easrc_slot slot[EASRC_CTX_MAX_NUM][2];
|
||||
struct clk *mem_clk;
|
||||
struct asrc_firmware_hdr *firmware_hdr;
|
||||
struct interp_params *interp;
|
||||
struct prefil_params *prefil;
|
||||
const struct firmware *fw;
|
||||
const char *fw_name;
|
||||
unsigned long paddr;
|
||||
unsigned int rs_num_taps;
|
||||
unsigned int bps_iec958[EASRC_CTX_MAX_NUM];
|
||||
unsigned int chn_avail;
|
||||
u64 *rs_coeff;
|
||||
u64 const_coeff;
|
||||
int firmware_loaded;
|
||||
spinlock_t lock; /* spin lock for resource protection */
|
||||
int easrc_rate;
|
||||
int easrc_format;
|
||||
};
|
||||
|
||||
struct dma_chan *fsl_easrc_get_dma_channel(
|
||||
struct fsl_easrc_context *ctx, bool dir);
|
||||
int fsl_easrc_request_context(
|
||||
struct fsl_easrc_context *ctx, unsigned int channels);
|
||||
int fsl_easrc_release_context(struct fsl_easrc_context *ctx);
|
||||
|
||||
#define DRV_NAME "fsl-easrc-dma"
|
||||
#endif /* _FSL_EASRC_H */
|
493
sound/soc/fsl/fsl_easrc_dma.c
Normal file
493
sound/soc/fsl/fsl_easrc_dma.c
Normal file
|
@ -0,0 +1,493 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright 2019 NXP
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/dma-imx.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "fsl_easrc.h"
|
||||
|
||||
#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
|
||||
|
||||
static struct snd_pcm_hardware snd_imx_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
.buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = 65532, /* Limited by SDMA engine */
|
||||
.periods_min = 2,
|
||||
.periods_max = 255,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static bool filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
if (!imx_dma_is_general_purpose(chan))
|
||||
return false;
|
||||
|
||||
chan->private = param;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void fsl_easrc_dma_complete(void *arg)
|
||||
{
|
||||
struct snd_pcm_substream *substream = arg;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_easrc_context *ctx = runtime->private_data;
|
||||
|
||||
ctx->pos += snd_pcm_lib_period_bytes(substream);
|
||||
if (ctx->pos >= snd_pcm_lib_buffer_bytes(substream))
|
||||
ctx->pos = 0;
|
||||
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
|
||||
static int fsl_easrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
|
||||
{
|
||||
u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_easrc_context *ctx = runtime->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct device *dev = component->dev;
|
||||
unsigned long flags = DMA_CTRL_ACK;
|
||||
|
||||
/* Prepare and submit Front-End DMA channel */
|
||||
if (!substream->runtime->no_period_wakeup)
|
||||
flags |= DMA_PREP_INTERRUPT;
|
||||
|
||||
ctx->pos = 0;
|
||||
ctx->desc[!dir] = dmaengine_prep_dma_cyclic(
|
||||
ctx->dma_chan[!dir], runtime->dma_addr,
|
||||
snd_pcm_lib_buffer_bytes(substream),
|
||||
snd_pcm_lib_period_bytes(substream),
|
||||
dir == OUT ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, flags);
|
||||
if (!ctx->desc[!dir]) {
|
||||
dev_err(dev, "failed to prepare slave DMA for Front-End\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ctx->desc[!dir]->callback = fsl_easrc_dma_complete;
|
||||
ctx->desc[!dir]->callback_param = substream;
|
||||
|
||||
dmaengine_submit(ctx->desc[!dir]);
|
||||
|
||||
/* Prepare and submit Back-End DMA channel */
|
||||
ctx->desc[dir] = dmaengine_prep_dma_cyclic(
|
||||
ctx->dma_chan[dir], 0xffff, 64, 64, DMA_DEV_TO_DEV, 0);
|
||||
if (!ctx->desc[dir]) {
|
||||
dev_err(dev, "failed to prepare slave DMA for Back-End\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dmaengine_submit(ctx->desc[dir]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_easrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_easrc_context *ctx = runtime->private_data;
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
ret = fsl_easrc_dma_prepare_and_submit(substream);
|
||||
if (ret)
|
||||
return ret;
|
||||
dma_async_issue_pending(ctx->dma_chan[IN]);
|
||||
dma_async_issue_pending(ctx->dma_chan[OUT]);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
dmaengine_terminate_all(ctx->dma_chan[OUT]);
|
||||
dmaengine_terminate_all(ctx->dma_chan[IN]);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_easrc_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
|
||||
struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_easrc_context *ctx = runtime->private_data;
|
||||
struct fsl_easrc *easrc = ctx->easrc;
|
||||
struct dma_slave_config config_fe, config_be;
|
||||
enum asrc_pair_index index = ctx->index;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct device *dev = component->dev;
|
||||
int stream = substream->stream;
|
||||
struct imx_dma_data *tmp_data;
|
||||
struct snd_soc_dpcm *dpcm;
|
||||
struct dma_chan *tmp_chan;
|
||||
struct device *dev_be;
|
||||
u8 dir = tx ? OUT : IN;
|
||||
dma_cap_mask_t mask;
|
||||
enum sdma_peripheral_type be_peripheral_type;
|
||||
int ret;
|
||||
|
||||
/* Fetch the Back-End dma_data from DPCM */
|
||||
list_for_each_entry(dpcm, &rtd->dpcm[stream].be_clients, list_be) {
|
||||
struct snd_soc_pcm_runtime *be = dpcm->be;
|
||||
struct snd_pcm_substream *substream_be;
|
||||
struct snd_soc_dai *dai = be->cpu_dai;
|
||||
|
||||
if (dpcm->fe != rtd)
|
||||
continue;
|
||||
|
||||
substream_be = snd_soc_dpcm_get_substream(be, stream);
|
||||
dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be);
|
||||
dev_be = dai->dev;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!dma_params_be) {
|
||||
dev_err(dev, "failed to get the substream of Back-End\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Override dma_data of the Front-End and config its dmaengine */
|
||||
dma_params_fe = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
dma_params_fe->addr = easrc->paddr + REG_EASRC_FIFO(!dir, index);
|
||||
dma_params_fe->maxburst = dma_params_be->maxburst;
|
||||
|
||||
ctx->dma_chan[!dir] = fsl_easrc_get_dma_channel(ctx, !dir);
|
||||
if (!ctx->dma_chan[!dir]) {
|
||||
dev_err(dev, "failed to request DMA channel\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&config_fe, 0, sizeof(config_fe));
|
||||
ret = snd_dmaengine_pcm_prepare_slave_config(substream,
|
||||
params, &config_fe);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to prepare DMA config for Front-End\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(ctx->dma_chan[!dir], &config_fe);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to config DMA channel for Front-End\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Request and config DMA channel for Back-End */
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
dma_cap_set(DMA_CYCLIC, mask);
|
||||
|
||||
/* Get DMA request of Back-End */
|
||||
tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
|
||||
if (tmp_chan) {
|
||||
tmp_data = tmp_chan->private;
|
||||
if (tmp_data) {
|
||||
ctx->dma_data.dma_request = tmp_data->dma_request;
|
||||
be_peripheral_type = tmp_data->peripheral_type;
|
||||
if (tx && be_peripheral_type == IMX_DMATYPE_SSI_DUAL)
|
||||
ctx->dma_data.dst_dualfifo = true;
|
||||
if (!tx && be_peripheral_type == IMX_DMATYPE_SSI_DUAL)
|
||||
ctx->dma_data.src_dualfifo = true;
|
||||
}
|
||||
dma_release_channel(tmp_chan);
|
||||
}
|
||||
|
||||
/* Get DMA request of Front-End */
|
||||
tmp_chan = fsl_easrc_get_dma_channel(ctx, dir);
|
||||
if (tmp_chan) {
|
||||
tmp_data = tmp_chan->private;
|
||||
if (tmp_data) {
|
||||
ctx->dma_data.dma_request2 = tmp_data->dma_request;
|
||||
ctx->dma_data.peripheral_type =
|
||||
tmp_data->peripheral_type;
|
||||
ctx->dma_data.priority = tmp_data->priority;
|
||||
}
|
||||
dma_release_channel(tmp_chan);
|
||||
}
|
||||
|
||||
/* For sdma DEV_TO_DEV, there is two dma request
|
||||
* But for emda DEV_TO_DEV, there is only one dma request, which is
|
||||
* from the BE.
|
||||
*/
|
||||
if (ctx->dma_data.dma_request2 != ctx->dma_data.dma_request)
|
||||
ctx->dma_chan[dir] =
|
||||
dma_request_channel(mask, filter, &ctx->dma_data);
|
||||
else
|
||||
ctx->dma_chan[dir] =
|
||||
dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
|
||||
|
||||
if (!ctx->dma_chan[dir]) {
|
||||
dev_err(dev, "failed to request DMA channel for Back-End\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (easrc->easrc_format == SNDRV_PCM_FORMAT_S16_LE)
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
else
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
||||
memset(&config_be, 0, sizeof(config_be));
|
||||
|
||||
config_be.direction = DMA_DEV_TO_DEV;
|
||||
config_be.src_addr_width = buswidth;
|
||||
config_be.src_maxburst = dma_params_be->maxburst;
|
||||
config_be.dst_addr_width = buswidth;
|
||||
config_be.dst_maxburst = dma_params_be->maxburst;
|
||||
|
||||
if (tx) {
|
||||
config_be.src_addr = easrc->paddr + REG_EASRC_RDFIFO(index);
|
||||
config_be.dst_addr = dma_params_be->addr;
|
||||
} else {
|
||||
config_be.dst_addr = easrc->paddr + REG_EASRC_WRFIFO(index);
|
||||
config_be.src_addr = dma_params_be->addr;
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(ctx->dma_chan[dir], &config_be);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to config DMA channel for Back-End\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_easrc_dma_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_easrc_context *ctx = runtime->private_data;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, NULL);
|
||||
|
||||
if (ctx->dma_chan[IN])
|
||||
dma_release_channel(ctx->dma_chan[IN]);
|
||||
|
||||
if (ctx->dma_chan[OUT])
|
||||
dma_release_channel(ctx->dma_chan[OUT]);
|
||||
|
||||
ctx->dma_chan[IN] = NULL;
|
||||
ctx->dma_chan[OUT] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_easrc_dma_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct device *dev = component->dev;
|
||||
struct fsl_easrc *easrc = dev_get_drvdata(dev);
|
||||
struct fsl_easrc_context *ctx;
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
u8 dir = tx ? OUT : IN;
|
||||
struct dma_slave_caps dma_caps;
|
||||
struct dma_chan *tmp_chan;
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->easrc = easrc;
|
||||
|
||||
runtime->private_data = ctx;
|
||||
|
||||
ret = snd_pcm_hw_constraint_integer(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set pcm hw params periods\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
|
||||
fsl_easrc_request_context(ctx, 1);
|
||||
|
||||
tmp_chan = fsl_easrc_get_dma_channel(ctx, dir);
|
||||
if (!tmp_chan) {
|
||||
dev_err(dev, "can't get dma channel\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = dma_get_slave_caps(tmp_chan, &dma_caps);
|
||||
if (ret == 0) {
|
||||
if (dma_caps.cmd_pause)
|
||||
snd_imx_hardware.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
|
||||
if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
|
||||
snd_imx_hardware.info |= SNDRV_PCM_INFO_BATCH;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
addr_widths = dma_caps.dst_addr_widths;
|
||||
else
|
||||
addr_widths = dma_caps.src_addr_widths;
|
||||
}
|
||||
|
||||
/*
|
||||
* If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
|
||||
* hw.formats set to 0, meaning no restrictions are in place.
|
||||
* In this case it's the responsibility of the DAI driver to
|
||||
* provide the supported format information.
|
||||
*/
|
||||
if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
|
||||
/*
|
||||
* Prepare formats mask for valid/allowed sample types. If the
|
||||
* dma does not have support for the given physical word size,
|
||||
* it needs to be masked out so user space can not use the
|
||||
* format which produces corrupted audio.
|
||||
* In case the dma driver does not implement the slave_caps the
|
||||
* default assumption is that it supports 1, 2 and 4 bytes
|
||||
* widths.
|
||||
*/
|
||||
for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
|
||||
int bits = snd_pcm_format_physical_width(i);
|
||||
|
||||
/*
|
||||
* Enable only samples with DMA supported physical
|
||||
* widths
|
||||
*/
|
||||
switch (bits) {
|
||||
case 8:
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
case 64:
|
||||
if (addr_widths & (1 << (bits / 8)))
|
||||
snd_imx_hardware.formats |= (1LL << i);
|
||||
break;
|
||||
default:
|
||||
/* Unsupported types */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp_chan)
|
||||
dma_release_channel(tmp_chan);
|
||||
|
||||
fsl_easrc_release_context(ctx);
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_easrc_dma_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_easrc_context *ctx = runtime->private_data;
|
||||
struct fsl_easrc *easrc;
|
||||
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
easrc = ctx->easrc;
|
||||
|
||||
if (easrc->ctx[ctx->index] == ctx)
|
||||
easrc->ctx[ctx->index] = NULL;
|
||||
|
||||
kfree(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t fsl_easrc_dma_pcm_pointer(
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_easrc_context *ctx = runtime->private_data;
|
||||
|
||||
return bytes_to_frames(substream->runtime, ctx->pos);
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops fsl_easrc_dma_pcm_ops = {
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = fsl_easrc_dma_hw_params,
|
||||
.hw_free = fsl_easrc_dma_hw_free,
|
||||
.trigger = fsl_easrc_dma_trigger,
|
||||
.open = fsl_easrc_dma_startup,
|
||||
.close = fsl_easrc_dma_shutdown,
|
||||
.pointer = fsl_easrc_dma_pcm_pointer,
|
||||
};
|
||||
|
||||
static int fsl_easrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
int ret, i;
|
||||
|
||||
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "failed to set DMA mask\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
|
||||
substream = pcm->streams[i].substream;
|
||||
if (!substream)
|
||||
continue;
|
||||
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
|
||||
FSL_ASRC_DMABUF_SIZE,
|
||||
&substream->dma_buffer);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "failed to allocate DMA buffer\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (--i == 0 && pcm->streams[i].substream)
|
||||
snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fsl_easrc_dma_pcm_free(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
int i;
|
||||
|
||||
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
|
||||
substream = pcm->streams[i].substream;
|
||||
if (!substream)
|
||||
continue;
|
||||
|
||||
snd_dma_free_pages(&substream->dma_buffer);
|
||||
substream->dma_buffer.area = NULL;
|
||||
substream->dma_buffer.addr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct snd_soc_component_driver fsl_easrc_dma_component = {
|
||||
.name = DRV_NAME,
|
||||
.ops = &fsl_easrc_dma_pcm_ops,
|
||||
.pcm_new = fsl_easrc_dma_pcm_new,
|
||||
.pcm_free = fsl_easrc_dma_pcm_free,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_easrc_dma_component);
|
966
sound/soc/fsl/fsl_easrc_m2m.c
Normal file
966
sound/soc/fsl/fsl_easrc_m2m.c
Normal file
|
@ -0,0 +1,966 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright 2019 NXP
|
||||
|
||||
struct fsl_easrc_m2m {
|
||||
struct fsl_easrc *easrc;
|
||||
struct fsl_easrc_context *ctx;
|
||||
struct completion complete[2];
|
||||
struct dma_block dma_block[2];
|
||||
unsigned int ctx_hold;
|
||||
unsigned int easrc_active;
|
||||
unsigned int first_convert;
|
||||
unsigned int sg_nodes[2];
|
||||
struct scatterlist sg[2][9];
|
||||
struct dma_async_tx_descriptor *desc[2];
|
||||
spinlock_t lock; /* protect mem resource */
|
||||
};
|
||||
|
||||
void fsl_easrc_get_status(struct fsl_easrc_context *ctx,
|
||||
struct asrc_status_flags *flags)
|
||||
{
|
||||
flags->overload_error = 0;
|
||||
}
|
||||
|
||||
#define mxc_easrc_dma_umap_in(dev, m2m) \
|
||||
dma_unmap_sg(dev, m2m->sg[IN], m2m->sg_nodes[IN], \
|
||||
DMA_MEM_TO_DEV) \
|
||||
|
||||
#define mxc_easrc_dma_umap_out(dev, m2m) \
|
||||
dma_unmap_sg(dev, m2m->sg[OUT], m2m->sg_nodes[OUT], \
|
||||
DMA_DEV_TO_MEM) \
|
||||
|
||||
#define EASRC_xPUT_DMA_CALLBACK(dir) \
|
||||
((dir == IN) ? fsl_easrc_input_dma_callback \
|
||||
: fsl_easrc_output_dma_callback)
|
||||
|
||||
#define DIR_STR(dir) dir == IN ? "in" : "out"
|
||||
|
||||
static void fsl_easrc_input_dma_callback(void *data)
|
||||
{
|
||||
struct fsl_easrc_m2m *m2m = (struct fsl_easrc_m2m *)data;
|
||||
|
||||
complete(&m2m->complete[IN]);
|
||||
}
|
||||
|
||||
static void fsl_easrc_output_dma_callback(void *data)
|
||||
{
|
||||
struct fsl_easrc_m2m *m2m = (struct fsl_easrc_m2m *)data;
|
||||
|
||||
complete(&m2m->complete[OUT]);
|
||||
}
|
||||
|
||||
static int fsl_allocate_dma_buf(struct fsl_easrc_m2m *m2m)
|
||||
{
|
||||
struct dma_block *input = &m2m->dma_block[IN];
|
||||
struct dma_block *output = &m2m->dma_block[OUT];
|
||||
|
||||
input->dma_vaddr = kzalloc(input->length, GFP_KERNEL);
|
||||
if (!input->dma_vaddr)
|
||||
return -ENOMEM;
|
||||
|
||||
output->dma_vaddr = kzalloc(output->length, GFP_KERNEL);
|
||||
if (!output->dma_vaddr)
|
||||
goto alloc_fail;
|
||||
|
||||
return 0;
|
||||
|
||||
alloc_fail:
|
||||
kfree(input->dma_vaddr);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int fsl_easrc_dmaconfig(struct fsl_easrc_m2m *m2m,
|
||||
struct dma_chan *chan,
|
||||
u32 dma_addr, void *buf_addr, u32 buf_len,
|
||||
bool dir, int bits)
|
||||
{
|
||||
struct dma_async_tx_descriptor *desc = m2m->desc[dir];
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
unsigned int sg_nent = m2m->sg_nodes[dir];
|
||||
struct scatterlist *sg = m2m->sg[dir];
|
||||
struct dma_slave_config slave_config;
|
||||
enum dma_slave_buswidth buswidth;
|
||||
int ret, i;
|
||||
|
||||
switch (bits) {
|
||||
case 16:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
break;
|
||||
case 24:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
|
||||
break;
|
||||
default:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
}
|
||||
|
||||
if (dir == IN) {
|
||||
slave_config.direction = DMA_MEM_TO_DEV;
|
||||
slave_config.dst_addr = dma_addr;
|
||||
slave_config.dst_addr_width = buswidth;
|
||||
slave_config.dst_maxburst =
|
||||
ctx->in_params.fifo_wtmk * ctx->channels;
|
||||
} else {
|
||||
slave_config.direction = DMA_DEV_TO_MEM;
|
||||
slave_config.src_addr = dma_addr;
|
||||
slave_config.src_addr_width = buswidth;
|
||||
slave_config.src_maxburst =
|
||||
ctx->out_params.fifo_wtmk * ctx->channels;
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(chan, &slave_config);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to config dmaengine for %sput task: %d\n",
|
||||
DIR_STR(dir), ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sg_init_table(sg, sg_nent);
|
||||
switch (sg_nent) {
|
||||
case 1:
|
||||
sg_init_one(sg, buf_addr, buf_len);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
for (i = 0; i < (sg_nent - 1); i++)
|
||||
sg_set_buf(&sg[i],
|
||||
buf_addr + i * m2m->dma_block[dir].max_buf_size,
|
||||
m2m->dma_block[dir].max_buf_size);
|
||||
|
||||
sg_set_buf(&sg[i],
|
||||
buf_addr + i * m2m->dma_block[dir].max_buf_size,
|
||||
buf_len - i * m2m->dma_block[dir].max_buf_size);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid input DMA nodes number: %d\n", sg_nent);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = dma_map_sg(dev, sg, sg_nent, slave_config.direction);
|
||||
if (ret != sg_nent) {
|
||||
dev_err(dev, "failed to map DMA sg for %sput task\n",
|
||||
DIR_STR(dir));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc = dmaengine_prep_slave_sg(chan, sg, sg_nent,
|
||||
slave_config.direction,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!desc) {
|
||||
dev_err(dev, "failed to prepare dmaengine for %sput task\n",
|
||||
DIR_STR(dir));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m2m->desc[dir] = desc;
|
||||
m2m->desc[dir]->callback = EASRC_xPUT_DMA_CALLBACK(dir);
|
||||
|
||||
desc->callback = EASRC_xPUT_DMA_CALLBACK(dir);
|
||||
desc->callback_param = m2m;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long fsl_easrc_calc_outbuf_len(struct fsl_easrc_m2m *m2m,
|
||||
struct asrc_convert_buffer *pbuf)
|
||||
{
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
unsigned int out_length;
|
||||
unsigned int in_width, out_width;
|
||||
unsigned int channels = ctx->channels;
|
||||
unsigned int in_samples, out_samples;
|
||||
|
||||
in_width = snd_pcm_format_physical_width(ctx->in_params.sample_format) / 8;
|
||||
out_width = snd_pcm_format_physical_width(ctx->out_params.sample_format) / 8;
|
||||
|
||||
in_samples = pbuf->input_buffer_length / (in_width * channels);
|
||||
out_samples = ctx->out_params.sample_rate * in_samples /
|
||||
ctx->in_params.sample_rate;
|
||||
out_length = out_samples * out_width * channels;
|
||||
|
||||
if (out_samples <= ctx->out_missed_sample) {
|
||||
out_length = 0;
|
||||
ctx->out_missed_sample -= out_samples;
|
||||
} else {
|
||||
out_length -= ctx->out_missed_sample * out_width * channels;
|
||||
ctx->out_missed_sample = 0;
|
||||
}
|
||||
|
||||
return out_length;
|
||||
}
|
||||
|
||||
static long fsl_easrc_prepare_io_buffer(struct fsl_easrc_m2m *m2m,
|
||||
struct asrc_convert_buffer *buf,
|
||||
bool dir)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
struct dma_chan *dma_chan = ctx->dma_chan[dir];
|
||||
unsigned int *dma_len = &m2m->dma_block[dir].length;
|
||||
unsigned int *sg_nodes = &m2m->sg_nodes[dir];
|
||||
void *dma_vaddr = m2m->dma_block[dir].dma_vaddr;
|
||||
enum asrc_pair_index index = m2m->ctx->index;
|
||||
unsigned int buf_len, bits;
|
||||
u32 fifo_addr;
|
||||
void __user *buf_vaddr;
|
||||
|
||||
if (dir == IN) {
|
||||
buf_vaddr = (void __user *)buf->input_buffer_vaddr;
|
||||
buf_len = buf->input_buffer_length;
|
||||
bits = snd_pcm_format_physical_width(ctx->in_params.sample_format);
|
||||
fifo_addr = easrc->paddr + REG_EASRC_WRFIFO(index);
|
||||
} else {
|
||||
buf_vaddr = (void __user *)buf->output_buffer_vaddr;
|
||||
buf_len = buf->output_buffer_length;
|
||||
bits = snd_pcm_format_physical_width(ctx->out_params.sample_format);
|
||||
fifo_addr = easrc->paddr + REG_EASRC_RDFIFO(index);
|
||||
}
|
||||
|
||||
if (buf_len > EASRC_DMA_BUFFER_SIZE ||
|
||||
(dir == IN && (buf_len % (bits / 8)))) {
|
||||
dev_err(dev, "%sput buffer size is error: [%d]\n",
|
||||
DIR_STR(dir), buf_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dir == IN && copy_from_user(dma_vaddr, buf_vaddr, buf_len))
|
||||
return -EFAULT;
|
||||
|
||||
*dma_len = buf_len;
|
||||
|
||||
if (dir == OUT)
|
||||
*dma_len = fsl_easrc_calc_outbuf_len(m2m, buf);
|
||||
|
||||
if (*dma_len <= 0)
|
||||
return 0;
|
||||
|
||||
*sg_nodes = *dma_len / m2m->dma_block[dir].max_buf_size + 1;
|
||||
|
||||
return fsl_easrc_dmaconfig(m2m, dma_chan, fifo_addr, dma_vaddr,
|
||||
*dma_len, dir, bits);
|
||||
}
|
||||
|
||||
static long fsl_easrc_prepare_buffer(struct fsl_easrc_m2m *m2m,
|
||||
struct asrc_convert_buffer *buf)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
int ret;
|
||||
|
||||
ret = fsl_easrc_prepare_io_buffer(m2m, buf, IN);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to prepare input buffer %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = fsl_easrc_prepare_io_buffer(m2m, buf, OUT);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to prepare output buffer %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fsl_easrc_process_buffer_pre(struct fsl_easrc_m2m *m2m, bool dir)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
|
||||
if (!wait_for_completion_interruptible_timeout(&m2m->complete[dir],
|
||||
10 * HZ)) {
|
||||
dev_err(dev, "%sput DMA task timeout\n", DIR_STR(dir));
|
||||
return -ETIME;
|
||||
} else if (signal_pending(current)) {
|
||||
dev_err(dev, "%sput task forcibly aborted\n", DIR_STR(dir));
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int fsl_easrc_get_output_FIFO_size(struct fsl_easrc_m2m *m2m)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
enum asrc_pair_index index = m2m->ctx->index;
|
||||
u32 val;
|
||||
|
||||
regmap_read(easrc->regmap, REG_EASRC_SFS(index), &val);
|
||||
|
||||
val &= EASRC_SFS_NSGO_MASK;
|
||||
|
||||
return val >> EASRC_SFS_NSGO_SHIFT;
|
||||
}
|
||||
|
||||
static void fsl_easrc_read_last_FIFO(struct fsl_easrc_m2m *m2m)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct dma_block *output = &m2m->dma_block[OUT];
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
enum asrc_pair_index index = m2m->ctx->index;
|
||||
u32 i, reg, size, t_size = 0, width;
|
||||
u32 *reg32 = NULL;
|
||||
u16 *reg16 = NULL;
|
||||
u8 *reg24 = NULL;
|
||||
|
||||
width = snd_pcm_format_physical_width(ctx->out_params.sample_format);
|
||||
|
||||
if (width == 32)
|
||||
reg32 = output->dma_vaddr + output->length;
|
||||
else if (width == 16)
|
||||
reg16 = output->dma_vaddr + output->length;
|
||||
else
|
||||
reg24 = output->dma_vaddr + output->length;
|
||||
retry:
|
||||
size = fsl_easrc_get_output_FIFO_size(m2m);
|
||||
for (i = 0; i < size * ctx->channels; i++) {
|
||||
regmap_read(easrc->regmap, REG_EASRC_RDFIFO(index), ®);
|
||||
|
||||
if (reg32) {
|
||||
*(reg32) = reg;
|
||||
reg32++;
|
||||
} else if (reg16) {
|
||||
*(reg16) = (u16)reg;
|
||||
reg16++;
|
||||
} else {
|
||||
*reg24++ = (u8)reg;
|
||||
*reg24++ = (u8)(reg >> 8);
|
||||
*reg24++ = (u8)(reg >> 16);
|
||||
}
|
||||
}
|
||||
t_size += size;
|
||||
|
||||
if (size)
|
||||
goto retry;
|
||||
|
||||
if (reg32)
|
||||
output->length += t_size * ctx->channels * 4;
|
||||
else if (reg16)
|
||||
output->length += t_size * ctx->channels * 2;
|
||||
else
|
||||
output->length += t_size * ctx->channels * 3;
|
||||
}
|
||||
|
||||
static long fsl_easrc_process_buffer(struct fsl_easrc_m2m *m2m,
|
||||
struct asrc_convert_buffer *buf)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
unsigned long lock_flags;
|
||||
int ret;
|
||||
|
||||
/* Check input task first */
|
||||
ret = fsl_easrc_process_buffer_pre(m2m, IN);
|
||||
if (ret) {
|
||||
mxc_easrc_dma_umap_in(dev, m2m);
|
||||
if (m2m->dma_block[OUT].length > 0)
|
||||
mxc_easrc_dma_umap_out(dev, m2m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ...then output task*/
|
||||
if (m2m->dma_block[OUT].length > 0) {
|
||||
ret = fsl_easrc_process_buffer_pre(m2m, OUT);
|
||||
if (ret) {
|
||||
mxc_easrc_dma_umap_in(dev, m2m);
|
||||
mxc_easrc_dma_umap_out(dev, m2m);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
mxc_easrc_dma_umap_in(dev, m2m);
|
||||
if (m2m->dma_block[OUT].length > 0)
|
||||
mxc_easrc_dma_umap_out(dev, m2m);
|
||||
|
||||
spin_lock_irqsave(&m2m->lock, lock_flags);
|
||||
if (!m2m->ctx_hold) {
|
||||
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
||||
return -EFAULT;
|
||||
}
|
||||
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
||||
|
||||
/* Fetch the remaining data */
|
||||
fsl_easrc_read_last_FIFO(m2m);
|
||||
|
||||
/* Update final lengths after getting last FIFO */
|
||||
buf->input_buffer_length = m2m->dma_block[IN].length;
|
||||
buf->output_buffer_length = m2m->dma_block[OUT].length;
|
||||
|
||||
if (copy_to_user((void __user *)buf->output_buffer_vaddr,
|
||||
m2m->dma_block[OUT].dma_vaddr,
|
||||
m2m->dma_block[OUT].length))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fsl_easrc_submit_dma(struct fsl_easrc_m2m *m2m)
|
||||
{
|
||||
/* Submit DMA request */
|
||||
dmaengine_submit(m2m->desc[IN]);
|
||||
dma_async_issue_pending(m2m->desc[IN]->chan);
|
||||
|
||||
if (m2m->dma_block[OUT].length > 0) {
|
||||
dmaengine_submit(m2m->desc[OUT]);
|
||||
dma_async_issue_pending(m2m->desc[OUT]->chan);
|
||||
}
|
||||
}
|
||||
|
||||
static long fsl_easrc_ioctl_req_context(struct fsl_easrc_m2m *m2m,
|
||||
void __user *user)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
struct asrc_req req;
|
||||
unsigned long lock_flags;
|
||||
long ret;
|
||||
|
||||
ret = copy_from_user(&req, user, sizeof(req));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get req from user space:%ld\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = fsl_easrc_request_context(m2m->ctx, req.chn_num);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to request context:%ld\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* request context returns the context id in case of success */
|
||||
spin_lock_irqsave(&m2m->lock, lock_flags);
|
||||
m2m->ctx_hold = 1;
|
||||
req.index = m2m->ctx->index;
|
||||
req.supported_in_format = FSL_EASRC_FORMATS;
|
||||
req.supported_out_format = FSL_EASRC_FORMATS |
|
||||
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
|
||||
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
||||
|
||||
ret = copy_to_user(user, &req, sizeof(req));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to send req to user space: %ld\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long fsl_easrc_ioctl_config_context(struct fsl_easrc_m2m *m2m,
|
||||
void __user *user)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
enum asrc_pair_index index = m2m->ctx->index;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
struct asrc_config config;
|
||||
int ret;
|
||||
int in_word_size, out_word_size;
|
||||
|
||||
ret = copy_from_user(&config, user, sizeof(config));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get config from user space: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set context configuration parameters received from userspace */
|
||||
ctx->in_params.sample_rate = config.input_sample_rate;
|
||||
ctx->out_params.sample_rate = config.output_sample_rate;
|
||||
|
||||
ctx->in_params.fifo_wtmk = FSL_EASRC_INPUTFIFO_WML;
|
||||
ctx->out_params.fifo_wtmk = FSL_EASRC_OUTPUTFIFO_WML;
|
||||
|
||||
ctx->in_params.sample_format = config.input_format;
|
||||
ctx->out_params.sample_format = config.output_format;
|
||||
|
||||
ctx->channels = config.channel_num;
|
||||
ctx->rs_init_mode = 0x2;
|
||||
ctx->pf_init_mode = 0x2;
|
||||
|
||||
ret = fsl_easrc_set_ctx_format(ctx,
|
||||
&ctx->in_params.sample_format,
|
||||
&ctx->out_params.sample_format);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fsl_easrc_config_context(easrc, index);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to config context %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->in_params.iterations = 1;
|
||||
ctx->in_params.group_len = ctx->channels;
|
||||
ctx->in_params.access_len = ctx->channels;
|
||||
ctx->out_params.iterations = 1;
|
||||
ctx->out_params.group_len = ctx->channels;
|
||||
ctx->out_params.access_len = ctx->channels;
|
||||
|
||||
/* You can also call fsl_easrc_set_ctx_organziation for
|
||||
* sample interleaving support
|
||||
*/
|
||||
ret = fsl_easrc_set_ctx_organziation(ctx);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set fifo organization\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
in_word_size = snd_pcm_format_physical_width(config.input_format) / 8;
|
||||
out_word_size = snd_pcm_format_physical_width(config.output_format) / 8;
|
||||
|
||||
/* allocate dma buffers */
|
||||
m2m->dma_block[IN].length = EASRC_DMA_BUFFER_SIZE;
|
||||
m2m->dma_block[IN].max_buf_size = rounddown(EASRC_MAX_BUFFER_SIZE,
|
||||
in_word_size * ctx->channels);
|
||||
m2m->dma_block[OUT].length = EASRC_DMA_BUFFER_SIZE;
|
||||
m2m->dma_block[OUT].max_buf_size = rounddown(EASRC_MAX_BUFFER_SIZE,
|
||||
out_word_size * ctx->channels);
|
||||
|
||||
ret = fsl_allocate_dma_buf(m2m);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to allocate DMA buffers: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->dma_chan[IN] = fsl_easrc_get_dma_channel(ctx, IN);
|
||||
if (!ctx->dma_chan[IN]) {
|
||||
dev_err(dev, "[ctx%d] failed to get input DMA channel\n",
|
||||
m2m->ctx->index);
|
||||
return -EBUSY;
|
||||
}
|
||||
ctx->dma_chan[OUT] = fsl_easrc_get_dma_channel(ctx, OUT);
|
||||
if (!ctx->dma_chan[OUT]) {
|
||||
dev_err(dev, "[ctx%d] failed to get output DMA channel\n",
|
||||
m2m->ctx->index);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = copy_to_user(user, &config, sizeof(config));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to send config to user: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long fsl_easrc_ioctl_release_context(struct fsl_easrc_m2m *m2m,
|
||||
void __user *user)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
enum asrc_pair_index index;
|
||||
unsigned long lock_flags;
|
||||
int ret;
|
||||
|
||||
ret = copy_from_user(&index, user, sizeof(index));
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"[ctx%d] failed to get index from user space %d\n",
|
||||
m2m->ctx->index, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (index != m2m->ctx->index) {
|
||||
dev_err(dev,
|
||||
"[ctx%d] releasing wrong context - %d\n",
|
||||
m2m->ctx->index, index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (m2m->easrc_active) {
|
||||
m2m->easrc_active = 0;
|
||||
fsl_easrc_stop_context(ctx);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&m2m->lock, lock_flags);
|
||||
m2m->ctx_hold = 0;
|
||||
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
||||
|
||||
if (ctx->dma_chan[IN])
|
||||
dma_release_channel(ctx->dma_chan[IN]);
|
||||
if (ctx->dma_chan[OUT])
|
||||
dma_release_channel(ctx->dma_chan[OUT]);
|
||||
|
||||
ctx->dma_chan[IN] = NULL;
|
||||
ctx->dma_chan[OUT] = NULL;
|
||||
|
||||
/* free buffers allocated in config context*/
|
||||
kfree(m2m->dma_block[IN].dma_vaddr);
|
||||
kfree(m2m->dma_block[OUT].dma_vaddr);
|
||||
|
||||
fsl_easrc_release_context(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long fsl_easrc_ioctl_convert(struct fsl_easrc_m2m *m2m,
|
||||
void __user *user)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
struct asrc_convert_buffer buf;
|
||||
int ret;
|
||||
|
||||
ret = copy_from_user(&buf, user, sizeof(buf));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get buf from user space: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* fsl_easrc_calc_last_period_size(ctx, &buf); */
|
||||
ret = fsl_easrc_prepare_buffer(m2m, &buf);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to prepare buffer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&m2m->complete[IN]);
|
||||
init_completion(&m2m->complete[OUT]);
|
||||
|
||||
fsl_easrc_submit_dma(m2m);
|
||||
|
||||
if (m2m->first_convert) {
|
||||
fsl_easrc_start_context(ctx);
|
||||
m2m->first_convert = 0;
|
||||
}
|
||||
|
||||
ret = fsl_easrc_process_buffer(m2m, &buf);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to process buffer %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = copy_to_user(user, &buf, sizeof(buf));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to send buffer to user: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long fsl_easrc_ioctl_start_conv(struct fsl_easrc_m2m *m2m,
|
||||
void __user *user)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
enum asrc_pair_index index;
|
||||
int ret;
|
||||
|
||||
ret = copy_from_user(&index, user, sizeof(index));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get index from user space: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (index != m2m->ctx->index) {
|
||||
dev_err(dev, "[ctx%d] attempting to start wrong context%d\n",
|
||||
m2m->ctx->index, index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m2m->easrc_active = 1;
|
||||
m2m->first_convert = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long fsl_easrc_ioctl_stop_conv(struct fsl_easrc_m2m *m2m,
|
||||
void __user *user)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
enum asrc_pair_index index;
|
||||
int ret;
|
||||
|
||||
ret = copy_from_user(&index, user, sizeof(index));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get index from user space: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (index != m2m->ctx->index) {
|
||||
dev_err(dev, "[ctx%d] attempting to start wrong context%d\n",
|
||||
m2m->ctx->index, index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dmaengine_terminate_all(ctx->dma_chan[IN]);
|
||||
dmaengine_terminate_all(ctx->dma_chan[OUT]);
|
||||
|
||||
fsl_easrc_stop_context(ctx);
|
||||
m2m->easrc_active = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long fsl_easrc_ioctl_status(struct fsl_easrc_m2m *m2m,
|
||||
void __user *user)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
struct asrc_status_flags flags;
|
||||
int ret;
|
||||
|
||||
ret = copy_from_user(&flags, user, sizeof(flags));
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"[ctx%d] failed to get flags from user space: %d\n",
|
||||
m2m->ctx->index, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (m2m->ctx->index != flags.index) {
|
||||
dev_err(dev, "[ctx%d] getting status for other context: %d\n",
|
||||
m2m->ctx->index, flags.index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fsl_easrc_get_status(ctx, &flags);
|
||||
|
||||
ret = copy_to_user(user, &flags, sizeof(flags));
|
||||
if (ret)
|
||||
dev_err(dev, "[ctx%d] failed to send flags to user space\n",
|
||||
m2m->ctx->index);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long fsl_easrc_ioctl_flush(struct fsl_easrc_m2m *m2m,
|
||||
void __user *user)
|
||||
{
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
|
||||
/* Release DMA and request again */
|
||||
dma_release_channel(ctx->dma_chan[IN]);
|
||||
dma_release_channel(ctx->dma_chan[OUT]);
|
||||
|
||||
ctx->dma_chan[IN] = fsl_easrc_get_dma_channel(ctx, IN);
|
||||
if (!ctx->dma_chan[IN]) {
|
||||
dev_err(dev, "failed to request input task DMA channel\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ctx->dma_chan[OUT] = fsl_easrc_get_dma_channel(ctx, OUT);
|
||||
if (!ctx->dma_chan[OUT]) {
|
||||
dev_err(dev, "failed to request output task DMA channel\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_easrc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct miscdevice *easrc_miscdev = file->private_data;
|
||||
struct fsl_easrc *easrc = dev_get_drvdata(easrc_miscdev->parent);
|
||||
struct fsl_easrc_m2m *m2m;
|
||||
struct fsl_easrc_context *ctx;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
int ret;
|
||||
|
||||
ret = signal_pending(current);
|
||||
if (ret) {
|
||||
dev_err(dev, "current process has a signal pending\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
/* set the pointer to easrc private data */
|
||||
m2m = kzalloc(sizeof(*m2m), GFP_KERNEL);
|
||||
if (!m2m) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
/* just save the pointer to easrc private data */
|
||||
m2m->easrc = easrc;
|
||||
m2m->ctx = ctx;
|
||||
ctx->easrc = easrc;
|
||||
ctx->private_data = m2m;
|
||||
|
||||
spin_lock_init(&m2m->lock);
|
||||
|
||||
/* context structs are already allocated in fsl_easrc->ctx[i] */
|
||||
file->private_data = m2m;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
kfree(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_easrc_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct fsl_easrc_m2m *m2m = file->private_data;
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
struct fsl_easrc_context *ctx = m2m->ctx;
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
unsigned long lock_flags;
|
||||
|
||||
if (m2m->easrc_active) {
|
||||
m2m->easrc_active = 0;
|
||||
dmaengine_terminate_all(ctx->dma_chan[IN]);
|
||||
dmaengine_terminate_all(ctx->dma_chan[OUT]);
|
||||
|
||||
fsl_easrc_stop_context(ctx);
|
||||
fsl_easrc_input_dma_callback((void *)m2m);
|
||||
fsl_easrc_output_dma_callback((void *)m2m);
|
||||
}
|
||||
|
||||
if (!ctx)
|
||||
goto null_ctx;
|
||||
|
||||
spin_lock_irqsave(&m2m->lock, lock_flags);
|
||||
if (m2m->ctx_hold) {
|
||||
m2m->ctx_hold = 0;
|
||||
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
||||
|
||||
if (ctx->dma_chan[IN])
|
||||
dma_release_channel(ctx->dma_chan[IN]);
|
||||
if (ctx->dma_chan[OUT])
|
||||
dma_release_channel(ctx->dma_chan[OUT]);
|
||||
|
||||
kfree(m2m->dma_block[IN].dma_vaddr);
|
||||
kfree(m2m->dma_block[OUT].dma_vaddr);
|
||||
|
||||
fsl_easrc_release_context(ctx);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&m2m->lock, lock_flags);
|
||||
}
|
||||
|
||||
null_ctx:
|
||||
spin_lock_irqsave(&easrc->lock, lock_flags);
|
||||
kfree(m2m);
|
||||
kfree(ctx);
|
||||
file->private_data = NULL;
|
||||
spin_unlock_irqrestore(&easrc->lock, lock_flags);
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long fsl_easrc_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct fsl_easrc_m2m *m2m = file->private_data;
|
||||
struct fsl_easrc *easrc = m2m->easrc;
|
||||
void __user *user = (void __user *)arg;
|
||||
long ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case ASRC_REQ_PAIR:
|
||||
ret = fsl_easrc_ioctl_req_context(m2m, user);
|
||||
break;
|
||||
case ASRC_CONFIG_PAIR:
|
||||
ret = fsl_easrc_ioctl_config_context(m2m, user);
|
||||
break;
|
||||
case ASRC_RELEASE_PAIR:
|
||||
ret = fsl_easrc_ioctl_release_context(m2m, user);
|
||||
break;
|
||||
case ASRC_CONVERT:
|
||||
ret = fsl_easrc_ioctl_convert(m2m, user);
|
||||
break;
|
||||
case ASRC_START_CONV:
|
||||
ret = fsl_easrc_ioctl_start_conv(m2m, user);
|
||||
break;
|
||||
case ASRC_STOP_CONV:
|
||||
ret = fsl_easrc_ioctl_stop_conv(m2m, user);
|
||||
break;
|
||||
case ASRC_STATUS:
|
||||
ret = fsl_easrc_ioctl_status(m2m, user);
|
||||
break;
|
||||
case ASRC_FLUSH:
|
||||
ret = fsl_easrc_ioctl_flush(m2m, user);
|
||||
break;
|
||||
default:
|
||||
dev_err(&easrc->pdev->dev, "invalid ioctl command\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations easrc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = fsl_easrc_ioctl,
|
||||
.open = fsl_easrc_open,
|
||||
.release = fsl_easrc_close,
|
||||
};
|
||||
|
||||
static int fsl_easrc_m2m_init(struct fsl_easrc *easrc)
|
||||
{
|
||||
struct device *dev = &easrc->pdev->dev;
|
||||
int ret;
|
||||
|
||||
easrc->easrc_miscdev.fops = &easrc_fops;
|
||||
easrc->easrc_miscdev.parent = dev;
|
||||
easrc->easrc_miscdev.name = easrc->name;
|
||||
easrc->easrc_miscdev.minor = MISC_DYNAMIC_MINOR;
|
||||
ret = misc_register(&easrc->easrc_miscdev);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to register char device %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void fsl_easrc_m2m_suspend(struct fsl_easrc *easrc)
|
||||
{
|
||||
struct fsl_easrc_context *ctx;
|
||||
struct fsl_easrc_m2m *m2m;
|
||||
unsigned long lock_flags;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < EASRC_CTX_MAX_NUM; i++) {
|
||||
spin_lock_irqsave(&easrc->lock, lock_flags);
|
||||
ctx = easrc->ctx[i];
|
||||
if (!ctx || !ctx->private_data) {
|
||||
spin_unlock_irqrestore(&easrc->lock, lock_flags);
|
||||
continue;
|
||||
}
|
||||
m2m = ctx->private_data;
|
||||
|
||||
if (!completion_done(&m2m->complete[IN])) {
|
||||
if (ctx->dma_chan[IN])
|
||||
dmaengine_terminate_all(ctx->dma_chan[IN]);
|
||||
fsl_easrc_input_dma_callback((void *)m2m);
|
||||
}
|
||||
if (!completion_done(&m2m->complete[OUT])) {
|
||||
if (ctx->dma_chan[OUT])
|
||||
dmaengine_terminate_all(ctx->dma_chan[OUT]);
|
||||
fsl_easrc_output_dma_callback((void *)m2m);
|
||||
}
|
||||
|
||||
m2m->first_convert = 1;
|
||||
fsl_easrc_stop_context(ctx);
|
||||
spin_unlock_irqrestore(&easrc->lock, lock_flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_easrc_m2m_resume(struct fsl_easrc *easrc)
|
||||
{
|
||||
/* null */
|
||||
}
|
||||
#endif
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
|
@ -21,6 +22,19 @@
|
|||
SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE)
|
||||
|
||||
/**
|
||||
* fsl_esai_soc_data: soc specific data
|
||||
*
|
||||
* @imx: for imx platform
|
||||
* @reset_at_xrun: flags for enable reset operaton
|
||||
* @use_edma: edma is used.
|
||||
*/
|
||||
struct fsl_esai_soc_data {
|
||||
bool imx;
|
||||
bool reset_at_xrun;
|
||||
bool use_edma;
|
||||
};
|
||||
|
||||
/**
|
||||
* fsl_esai: ESAI private data
|
||||
*
|
||||
|
@ -32,7 +46,8 @@
|
|||
* @extalclk: esai clock source to derive HCK, SCK and FS
|
||||
* @fsysclk: system clock source to derive HCK, SCK and FS
|
||||
* @spbaclk: SPBA clock (optional, depending on SoC design)
|
||||
* @task: tasklet to handle the reset operation
|
||||
* @soc: soc specific data
|
||||
* @lock: spin lock between hw_reset() and trigger()
|
||||
* @fifo_depth: depth of tx/rx FIFO
|
||||
* @slot_width: width of each DAI slot
|
||||
* @slots: number of slots
|
||||
|
@ -43,7 +58,6 @@
|
|||
* @sck_div: if using PSR/PM dividers for SCKx clock
|
||||
* @slave_mode: if fully using DAI slave mode
|
||||
* @synchronous: if using tx/rx synchronous mode
|
||||
* @reset_at_xrun: flags for enable reset operaton
|
||||
* @name: driver name
|
||||
*/
|
||||
struct fsl_esai {
|
||||
|
@ -55,7 +69,8 @@ struct fsl_esai {
|
|||
struct clk *extalclk;
|
||||
struct clk *fsysclk;
|
||||
struct clk *spbaclk;
|
||||
struct tasklet_struct task;
|
||||
const struct fsl_esai_soc_data *soc;
|
||||
spinlock_t lock; /* Protect hw_reset and trigger */
|
||||
u32 fifo_depth;
|
||||
u32 slot_width;
|
||||
u32 slots;
|
||||
|
@ -66,12 +81,37 @@ struct fsl_esai {
|
|||
u32 sck_rate[2];
|
||||
bool hck_dir[2];
|
||||
bool sck_div[2];
|
||||
bool slave_mode;
|
||||
bool slave_mode[2];
|
||||
bool synchronous;
|
||||
bool reset_at_xrun;
|
||||
char name[32];
|
||||
};
|
||||
|
||||
static struct fsl_esai_soc_data fsl_esai_vf610 = {
|
||||
.imx = false,
|
||||
.reset_at_xrun = true,
|
||||
.use_edma = false,
|
||||
};
|
||||
|
||||
static struct fsl_esai_soc_data fsl_esai_imx35 = {
|
||||
.imx = true,
|
||||
.reset_at_xrun = true,
|
||||
.use_edma = false,
|
||||
};
|
||||
|
||||
static struct fsl_esai_soc_data fsl_esai_imx6ull = {
|
||||
.imx = true,
|
||||
.reset_at_xrun = false,
|
||||
.use_edma = false,
|
||||
};
|
||||
|
||||
static struct fsl_esai_soc_data fsl_esai_imx8qm = {
|
||||
.imx = true,
|
||||
.reset_at_xrun = false,
|
||||
.use_edma = true,
|
||||
};
|
||||
|
||||
static void fsl_esai_hw_reset(struct fsl_esai *esai_priv);
|
||||
|
||||
static irqreturn_t esai_isr(int irq, void *devid)
|
||||
{
|
||||
struct fsl_esai *esai_priv = (struct fsl_esai *)devid;
|
||||
|
@ -83,19 +123,19 @@ static irqreturn_t esai_isr(int irq, void *devid)
|
|||
regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);
|
||||
|
||||
if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) &&
|
||||
esai_priv->reset_at_xrun) {
|
||||
esai_priv->soc->reset_at_xrun) {
|
||||
dev_dbg(&pdev->dev, "reset module for xrun\n");
|
||||
tasklet_schedule(&esai_priv->task);
|
||||
fsl_esai_hw_reset(esai_priv);
|
||||
}
|
||||
|
||||
if (esr & ESAI_ESR_TINIT_MASK)
|
||||
dev_dbg(&pdev->dev, "isr: Transmission Initialized\n");
|
||||
|
||||
if (esr & ESAI_ESR_RFF_MASK)
|
||||
dev_warn(&pdev->dev, "isr: Receiving overrun\n");
|
||||
dev_dbg(&pdev->dev, "isr: Receiving overrun\n");
|
||||
|
||||
if (esr & ESAI_ESR_TFE_MASK)
|
||||
dev_warn(&pdev->dev, "isr: Transmission underrun\n");
|
||||
dev_dbg(&pdev->dev, "isr: Transmission underrun\n");
|
||||
|
||||
if (esr & ESAI_ESR_TLS_MASK)
|
||||
dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n");
|
||||
|
@ -336,7 +376,7 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
|
|||
int ret;
|
||||
|
||||
/* Don't apply for fully slave mode or unchanged bclk */
|
||||
if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq)
|
||||
if (esai_priv->slave_mode[tx] || esai_priv->sck_rate[tx] == freq)
|
||||
return 0;
|
||||
|
||||
if (ratio * freq > hck_rate)
|
||||
|
@ -445,35 +485,62 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
esai_priv->slave_mode = false;
|
||||
if (esai_priv->slave_mode[0] == esai_priv->slave_mode[1]) {
|
||||
/* DAI clock master masks */
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
esai_priv->slave_mode[0] = true;
|
||||
esai_priv->slave_mode[1] = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
xccr |= ESAI_xCCR_xCKD;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
xccr |= ESAI_xCCR_xFSD;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
|
||||
esai_priv->slave_mode[0] = false;
|
||||
esai_priv->slave_mode[1] = false;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* DAI clock master masks */
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
esai_priv->slave_mode = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
xccr |= ESAI_xCCR_xCKD;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
xccr |= ESAI_xCCR_xFSD;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP |
|
||||
ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
|
||||
regmap_update_bits(esai_priv->regmap,
|
||||
REG_ESAI_TCCR, mask, xccr);
|
||||
regmap_update_bits(esai_priv->regmap,
|
||||
REG_ESAI_RCCR, mask, xccr);
|
||||
|
||||
} else {
|
||||
|
||||
mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP |
|
||||
ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
|
||||
if (esai_priv->slave_mode[0])
|
||||
regmap_update_bits(esai_priv->regmap,
|
||||
REG_ESAI_RCCR, mask, xccr);
|
||||
else
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
|
||||
mask,
|
||||
xccr | ESAI_xCCR_xFSD |
|
||||
ESAI_xCCR_xCKD);
|
||||
|
||||
if (esai_priv->slave_mode[1])
|
||||
regmap_update_bits(esai_priv->regmap,
|
||||
REG_ESAI_TCCR, mask, xccr);
|
||||
else
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
|
||||
mask,
|
||||
xccr | ESAI_xCCR_xFSD |
|
||||
ESAI_xCCR_xCKD);
|
||||
}
|
||||
|
||||
mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR | ESAI_xCR_xWA;
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr);
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr);
|
||||
|
||||
mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP |
|
||||
ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr);
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -481,6 +548,7 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
|
||||
if (!dai->active) {
|
||||
/* Set synchronous mode */
|
||||
|
@ -495,6 +563,12 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
|
|||
ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
|
||||
}
|
||||
|
||||
if (esai_priv->soc->use_edma)
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
tx ? esai_priv->dma_params_tx.maxburst :
|
||||
esai_priv->dma_params_rx.maxburst);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
@ -672,12 +746,13 @@ static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx)
|
|||
ESAI_xFCR_xFR, 0);
|
||||
}
|
||||
|
||||
static void fsl_esai_hw_reset(unsigned long arg)
|
||||
static void fsl_esai_hw_reset(struct fsl_esai *esai_priv)
|
||||
{
|
||||
struct fsl_esai *esai_priv = (struct fsl_esai *)arg;
|
||||
bool tx = true, rx = false, enabled[2];
|
||||
unsigned long lock_flags;
|
||||
u32 tfcr, rfcr;
|
||||
|
||||
spin_lock_irqsave(&esai_priv->lock, lock_flags);
|
||||
/* Save the registers */
|
||||
regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
|
||||
regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
|
||||
|
@ -715,6 +790,8 @@ static void fsl_esai_hw_reset(unsigned long arg)
|
|||
fsl_esai_trigger_start(esai_priv, tx);
|
||||
if (enabled[rx])
|
||||
fsl_esai_trigger_start(esai_priv, rx);
|
||||
|
||||
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
|
||||
}
|
||||
|
||||
static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
|
@ -722,6 +799,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
{
|
||||
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
unsigned long lock_flags;
|
||||
|
||||
esai_priv->channels[tx] = substream->runtime->channels;
|
||||
|
||||
|
@ -729,12 +807,16 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
spin_lock_irqsave(&esai_priv->lock, lock_flags);
|
||||
fsl_esai_trigger_start(esai_priv, tx);
|
||||
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
spin_lock_irqsave(&esai_priv->lock, lock_flags);
|
||||
fsl_esai_trigger_stop(esai_priv, tx);
|
||||
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -913,6 +995,8 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
|||
const __be32 *iprop;
|
||||
void __iomem *regs;
|
||||
int irq, ret;
|
||||
unsigned long irqflag = 0;
|
||||
int i, num_domains = 0;
|
||||
|
||||
esai_priv = devm_kzalloc(&pdev->dev, sizeof(*esai_priv), GFP_KERNEL);
|
||||
if (!esai_priv)
|
||||
|
@ -921,9 +1005,11 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
|||
esai_priv->pdev = pdev;
|
||||
snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np);
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,vf610-esai") ||
|
||||
of_device_is_compatible(np, "fsl,imx35-esai"))
|
||||
esai_priv->reset_at_xrun = true;
|
||||
esai_priv->soc = of_device_get_match_data(&pdev->dev);
|
||||
if (!esai_priv->soc) {
|
||||
dev_err(&pdev->dev, "failed to get soc data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Get the addresses and IRQ */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -962,10 +1048,34 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
|||
PTR_ERR(esai_priv->spbaclk));
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, esai_isr, 0,
|
||||
num_domains = of_count_phandle_with_args(np, "power-domains",
|
||||
"#power-domain-cells");
|
||||
for (i = 0; i < num_domains; i++) {
|
||||
struct device *pd_dev;
|
||||
struct device_link *link;
|
||||
|
||||
pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i);
|
||||
if (IS_ERR(pd_dev))
|
||||
return PTR_ERR(pd_dev);
|
||||
|
||||
link = device_link_add(&pdev->dev, pd_dev,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (IS_ERR(link))
|
||||
return PTR_ERR(link);
|
||||
}
|
||||
|
||||
/* ESAI shared interrupt */
|
||||
if (of_property_read_bool(np, "shared-interrupt"))
|
||||
irqflag = IRQF_SHARED;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, esai_isr, irqflag,
|
||||
esai_priv->name, esai_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to claim irq %u\n", irq);
|
||||
|
@ -975,9 +1085,6 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
|||
/* Set a default slot number */
|
||||
esai_priv->slots = 2;
|
||||
|
||||
/* Set a default master/slave state */
|
||||
esai_priv->slave_mode = true;
|
||||
|
||||
/* Determine the FIFO depth */
|
||||
iprop = of_get_property(np, "fsl,fifo-depth", NULL);
|
||||
if (iprop)
|
||||
|
@ -993,6 +1100,20 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
|||
esai_priv->synchronous =
|
||||
of_property_read_bool(np, "fsl,esai-synchronous");
|
||||
|
||||
if (!esai_priv->synchronous) {
|
||||
if (of_property_read_bool(pdev->dev.of_node, "fsl,txm-rxs")) {
|
||||
/* 0 -- rx, 1 -- tx */
|
||||
esai_priv->slave_mode[0] = true;
|
||||
esai_priv->slave_mode[1] = false;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "fsl,txs-rxm")) {
|
||||
/* 0 -- rx, 1 -- tx */
|
||||
esai_priv->slave_mode[0] = false;
|
||||
esai_priv->slave_mode[1] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Implement full symmetry for synchronous mode */
|
||||
if (esai_priv->synchronous) {
|
||||
fsl_esai_dai.symmetric_rates = 1;
|
||||
|
@ -1002,6 +1123,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
|||
|
||||
dev_set_drvdata(&pdev->dev, esai_priv);
|
||||
|
||||
spin_lock_init(&esai_priv->lock);
|
||||
ret = fsl_esai_hw_init(esai_priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1022,9 +1144,6 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
tasklet_init(&esai_priv->task, fsl_esai_hw_reset,
|
||||
(unsigned long)esai_priv);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
regcache_cache_only(esai_priv->regmap, true);
|
||||
|
@ -1038,18 +1157,16 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
|||
|
||||
static int fsl_esai_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_esai *esai_priv = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
tasklet_kill(&esai_priv->task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id fsl_esai_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx35-esai", },
|
||||
{ .compatible = "fsl,vf610-esai", },
|
||||
{ .compatible = "fsl,imx6ull-esai", },
|
||||
{ .compatible = "fsl,imx35-esai", .data = &fsl_esai_imx35 },
|
||||
{ .compatible = "fsl,vf610-esai", .data = &fsl_esai_vf610 },
|
||||
{ .compatible = "fsl,imx6ull-esai", .data = &fsl_esai_imx6ull },
|
||||
{ .compatible = "fsl,imx8qm-esai", .data = &fsl_esai_imx8qm },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
|
||||
|
|
750
sound/soc/fsl/fsl_hdmi.c
Normal file
750
sound/soc/fsl/fsl_hdmi.c
Normal file
|
@ -0,0 +1,750 @@
|
|||
/*
|
||||
* ALSA SoC HDMI Audio Layer for Freescale i.MX
|
||||
*
|
||||
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Some code from patch_hdmi.c
|
||||
* Copyright (c) 2008-2010 Intel Corporation. All rights reserved.
|
||||
* Copyright (c) 2006 ATI Technologies Inc.
|
||||
* Copyright (c) 2008 NVIDIA Corp. All rights reserved.
|
||||
* Copyright (c) 2008 Wei Ni <wni@nvidia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/mxc-hdmi-core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/hdmi-codec.h>
|
||||
|
||||
#include <video/mxc_hdmi.h>
|
||||
|
||||
#include "imx-hdmi.h"
|
||||
|
||||
|
||||
static struct mxc_edid_cfg edid_cfg;
|
||||
|
||||
static u32 playback_rates[HDMI_MAX_RATES];
|
||||
static u32 playback_sample_size[HDMI_MAX_SAMPLE_SIZE];
|
||||
static u32 playback_channels[HDMI_MAX_CHANNEL_CONSTRAINTS];
|
||||
|
||||
static struct snd_pcm_hw_constraint_list playback_constraint_rates;
|
||||
static struct snd_pcm_hw_constraint_list playback_constraint_bits;
|
||||
static struct snd_pcm_hw_constraint_list playback_constraint_channels;
|
||||
|
||||
#ifdef DEBUG
|
||||
static void dumpregs(struct snd_soc_dai *dai)
|
||||
{
|
||||
u32 n, cts;
|
||||
|
||||
cts = (hdmi_readb(HDMI_AUD_CTS3) << 16) |
|
||||
(hdmi_readb(HDMI_AUD_CTS2) << 8) |
|
||||
hdmi_readb(HDMI_AUD_CTS1);
|
||||
|
||||
n = (hdmi_readb(HDMI_AUD_N3) << 16) |
|
||||
(hdmi_readb(HDMI_AUD_N2) << 8) |
|
||||
hdmi_readb(HDMI_AUD_N1);
|
||||
|
||||
dev_dbg(dai->dev, "HDMI_PHY_CONF0 0x%02x\n",
|
||||
hdmi_readb(HDMI_PHY_CONF0));
|
||||
dev_dbg(dai->dev, "HDMI_MC_CLKDIS 0x%02x\n",
|
||||
hdmi_readb(HDMI_MC_CLKDIS));
|
||||
dev_dbg(dai->dev, "HDMI_AUD_N[1-3] 0x%06x (%d)\n",
|
||||
n, n);
|
||||
dev_dbg(dai->dev, "HDMI_AUD_CTS[1-3] 0x%06x (%d)\n",
|
||||
cts, cts);
|
||||
dev_dbg(dai->dev, "HDMI_FC_AUDSCONF 0x%02x\n",
|
||||
hdmi_readb(HDMI_FC_AUDSCONF));
|
||||
}
|
||||
#else
|
||||
static void dumpregs(struct snd_soc_dai *dai) {}
|
||||
#endif
|
||||
|
||||
enum cea_speaker_placement {
|
||||
FL = (1 << 0), /* Front Left */
|
||||
FC = (1 << 1), /* Front Center */
|
||||
FR = (1 << 2), /* Front Right */
|
||||
FLC = (1 << 3), /* Front Left Center */
|
||||
FRC = (1 << 4), /* Front Right Center */
|
||||
RL = (1 << 5), /* Rear Left */
|
||||
RC = (1 << 6), /* Rear Center */
|
||||
RR = (1 << 7), /* Rear Right */
|
||||
RLC = (1 << 8), /* Rear Left Center */
|
||||
RRC = (1 << 9), /* Rear Right Center */
|
||||
LFE = (1 << 10), /* Low Frequency Effect */
|
||||
FLW = (1 << 11), /* Front Left Wide */
|
||||
FRW = (1 << 12), /* Front Right Wide */
|
||||
FLH = (1 << 13), /* Front Left High */
|
||||
FCH = (1 << 14), /* Front Center High */
|
||||
FRH = (1 << 15), /* Front Right High */
|
||||
TC = (1 << 16), /* Top Center */
|
||||
};
|
||||
|
||||
/*
|
||||
* EDID SA bits in the CEA Speaker Allocation data block
|
||||
*/
|
||||
static int edid_speaker_allocation_bits[] = {
|
||||
[0] = FL | FR,
|
||||
[1] = LFE,
|
||||
[2] = FC,
|
||||
[3] = RL | RR,
|
||||
[4] = RC,
|
||||
[5] = FLC | FRC,
|
||||
[6] = RLC | RRC,
|
||||
[7] = FLW | FRW,
|
||||
[8] = FLH | FRH,
|
||||
[9] = TC,
|
||||
[10] = FCH,
|
||||
};
|
||||
|
||||
struct cea_channel_speaker_allocation {
|
||||
int ca_index;
|
||||
int speakers[8];
|
||||
|
||||
/* Derived values, just for convenience */
|
||||
int channels;
|
||||
int spk_mask;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is an ordered list!
|
||||
*
|
||||
* The preceding ones have better chances to be selected by
|
||||
* hdmi_channel_allocation().
|
||||
*/
|
||||
static struct cea_channel_speaker_allocation channel_allocations[] = {
|
||||
/* channel: 7 6 5 4 3 2 1 0 */
|
||||
{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL },},
|
||||
/* 2.1 */
|
||||
{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL },},
|
||||
/* Dolby Surround */
|
||||
{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL },},
|
||||
{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL },},
|
||||
{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL },},
|
||||
{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL },},
|
||||
{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL },},
|
||||
/* surround51 */
|
||||
{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL },},
|
||||
{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL },},
|
||||
{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL },},
|
||||
/* 6.1 */
|
||||
{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL },},
|
||||
{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL },},
|
||||
{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL },},
|
||||
/* surround71 */
|
||||
{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL },},
|
||||
{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL },},
|
||||
{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL },},
|
||||
{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL },},
|
||||
{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL },},
|
||||
{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL },},
|
||||
{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL },},
|
||||
{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL },},
|
||||
{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL },},
|
||||
{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL },},
|
||||
{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL },},
|
||||
{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL },},
|
||||
{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL },},
|
||||
};
|
||||
|
||||
/* Compute derived values in channel_allocations[] */
|
||||
static void init_channel_allocations(void)
|
||||
{
|
||||
struct cea_channel_speaker_allocation *p;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
|
||||
p = channel_allocations + i;
|
||||
p->channels = 0;
|
||||
p->spk_mask = 0;
|
||||
for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
|
||||
if (p->speakers[j]) {
|
||||
p->channels++;
|
||||
p->spk_mask |= p->speakers[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The transformation takes two steps:
|
||||
*
|
||||
* speaker_alloc => (edid_speaker_allocation_bits[]) => spk_mask
|
||||
* spk_mask => (channel_allocations[]) => CA
|
||||
*
|
||||
* TODO: it could select the wrong CA from multiple candidates.
|
||||
*/
|
||||
static int hdmi_channel_allocation(int channels)
|
||||
{
|
||||
int spk_mask = 0, ca = 0, i, tmpchn, tmpspk;
|
||||
|
||||
/* CA defaults to 0 for basic stereo audio */
|
||||
if (channels <= 2)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Expand EDID's speaker allocation mask
|
||||
*
|
||||
* EDID tells the speaker mask in a compact(paired) form,
|
||||
* expand EDID's notions to match the ones used by Audio InfoFrame.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(edid_speaker_allocation_bits); i++) {
|
||||
if (edid_cfg.speaker_alloc & (1 << i))
|
||||
spk_mask |= edid_speaker_allocation_bits[i];
|
||||
}
|
||||
|
||||
/* Search for the first working match in the CA table */
|
||||
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
|
||||
tmpchn = channel_allocations[i].channels;
|
||||
tmpspk = channel_allocations[i].spk_mask;
|
||||
|
||||
if (channels == tmpchn && (spk_mask & tmpspk) == tmpspk) {
|
||||
ca = channel_allocations[i].ca_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ca;
|
||||
}
|
||||
|
||||
static void hdmi_set_audio_infoframe(unsigned int channels)
|
||||
{
|
||||
u8 audiconf0, audiconf2;
|
||||
|
||||
/*
|
||||
* From CEA-861-D spec:
|
||||
* HDMI requires the CT, SS and SF fields to be set to 0 ("Refer
|
||||
* to Stream Header") as these items are carried in the audio stream.
|
||||
*
|
||||
* So we only set the CC and CA fields.
|
||||
*/
|
||||
audiconf0 = ((channels - 1) << HDMI_FC_AUDICONF0_CC_OFFSET) &
|
||||
HDMI_FC_AUDICONF0_CC_MASK;
|
||||
|
||||
audiconf2 = hdmi_channel_allocation(channels);
|
||||
|
||||
hdmi_writeb(audiconf0, HDMI_FC_AUDICONF0);
|
||||
hdmi_writeb(0, HDMI_FC_AUDICONF1);
|
||||
hdmi_writeb(audiconf2, HDMI_FC_AUDICONF2);
|
||||
hdmi_writeb(0, HDMI_FC_AUDICONF3);
|
||||
}
|
||||
|
||||
static int cea_audio_rates[HDMI_MAX_RATES] = {
|
||||
32000, 44100, 48000, 88200, 96000, 176400, 192000,
|
||||
};
|
||||
|
||||
static void fsl_hdmi_get_playback_rates(void)
|
||||
{
|
||||
int i, count = 0;
|
||||
u8 rates;
|
||||
|
||||
/* Always assume basic audio support */
|
||||
rates = edid_cfg.sample_rates | 0x7;
|
||||
|
||||
for (i = 0 ; i < HDMI_MAX_RATES ; i++)
|
||||
if ((rates & (1 << i)) != 0)
|
||||
playback_rates[count++] = cea_audio_rates[i];
|
||||
|
||||
playback_constraint_rates.list = playback_rates;
|
||||
playback_constraint_rates.count = count;
|
||||
|
||||
for (i = 0 ; i < playback_constraint_rates.count ; i++)
|
||||
pr_debug("%s: constraint = %d Hz\n", __func__, playback_rates[i]);
|
||||
}
|
||||
|
||||
static void fsl_hdmi_get_playback_sample_size(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
/* Always assume basic audio support */
|
||||
playback_sample_size[i++] = 16;
|
||||
|
||||
if (edid_cfg.sample_sizes & 0x4)
|
||||
playback_sample_size[i++] = 24;
|
||||
|
||||
playback_constraint_bits.list = playback_sample_size;
|
||||
playback_constraint_bits.count = i;
|
||||
|
||||
for (i = 0 ; i < playback_constraint_bits.count ; i++)
|
||||
pr_debug("%s: constraint = %d bits\n", __func__, playback_sample_size[i]);
|
||||
}
|
||||
|
||||
static void fsl_hdmi_get_playback_channels(void)
|
||||
{
|
||||
int channels = 2, i = 0;
|
||||
|
||||
/* Always assume basic audio support */
|
||||
playback_channels[i++] = channels;
|
||||
channels += 2;
|
||||
|
||||
while ((i < HDMI_MAX_CHANNEL_CONSTRAINTS) &&
|
||||
(channels <= edid_cfg.max_channels)) {
|
||||
playback_channels[i++] = channels;
|
||||
channels += 2;
|
||||
}
|
||||
|
||||
playback_constraint_channels.list = playback_channels;
|
||||
playback_constraint_channels.count = i;
|
||||
|
||||
for (i = 0 ; i < playback_constraint_channels.count ; i++)
|
||||
pr_debug("%s: constraint = %d channels\n", __func__, playback_channels[i]);
|
||||
}
|
||||
|
||||
static int fsl_hdmi_update_constraints(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int ret;
|
||||
|
||||
hdmi_get_edid_cfg(&edid_cfg);
|
||||
|
||||
fsl_hdmi_get_playback_rates();
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
&playback_constraint_rates);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fsl_hdmi_get_playback_sample_size();
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
|
||||
&playback_constraint_bits);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fsl_hdmi_get_playback_channels();
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&playback_constraint_channels);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_hdmi_soc_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct imx_hdmi *hdmi_data = snd_soc_dai_get_drvdata(dai);
|
||||
int ret;
|
||||
|
||||
clk_prepare_enable(hdmi_data->mipi_core_clk);
|
||||
clk_prepare_enable(hdmi_data->isfr_clk);
|
||||
clk_prepare_enable(hdmi_data->iahb_clk);
|
||||
|
||||
dev_dbg(dai->dev, "%s hdmi clks: mipi_core: %d isfr:%d iahb:%d\n", __func__,
|
||||
(int)clk_get_rate(hdmi_data->mipi_core_clk),
|
||||
(int)clk_get_rate(hdmi_data->isfr_clk),
|
||||
(int)clk_get_rate(hdmi_data->iahb_clk));
|
||||
|
||||
ret = fsl_hdmi_update_constraints(substream);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Indicates the subpacket represents a flatline sample */
|
||||
hdmi_audio_writeb(FC_AUDSCONF, AUD_PACKET_SAMPFIT, 0x0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_hdmi_soc_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct imx_hdmi *hdmi_data = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
clk_disable_unprepare(hdmi_data->iahb_clk);
|
||||
clk_disable_unprepare(hdmi_data->isfr_clk);
|
||||
clk_disable_unprepare(hdmi_data->mipi_core_clk);
|
||||
}
|
||||
|
||||
static int fsl_hdmi_soc_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
hdmi_set_audio_infoframe(runtime->channels);
|
||||
hdmi_audio_writeb(FC_AUDSCONF, AUD_PACKET_LAYOUT,
|
||||
(runtime->channels > 2) ? 0x1 : 0x0);
|
||||
hdmi_set_sample_rate(runtime->rate);
|
||||
dumpregs(dai);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops fsl_hdmi_soc_dai_ops = {
|
||||
.startup = fsl_hdmi_soc_startup,
|
||||
.shutdown = fsl_hdmi_soc_shutdown,
|
||||
.prepare = fsl_hdmi_soc_prepare,
|
||||
};
|
||||
|
||||
/* IEC60958 status functions */
|
||||
static int fsl_hdmi_iec_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
uinfo->count = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int fsl_hdmi_iec_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < 4 ; i++)
|
||||
uvalue->value.iec958.status[i] = iec_header.status[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_hdmi_iec_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Do not allow professional mode */
|
||||
if (uvalue->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL)
|
||||
return -EPERM;
|
||||
|
||||
for (i = 0 ; i < 4 ; i++) {
|
||||
iec_header.status[i] = uvalue->value.iec958.status[i];
|
||||
pr_debug("%s status[%d]=0x%02x\n", __func__, i, iec_header.status[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_hdmi_channels_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
hdmi_get_edid_cfg(&edid_cfg);
|
||||
fsl_hdmi_get_playback_channels();
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = playback_constraint_channels.count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int fsl_hdmi_channels_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
int i;
|
||||
hdmi_get_edid_cfg(&edid_cfg);
|
||||
fsl_hdmi_get_playback_channels();
|
||||
|
||||
for (i = 0 ; i < playback_constraint_channels.count ; i++)
|
||||
uvalue->value.integer.value[i] = playback_channels[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_hdmi_rates_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
hdmi_get_edid_cfg(&edid_cfg);
|
||||
fsl_hdmi_get_playback_rates();
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = playback_constraint_rates.count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_hdmi_rates_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
int i;
|
||||
hdmi_get_edid_cfg(&edid_cfg);
|
||||
fsl_hdmi_get_playback_rates();
|
||||
|
||||
for (i = 0 ; i < playback_constraint_rates.count ; i++)
|
||||
uvalue->value.integer.value[i] = playback_rates[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_hdmi_formats_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
hdmi_get_edid_cfg(&edid_cfg);
|
||||
fsl_hdmi_get_playback_sample_size();
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = playback_constraint_bits.count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_hdmi_formats_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
int i;
|
||||
hdmi_get_edid_cfg(&edid_cfg);
|
||||
fsl_hdmi_get_playback_sample_size();
|
||||
|
||||
for (i = 0 ; i < playback_constraint_bits.count ; i++)
|
||||
uvalue->value.integer.value[i] = playback_sample_size[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new fsl_hdmi_ctrls[] = {
|
||||
/* Status cchanel controller */
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_WRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = fsl_hdmi_iec_info,
|
||||
.get = fsl_hdmi_iec_get,
|
||||
.put = fsl_hdmi_iec_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HDMI Support Channels",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = fsl_hdmi_channels_info,
|
||||
.get = fsl_hdmi_channels_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HDMI Support Rates",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = fsl_hdmi_rates_info,
|
||||
.get = fsl_hdmi_rates_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HDMI Support Formats",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = fsl_hdmi_formats_info,
|
||||
.get = fsl_hdmi_formats_get,
|
||||
},
|
||||
};
|
||||
|
||||
static int fsl_hdmi_soc_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret;
|
||||
|
||||
init_channel_allocations();
|
||||
|
||||
ret = snd_soc_add_dai_controls(dai, fsl_hdmi_ctrls,
|
||||
ARRAY_SIZE(fsl_hdmi_ctrls));
|
||||
if (ret)
|
||||
dev_warn(dai->dev, "failed to add dai controls\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver fsl_hdmi_dai = {
|
||||
.probe = &fsl_hdmi_soc_dai_probe,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.rates = MXC_HDMI_RATES_PLAYBACK,
|
||||
.formats = MXC_HDMI_FORMATS_PLAYBACK,
|
||||
},
|
||||
.ops = &fsl_hdmi_soc_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver fsl_hdmi_component = {
|
||||
.name = "fsl-hdmi",
|
||||
};
|
||||
|
||||
/* HDMI audio codec callbacks */
|
||||
static int fsl_hdmi_audio_hw_params(struct device *dev, void *data,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
dev_dbg(dev, "[%s]: %u Hz, %d bit, %d channels\n", __func__,
|
||||
hparms->sample_rate, hparms->sample_width, hparms->cea.channels);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_hdmi_audio_shutdown(struct device *dev, void *data)
|
||||
{
|
||||
dev_dbg(dev, "[%s]\n", __func__);
|
||||
}
|
||||
|
||||
static const struct hdmi_codec_ops fsl_hdmi_audio_codec_ops = {
|
||||
.hw_params = fsl_hdmi_audio_hw_params,
|
||||
.audio_shutdown = fsl_hdmi_audio_shutdown,
|
||||
};
|
||||
|
||||
static struct hdmi_codec_pdata codec_data = {
|
||||
.ops = &fsl_hdmi_audio_codec_ops,
|
||||
.i2s = 1,
|
||||
.max_i2s_channels = 8,
|
||||
};
|
||||
|
||||
static int fsl_hdmi_dai_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct imx_hdmi *hdmi_data;
|
||||
int ret = 0;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
if (!hdmi_get_registered()) {
|
||||
dev_err(&pdev->dev, "failed to probe. Load HDMI-video first.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hdmi_data = devm_kzalloc(&pdev->dev, sizeof(*hdmi_data), GFP_KERNEL);
|
||||
if (!hdmi_data) {
|
||||
dev_err(&pdev->dev, "failed to alloc hdmi_data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hdmi_data->pdev = pdev;
|
||||
|
||||
memcpy(&hdmi_data->cpu_dai_drv, &fsl_hdmi_dai, sizeof(fsl_hdmi_dai));
|
||||
hdmi_data->cpu_dai_drv.name = np->name;
|
||||
|
||||
hdmi_data->mipi_core_clk = devm_clk_get(&pdev->dev, "mipi_core");
|
||||
if (IS_ERR(hdmi_data->mipi_core_clk)) {
|
||||
ret = PTR_ERR(hdmi_data->mipi_core_clk);
|
||||
dev_err(&pdev->dev, "failed to get mipi core clk: %d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdmi_data->isfr_clk = devm_clk_get(&pdev->dev, "hdmi_isfr");
|
||||
if (IS_ERR(hdmi_data->isfr_clk)) {
|
||||
ret = PTR_ERR(hdmi_data->isfr_clk);
|
||||
dev_err(&pdev->dev, "failed to get HDMI isfr clk: %d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdmi_data->iahb_clk = devm_clk_get(&pdev->dev, "hdmi_iahb");
|
||||
if (IS_ERR(hdmi_data->iahb_clk)) {
|
||||
ret = PTR_ERR(hdmi_data->iahb_clk);
|
||||
dev_err(&pdev->dev, "failed to get HDMI ahb clk: %d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, hdmi_data);
|
||||
ret = snd_soc_register_component(&pdev->dev, &fsl_hdmi_component,
|
||||
&hdmi_data->cpu_dai_drv, 1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "register DAI failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
hdmi_data->codec_dev = platform_device_register_data(&pdev->dev,
|
||||
HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_NONE,
|
||||
&codec_data, sizeof(codec_data));
|
||||
if (IS_ERR(hdmi_data->codec_dev)) {
|
||||
dev_err(&pdev->dev, "failed to register HDMI audio codec\n");
|
||||
ret = PTR_ERR(hdmi_data->codec_dev);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi_data->dma_dev = platform_device_alloc("imx-hdmi-audio", -1);
|
||||
if (!hdmi_data->dma_dev) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_dma;
|
||||
}
|
||||
|
||||
platform_set_drvdata(hdmi_data->dma_dev, hdmi_data);
|
||||
|
||||
ret = platform_device_add(hdmi_data->dma_dev);
|
||||
if (ret) {
|
||||
platform_device_put(hdmi_data->dma_dev);
|
||||
goto fail_dma;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_dma:
|
||||
platform_device_unregister(hdmi_data->codec_dev);
|
||||
fail:
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_hdmi_dai_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_hdmi *hdmi_data = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_unregister(hdmi_data->dma_dev);
|
||||
platform_device_unregister(hdmi_data->codec_dev);
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id fsl_hdmi_dai_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6dl-hdmi-audio", },
|
||||
{ .compatible = "fsl,imx6q-hdmi-audio", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_hdmi_dai_dt_ids);
|
||||
|
||||
static struct platform_driver fsl_hdmi_driver = {
|
||||
.probe = fsl_hdmi_dai_probe,
|
||||
.remove = fsl_hdmi_dai_remove,
|
||||
.driver = {
|
||||
.name = "fsl-hdmi-dai",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = fsl_hdmi_dai_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(fsl_hdmi_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("IMX HDMI TX DAI");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:fsl-hdmi-dai");
|
File diff suppressed because it is too large
Load Diff
|
@ -258,6 +258,40 @@
|
|||
#define MICFIL_VAD0_STAT_IF_MASK BIT(MICFIL_VAD0_STAT_IF_SHIFT)
|
||||
#define MICFIL_VAD0_STAT_IF BIT(MICFIL_VAD0_STAT_IF_SHIFT)
|
||||
|
||||
/* HWVAD Constants */
|
||||
#define MICFIL_HWVAD_ENVELOPE_MODE 0
|
||||
#define MICFIL_HWVAD_ENERGY_MODE 1
|
||||
#define MICFIL_HWVAD_INIT_FRAMES 10
|
||||
#define MICFIL_HWVAD_INPGAIN 0
|
||||
#define MICFIL_HWVAD_SGAIN 6
|
||||
#define MICFIL_HWVAD_NGAIN 3
|
||||
#define MICFIL_HWVAD_NFILADJ 0
|
||||
#define MICFIL_HWVAD_ZCDADJ (1 << (MICFIL_VAD0_ZCD_ZCDADJ_WIDTH - 2))
|
||||
#define MICFIL_HWVAD_ZCDTH 10 /* initial threshold value */
|
||||
#define MICFIL_HWVAD_ZCDOR 0
|
||||
#define MICFIL_HWVAD_ZCDAND 1
|
||||
#define MICFIL_HWVAD_ZCD_MANUAL 0
|
||||
#define MICFIL_HWVAD_ZCD_AUTO 1
|
||||
#define MICFIL_HWVAD_HPF_BYPASS 0
|
||||
#define MICFIL_HWVAD_HPF_1750HZ 1
|
||||
#define MICFIL_HWVAD_HPF_215HZ 2
|
||||
#define MICFIL_HWVAD_HPF_102HZ 3
|
||||
#define MICFIL_HWVAD_FRAMET_DEFAULT 10
|
||||
|
||||
/* MICFIL DC Remover Control Register -- REG_MICFIL_DC_CTRL */
|
||||
#define MICFIL_DC_CTRL_SHIFT 0
|
||||
#define MICFIL_DC_CTRL_MASK 0xFFFF
|
||||
#define MICFIL_DC_CTRL_WIDTH 2
|
||||
#define MICFIL_DC_CHX_SHIFT(v) (2 * (v))
|
||||
#define MICFIL_DC_CHX_MASK(v) ((BIT(MICFIL_DC_CTRL_WIDTH) - 1) \
|
||||
<< MICFIL_DC_CHX_SHIFT(v))
|
||||
#define MICFIL_DC_MODE(v1, v2) (((v1) << MICFIL_DC_CHX_SHIFT(v2)) \
|
||||
& MICFIL_DC_CHX_MASK(v2))
|
||||
#define MICFIL_DC_CUTOFF_21HZ 0
|
||||
#define MICFIL_DC_CUTOFF_83HZ 1
|
||||
#define MICFIL_DC_CUTOFF_152Hz 2
|
||||
#define MICFIL_DC_BYPASS 3
|
||||
|
||||
/* MICFIL Output Control Register */
|
||||
#define MICFIL_OUTGAIN_CHX_SHIFT(v) (4 * (v))
|
||||
|
||||
|
@ -273,11 +307,24 @@
|
|||
#define FIFO_PTRWID 3
|
||||
#define FIFO_LEN BIT(FIFO_PTRWID)
|
||||
|
||||
#define MICFIL_IRQ_LINES 2
|
||||
#define MICFIL_IRQ_LINES 4
|
||||
#define MICFIL_MAX_RETRY 25
|
||||
#define MICFIL_SLEEP_MIN 90000 /* in us */
|
||||
#define MICFIL_SLEEP_MAX 100000 /* in us */
|
||||
#define MICFIL_SLEEP 100 /* in ms */
|
||||
#define MICFIL_DMA_MAXBURST_RX 6
|
||||
#define MICFIL_CTRL2_OSR_DEFAULT (0 << MICFIL_CTRL2_CICOSR_SHIFT)
|
||||
#define MICFIL_DEFAULT_RATE 48000
|
||||
#define MICFIL_CLK_SRC_NUM 3
|
||||
#define MICFIL_CLK_AUTO 0
|
||||
|
||||
/* clock source ids */
|
||||
#define MICFIL_AUDIO_PLL1 0
|
||||
#define MICFIL_AUDIO_PLL2 1
|
||||
#define MICFIL_CLK_EXT3 2
|
||||
|
||||
/* States of micfil */
|
||||
#define MICFIL_HWVAD_OFF 0
|
||||
#define MICFIL_HWVAD_ON 1
|
||||
#define MICFIL_RECORDING_OFF 0
|
||||
#define MICFIL_RECORDING_ON 1
|
||||
|
||||
#endif /* _FSL_MICFIL_H */
|
||||
|
|
425
sound/soc/fsl/fsl_rpmsg_i2s.c
Normal file
425
sound/soc/fsl/fsl_rpmsg_i2s.c
Normal file
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* Freescale ALSA SoC rpmsg i2s driver.
|
||||
*
|
||||
* Copyright 2017 NXP
|
||||
*
|
||||
* This program is free software, you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 2 of the License, or(at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "fsl_rpmsg_i2s.h"
|
||||
#include "imx-pcm.h"
|
||||
|
||||
#define FSL_RPMSG_I2S_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
|
||||
SNDRV_PCM_RATE_48000)
|
||||
#define FSL_RPMSG_I2S_FORMATS SNDRV_PCM_FMTBIT_S16_LE
|
||||
|
||||
static int i2s_send_message(struct i2s_rpmsg *msg,
|
||||
struct i2s_info *info)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&info->tx_lock);
|
||||
if (!info->rpdev) {
|
||||
dev_err(info->dev, "rpmsg channel not ready, m4 image ready?\n");
|
||||
mutex_unlock(&info->tx_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&info->rpdev->dev, "send cmd %d\n", msg->send_msg.header.cmd);
|
||||
|
||||
if (!(msg->send_msg.header.type == I2S_TYPE_C))
|
||||
reinit_completion(&info->cmd_complete);
|
||||
|
||||
err = rpmsg_send(info->rpdev->ept, (void *)&msg->send_msg,
|
||||
sizeof(struct i2s_rpmsg_s));
|
||||
if (err) {
|
||||
dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err);
|
||||
mutex_unlock(&info->tx_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!(msg->send_msg.header.type == I2S_TYPE_C)) {
|
||||
/* wait response from rpmsg */
|
||||
err = wait_for_completion_timeout(&info->cmd_complete,
|
||||
msecs_to_jiffies(RPMSG_TIMEOUT));
|
||||
if (!err) {
|
||||
dev_err(&info->rpdev->dev,
|
||||
"rpmsg_send cmd %d timeout!\n",
|
||||
msg->send_msg.header.cmd);
|
||||
mutex_unlock(&info->tx_lock);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
memcpy(&msg->recv_msg, &info->recv_msg,
|
||||
sizeof(struct i2s_rpmsg_r));
|
||||
memcpy(&info->rpmsg[msg->recv_msg.header.cmd].recv_msg,
|
||||
&msg->recv_msg, sizeof(struct i2s_rpmsg_r));
|
||||
|
||||
/*
|
||||
* Reset the buffer pointer to be zero, actully we have
|
||||
* set the buffer pointer to be zero in imx_rpmsg_terminate_all
|
||||
* But if there is timer task queued in queue, after it is
|
||||
* executed the buffer pointer will be changed, so need to
|
||||
* reset it again with TERMINATE command.
|
||||
*/
|
||||
|
||||
switch (msg->send_msg.header.cmd) {
|
||||
case I2S_TX_TERMINATE:
|
||||
info->rpmsg[I2S_TX_POINTER].recv_msg.param.buffer_offset = 0;
|
||||
break;
|
||||
case I2S_RX_TERMINATE:
|
||||
info->rpmsg[I2S_RX_POINTER].recv_msg.param.buffer_offset = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(&info->rpdev->dev, "cmd:%d, resp %d\n",
|
||||
msg->send_msg.header.cmd,
|
||||
info->recv_msg.param.resp);
|
||||
mutex_unlock(&info->tx_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const unsigned int fsl_rpmsg_rates[] = {
|
||||
8000, 11025, 16000, 22050, 44100,
|
||||
32000, 48000, 96000, 88200, 176400, 192000,
|
||||
352800, 384000, 705600, 768000, 1411200, 2822400,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list fsl_rpmsg_rate_constraints = {
|
||||
.count = ARRAY_SIZE(fsl_rpmsg_rates),
|
||||
.list = fsl_rpmsg_rates,
|
||||
};
|
||||
|
||||
static int fsl_rpmsg_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &fsl_rpmsg_rate_constraints);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops = {
|
||||
.startup = fsl_rpmsg_startup,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver fsl_rpmsg_i2s_dai = {
|
||||
.playback = {
|
||||
.stream_name = "CPU-Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = FSL_RPMSG_I2S_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "CPU-Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = FSL_RPMSG_I2S_FORMATS,
|
||||
},
|
||||
.symmetric_rates = 1,
|
||||
.symmetric_channels = 1,
|
||||
.symmetric_samplebits = 1,
|
||||
.ops = &fsl_rpmsg_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver fsl_component = {
|
||||
.name = "fsl-rpmsg-i2s",
|
||||
};
|
||||
|
||||
static const struct of_device_id fsl_rpmsg_i2s_ids[] = {
|
||||
{ .compatible = "fsl,imx7ulp-rpmsg-i2s"},
|
||||
{ .compatible = "fsl,imx8mq-rpmsg-i2s"},
|
||||
{ .compatible = "fsl,imx8qxp-rpmsg-i2s"},
|
||||
{ .compatible = "fsl,imx8qm-rpmsg-i2s"},
|
||||
{ .compatible = "fsl,imx8mn-rpmsg-i2s"},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_rpmsg_i2s_ids);
|
||||
|
||||
static void rpmsg_i2s_work(struct work_struct *work)
|
||||
{
|
||||
struct work_of_rpmsg *work_of_rpmsg;
|
||||
struct i2s_info *i2s_info;
|
||||
bool is_period_done = false;
|
||||
unsigned long flags;
|
||||
struct i2s_rpmsg msg;
|
||||
|
||||
work_of_rpmsg = container_of(work, struct work_of_rpmsg, work);
|
||||
i2s_info = work_of_rpmsg->i2s_info;
|
||||
|
||||
spin_lock_irqsave(&i2s_info->lock[0], flags);
|
||||
if (i2s_info->period_done_msg_enabled[0]) {
|
||||
memcpy(&msg, &i2s_info->period_done_msg[0], sizeof(struct i2s_rpmsg_s));
|
||||
i2s_info->period_done_msg_enabled[0] = false;
|
||||
spin_unlock_irqrestore(&i2s_info->lock[0], flags);
|
||||
|
||||
i2s_send_message(&msg, i2s_info);
|
||||
} else
|
||||
spin_unlock_irqrestore(&i2s_info->lock[0], flags);
|
||||
|
||||
if (i2s_info->period_done_msg_enabled[1]) {
|
||||
i2s_send_message(&i2s_info->period_done_msg[1], i2s_info);
|
||||
i2s_info->period_done_msg_enabled[1] = false;
|
||||
}
|
||||
|
||||
if (work_of_rpmsg->msg.send_msg.header.type == I2S_TYPE_C &&
|
||||
(work_of_rpmsg->msg.send_msg.header.cmd == I2S_TX_PERIOD_DONE ||
|
||||
work_of_rpmsg->msg.send_msg.header.cmd == I2S_RX_PERIOD_DONE))
|
||||
is_period_done = true;
|
||||
|
||||
if (!is_period_done)
|
||||
i2s_send_message(&work_of_rpmsg->msg, i2s_info);
|
||||
|
||||
spin_lock_irqsave(&i2s_info->wq_lock, flags);
|
||||
i2s_info->work_read_index++;
|
||||
i2s_info->work_read_index %= WORK_MAX_NUM;
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
}
|
||||
|
||||
static int fsl_rpmsg_i2s_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s;
|
||||
struct i2s_info *i2s_info;
|
||||
int audioindex = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
rpmsg_i2s = devm_kzalloc(&pdev->dev, sizeof(struct fsl_rpmsg_i2s),
|
||||
GFP_KERNEL);
|
||||
if (!rpmsg_i2s)
|
||||
return -ENOMEM;
|
||||
|
||||
rpmsg_i2s->pdev = pdev;
|
||||
i2s_info = &rpmsg_i2s->i2s_info;
|
||||
|
||||
ret = of_property_read_u32(np, "fsl,audioindex", &audioindex);
|
||||
if (ret)
|
||||
audioindex = 0;
|
||||
|
||||
/* Setup work queue */
|
||||
i2s_info->rpmsg_wq = alloc_ordered_workqueue("rpmsg_i2s", WQ_HIGHPRI | WQ_UNBOUND | WQ_FREEZABLE);
|
||||
if (i2s_info->rpmsg_wq == NULL) {
|
||||
dev_err(&pdev->dev, "workqueue create failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i2s_info->work_write_index = 1;
|
||||
i2s_info->send_message = i2s_send_message;
|
||||
|
||||
for (i = 0; i < WORK_MAX_NUM; i++) {
|
||||
INIT_WORK(&i2s_info->work_list[i].work, rpmsg_i2s_work);
|
||||
i2s_info->work_list[i].i2s_info = i2s_info;
|
||||
}
|
||||
|
||||
for (i = 0; i < I2S_CMD_MAX_NUM ; i++) {
|
||||
i2s_info->rpmsg[i].send_msg.header.cate = IMX_RPMSG_AUDIO;
|
||||
i2s_info->rpmsg[i].send_msg.header.major = IMX_RMPSG_MAJOR;
|
||||
i2s_info->rpmsg[i].send_msg.header.minor = IMX_RMPSG_MINOR;
|
||||
i2s_info->rpmsg[i].send_msg.header.type = I2S_TYPE_A;
|
||||
i2s_info->rpmsg[i].send_msg.param.audioindex = audioindex;
|
||||
}
|
||||
|
||||
mutex_init(&i2s_info->tx_lock);
|
||||
mutex_init(&i2s_info->i2c_lock);
|
||||
spin_lock_init(&i2s_info->lock[0]);
|
||||
spin_lock_init(&i2s_info->lock[1]);
|
||||
spin_lock_init(&i2s_info->wq_lock);
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"fsl,imx7ulp-rpmsg-i2s")) {
|
||||
rpmsg_i2s->codec_wm8960 = 1;
|
||||
rpmsg_i2s->version = 1;
|
||||
rpmsg_i2s->rates = SNDRV_PCM_RATE_8000 |
|
||||
SNDRV_PCM_RATE_16000 |
|
||||
SNDRV_PCM_RATE_48000;
|
||||
rpmsg_i2s->formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
fsl_rpmsg_i2s_dai.playback.rates = rpmsg_i2s->rates;
|
||||
fsl_rpmsg_i2s_dai.playback.formats = rpmsg_i2s->formats;
|
||||
fsl_rpmsg_i2s_dai.capture.rates = rpmsg_i2s->rates;
|
||||
fsl_rpmsg_i2s_dai.capture.formats = rpmsg_i2s->formats;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"fsl,imx8qxp-rpmsg-i2s")) {
|
||||
rpmsg_i2s->codec_wm8960 = 1 + (1 << 16);
|
||||
rpmsg_i2s->version = 1;
|
||||
rpmsg_i2s->codec_cs42888 = 1 + (2 << 16);
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"fsl,imx8qm-rpmsg-i2s")) {
|
||||
rpmsg_i2s->codec_wm8960 = 0;
|
||||
rpmsg_i2s->version = 1;
|
||||
rpmsg_i2s->codec_cs42888 = 1 + (0 << 16);
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"fsl,imx8mq-rpmsg-i2s")) {
|
||||
rpmsg_i2s->codec_dummy = 0;
|
||||
rpmsg_i2s->codec_ak4497 = 1;
|
||||
rpmsg_i2s->version = 2;
|
||||
rpmsg_i2s->rates = SNDRV_PCM_RATE_KNOT;
|
||||
rpmsg_i2s->formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_DSD_U8 |
|
||||
SNDRV_PCM_FMTBIT_DSD_U16_LE |
|
||||
SNDRV_PCM_FMTBIT_DSD_U32_LE;
|
||||
|
||||
fsl_rpmsg_i2s_dai.playback.rates = rpmsg_i2s->rates;
|
||||
fsl_rpmsg_i2s_dai.playback.formats = rpmsg_i2s->formats;
|
||||
fsl_rpmsg_i2s_dai.capture.rates = rpmsg_i2s->rates;
|
||||
fsl_rpmsg_i2s_dai.capture.formats = rpmsg_i2s->formats;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"fsl,imx8mn-rpmsg-i2s")) {
|
||||
rpmsg_i2s->codec_dummy = 1;
|
||||
rpmsg_i2s->version = 2;
|
||||
rpmsg_i2s->rates = SNDRV_PCM_RATE_KNOT;
|
||||
rpmsg_i2s->formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE;
|
||||
|
||||
fsl_rpmsg_i2s_dai.playback.rates = rpmsg_i2s->rates;
|
||||
fsl_rpmsg_i2s_dai.playback.formats = rpmsg_i2s->formats;
|
||||
fsl_rpmsg_i2s_dai.capture.rates = rpmsg_i2s->rates;
|
||||
fsl_rpmsg_i2s_dai.capture.formats = rpmsg_i2s->formats;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "fsl,enable-lpa"))
|
||||
rpmsg_i2s->enable_lpa = 1;
|
||||
|
||||
if (of_property_read_u32(np, "fsl,dma-buffer-size",
|
||||
&i2s_info->prealloc_buffer_size))
|
||||
i2s_info->prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE;
|
||||
|
||||
platform_set_drvdata(pdev, rpmsg_i2s);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
|
||||
&fsl_rpmsg_i2s_dai, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return imx_rpmsg_platform_register(&pdev->dev);
|
||||
}
|
||||
|
||||
static int fsl_rpmsg_i2s_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = platform_get_drvdata(pdev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
|
||||
if (i2s_info->rpmsg_wq)
|
||||
destroy_workqueue(i2s_info->rpmsg_wq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_rpmsg_i2s_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev);
|
||||
|
||||
pm_qos_add_request(&rpmsg_i2s->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_rpmsg_i2s_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev);
|
||||
|
||||
pm_qos_remove_request(&rpmsg_i2s->pm_qos_req);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int fsl_rpmsg_i2s_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg_tx;
|
||||
struct i2s_rpmsg *rpmsg_rx;
|
||||
|
||||
flush_workqueue(i2s_info->rpmsg_wq);
|
||||
rpmsg_tx = &i2s_info->rpmsg[I2S_TX_SUSPEND];
|
||||
rpmsg_rx = &i2s_info->rpmsg[I2S_RX_SUSPEND];
|
||||
|
||||
rpmsg_tx->send_msg.header.cmd = I2S_TX_SUSPEND;
|
||||
i2s_send_message(rpmsg_tx, i2s_info);
|
||||
|
||||
rpmsg_rx->send_msg.header.cmd = I2S_RX_SUSPEND;
|
||||
i2s_send_message(rpmsg_rx, i2s_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_rpmsg_i2s_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg_tx;
|
||||
struct i2s_rpmsg *rpmsg_rx;
|
||||
|
||||
rpmsg_tx = &i2s_info->rpmsg[I2S_TX_RESUME];
|
||||
rpmsg_rx = &i2s_info->rpmsg[I2S_RX_RESUME];
|
||||
|
||||
rpmsg_tx->send_msg.header.cmd = I2S_TX_RESUME;
|
||||
i2s_send_message(rpmsg_tx, i2s_info);
|
||||
|
||||
rpmsg_rx->send_msg.header.cmd = I2S_RX_RESUME;
|
||||
i2s_send_message(rpmsg_rx, i2s_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops fsl_rpmsg_i2s_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(fsl_rpmsg_i2s_runtime_suspend,
|
||||
fsl_rpmsg_i2s_runtime_resume,
|
||||
NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(fsl_rpmsg_i2s_suspend, fsl_rpmsg_i2s_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver fsl_rpmsg_i2s_driver = {
|
||||
.probe = fsl_rpmsg_i2s_probe,
|
||||
.remove = fsl_rpmsg_i2s_remove,
|
||||
.driver = {
|
||||
.name = "fsl-rpmsg-i2s",
|
||||
.pm = &fsl_rpmsg_i2s_pm_ops,
|
||||
.of_match_table = fsl_rpmsg_i2s_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(fsl_rpmsg_i2s_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Freescale Soc rpmsg_i2s Interface");
|
||||
MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@freescale.com>");
|
||||
MODULE_ALIAS("platform:fsl-rpmsg_i2s");
|
||||
MODULE_LICENSE("GPL");
|
454
sound/soc/fsl/fsl_rpmsg_i2s.h
Normal file
454
sound/soc/fsl/fsl_rpmsg_i2s.h
Normal file
|
@ -0,0 +1,454 @@
|
|||
/*
|
||||
* Copyright 2017 NXP
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************
|
||||
* communication stack of audio with rpmsg
|
||||
******************************************************************************
|
||||
* Packet structure:
|
||||
* A SRTM message consists of a 10 bytes header followed by 0~N bytes of data
|
||||
*
|
||||
* +---------------+-------------------------------+
|
||||
* | | Content |
|
||||
* +---------------+-------------------------------+
|
||||
* | Byte Offset | 7 6 5 4 3 2 1 0 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 0 | Category |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 1 ~ 2 | Version |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 3 | Type |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 4 | Command |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 5 | Reserved0 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 6 | Reserved1 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 7 | Reserved2 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 8 | Reserved3 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 9 | Reserved4 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | 10 | DATA 0 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* : : : : : : : : : : : : :
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
* | N + 10 - 1 | DATA N-1 |
|
||||
* +---------------+---+---+---+---+---+---+---+---+
|
||||
*
|
||||
* +----------+------------+------------------------------------------------+
|
||||
* | Field | Byte | |
|
||||
* +----------+------------+------------------------------------------------+
|
||||
* | Category | 0 | The destination category. |
|
||||
* +----------+------------+------------------------------------------------+
|
||||
* | Version | 1 ~ 2 | The category version of the sender of the |
|
||||
* | | | packet. |
|
||||
* | | | The first byte represent the major version of |
|
||||
* | | | the packet.The second byte represent the minor |
|
||||
* | | | version of the packet. |
|
||||
* +----------+------------+------------------------------------------------+
|
||||
* | Type | 3 | The message type of current message packet. |
|
||||
* +----------+------------+------------------------------------------------+
|
||||
* | Command | 4 | The command byte sent to remote processor/SoC. |
|
||||
* +----------+------------+------------------------------------------------+
|
||||
* | Reserved | 5 ~ 9 | Reserved field for future extension. |
|
||||
* +----------+------------+------------------------------------------------+
|
||||
* | Data | N | The data payload of the message packet. |
|
||||
* +----------+------------+------------------------------------------------+
|
||||
*
|
||||
* Audio control:
|
||||
* SRTM Audio Control Category Request Command Table:
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | Category | Version | Type | Command | Data | Function |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x00 | Data[0]: Audio Device Index | Open an Audio TX Instance. |
|
||||
* | | | | | Data[1]: format | |
|
||||
* | | | | | Data[2]: channels | |
|
||||
* | | | | | Data[3-6]: samplerate | |
|
||||
* | | | | | Data[7-10]: buffer_addr | |
|
||||
* | | | | | Data[11-14]: buffer_size | |
|
||||
* | | | | | Data[15-18]: period_size | |
|
||||
* | | | | | Data[19-22]: buffer_tail | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x01 | Data[0]: Audio Device Index | Start an Audio TX Instance. |
|
||||
* | | | | | Same as above command | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x02 | Data[0]: Audio Device Index | Pause an Audio TX Instance. |
|
||||
* | | | | | Same as above command | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x03 | Data[0]: Audio Device Index | Resume an Audio TX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x04 | Data[0]: Audio Device Index | Terminate an Audio TX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x05 | Data[0]: Audio Device Index | Close an Audio TX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x06 | Data[0]: Audio Device Index | Set Parameters for an Audio TX Instance. |
|
||||
* | | | | | Data[1]: format | |
|
||||
* | | | | | Data[2]: channels | |
|
||||
* | | | | | Data[3-6]: samplerate | |
|
||||
* | | | | | Data[7-22]: reserved | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x07 | Data[0]: Audio Device Index | Set Audio TX Buffer. |
|
||||
* | | | | | Data[1-6]: reserved | |
|
||||
* | | | | | Data[7-10]: buffer_addr | |
|
||||
* | | | | | Data[11-14]: buffer_size | |
|
||||
* | | | | | Data[15-18]: period_size | |
|
||||
* | | | | | Data[19-22]: buffer_tail | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x08 | Data[0]: Audio Device Index | Suspend an Audio TX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x09 | Data[0]: Audio Device Index | Resume an Audio TX Instance. |
|
||||
* | | | | | Data[1]: format | |
|
||||
* | | | | | Data[2]: channels | |
|
||||
* | | | | | Data[3-6]: samplerate | |
|
||||
* | | | | | Data[7-10]: buffer_addr | |
|
||||
* | | | | | Data[11-14]: buffer_size | |
|
||||
* | | | | | Data[15-18]: period_size | |
|
||||
* | | | | | Data[19-22]: buffer_tail | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x0A | Data[0]: Audio Device Index | Open an Audio RX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x0B | Data[0]: Audio Device Index | Start an Audio RX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x0C | Data[0]: Audio Device Index | Pause an Audio RX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x0D | Data[0]: Audio Device Index | Resume an Audio RX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x0E | Data[0]: Audio Device Index | Terminate an Audio RX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x0F | Data[0]: Audio Device Index | Close an Audio RX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x10 | Data[0]: Audio Device Index | Set Parameters for an Audio RX Instance. |
|
||||
* | | | | | Data[1]: format | |
|
||||
* | | | | | Data[2]: channels | |
|
||||
* | | | | | Data[3-6]: samplerate | |
|
||||
* | | | | | Data[7-22]: reserved | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x11 | Data[0]: Audio Device Index | Set Audio RX Buffer. |
|
||||
* | | | | | Data[1-6]: reserved | |
|
||||
* | | | | | Data[7-10]: buffer_addr | |
|
||||
* | | | | | Data[11-14]: buffer_size | |
|
||||
* | | | | | Data[15-18]: period_size | |
|
||||
* | | | | | Data[19-22]: buffer_tail | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x12 | Data[0]: Audio Device Index | Suspend an Audio RX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x13 | Data[0]: Audio Device Index | Resume an Audio RX Instance. |
|
||||
* | | | | | Data[1]: format | |
|
||||
* | | | | | Data[2]: channels | |
|
||||
* | | | | | Data[3-6]: samplerate | |
|
||||
* | | | | | Data[7-10]: buffer_addr | |
|
||||
* | | | | | Data[11-14]: buffer_size | |
|
||||
* | | | | | Data[15-18]: period_size | |
|
||||
* | | | | | Data[19-22]: buffer_tail | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x14 | Data[0]: Audio Device Index | Set register value to codec. |
|
||||
* | | | | | Data[1-6]: reserved | |
|
||||
* | | | | | Data[7-10]: register | |
|
||||
* | | | | | Data[11-14]: value | |
|
||||
* | | | | | Data[15-22]: reserved | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x00 | 0x15 | Data[0]: Audio Device Index | Get register value from codec. |
|
||||
* | | | | | Data[1-6]: reserved | |
|
||||
* | | | | | Data[7-10]: register | |
|
||||
* | | | | | Data[11-22]: reserved | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* Note 1: See <List of Sample Format> for available value of
|
||||
* Sample Format;
|
||||
* Note 2: See <List of Audio Channels> for available value of Channels;
|
||||
* Note 3: Sample Rate of Set Parameters for an Audio TX Instance
|
||||
* Command and Set Parameters for an Audio RX Instance Command is
|
||||
* in little-endian format.
|
||||
*
|
||||
* SRTM Audio Control Category Response Command Table:
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | Category | Version | Type | Command | Data | Function |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x00 | Data[0]: Audio Device Index | Reply for Open an Audio TX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x01 | Data[0]: Audio Device Index | Reply for Start an Audio TX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x02 | Data[0]: Audio Device Index | Reply for Pause an Audio TX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x03 | Data[0]: Audio Device Index | Reply for Resume an Audio TX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x04 | Data[0]: Audio Device Index | Reply for Terminate an Audio TX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x05 | Data[0]: Audio Device Index | Reply for Close an Audio TX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x06 | Data[0]: Audio Device Index | Reply for Set Parameters for an Audio |
|
||||
* | | | | | Data[1]: Return code | TX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x07 | Data[0]: Audio Device Index | Reply for Set Audio TX Buffer. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x08 | Data[0]: Audio Device Index | Reply for Suspend an Audio TX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x09 | Data[0]: Audio Device Index | Reply for Resume an Audio TX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x0A | Data[0]: Audio Device Index | Reply for Open an Audio RX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x0B | Data[0]: Audio Device Index | Reply for Start an Audio RX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x0C | Data[0]: Audio Device Index | Reply for Pause an Audio RX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x0D | Data[0]: Audio Device Index | Reply for Resume an Audio RX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x0E | Data[0]: Audio Device Index | Reply for Terminate an Audio RX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x0F | Data[0]: Audio Device Index | Reply for Close an Audio RX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x10 | Data[0]: Audio Device Index | Reply for Set Parameters for an Audio |
|
||||
* | | | | | Data[1]: Return code | RX Instance. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x11 | Data[0]: Audio Device Index | Reply for Set Audio RX Buffer. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x12 | Data[0]: Audio Device Index | Reply for Supend an Audio RX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x13 | Data[0]: Audio Device Index | Reply for Resume an Audio RX Instance. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x14 | Data[0]: Audio Device Index | Reply for Set codec register value. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x01 | 0x15 | Data[0]: Audio Device Index | Reply for Get codec register value. |
|
||||
* | | | | | Data[1]: Return code | |
|
||||
* | | | | | Data[2-6]: reserved | |
|
||||
* | | | | | Data[7-10]: register | |
|
||||
* | | | | | Data[11-14]: value | |
|
||||
* | | | | | Data[15-22]: reserved | |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
*
|
||||
* SRTM Audio Control Category Notification Command Table:
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | Category | Version | Type | Command | Data | Function |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x02 | 0x00 | Data[0]: Audio Device Index | Notify one TX period is finished. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
* | 0x03 | 0x0100 | 0x02 | 0x01 | Data[0]: Audio Device Index | Notify one RX period is finished. |
|
||||
* +------------+---------+------+---------+-------------------------------+------------------------------------------------+
|
||||
*
|
||||
* List of Sample Format:
|
||||
* +--------------------+----------------------------------------------+
|
||||
* | Sample Format | Description |
|
||||
* +--------------------+----------------------------------------------+
|
||||
* | 0x0 | S16_LE |
|
||||
* +--------------------+----------------------------------------------+
|
||||
* | 0x1 | S24_LE |
|
||||
* +--------------------+----------------------------------------------+
|
||||
*
|
||||
* List of Audio Channels
|
||||
* +--------------------+----------------------------------------------+
|
||||
* | Audio Channel | Description |
|
||||
* +--------------------+----------------------------------------------+
|
||||
* | 0x0 | Left Channel |
|
||||
* +--------------------+----------------------------------------------+
|
||||
* | 0x1 | Right Channel |
|
||||
* +--------------------+----------------------------------------------+
|
||||
* | 0x2 | Left & Right Channel |
|
||||
* +--------------------+----------------------------------------------+
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __FSL_RPMSG_I2S_H
|
||||
#define __FSL_RPMSG_I2S_H
|
||||
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/imx_rpmsg.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#define RPMSG_TIMEOUT 1000
|
||||
|
||||
#define I2S_TX_OPEN 0x0
|
||||
#define I2S_TX_START 0x1
|
||||
#define I2S_TX_PAUSE 0x2
|
||||
#define I2S_TX_RESTART 0x3
|
||||
#define I2S_TX_TERMINATE 0x4
|
||||
#define I2S_TX_CLOSE 0x5
|
||||
#define I2S_TX_HW_PARAM 0x6
|
||||
#define I2S_TX_BUFFER 0x7
|
||||
#define I2S_TX_SUSPEND 0x8
|
||||
#define I2S_TX_RESUME 0x9
|
||||
|
||||
#define I2S_RX_OPEN 0xA
|
||||
#define I2S_RX_START 0xB
|
||||
#define I2S_RX_PAUSE 0xC
|
||||
#define I2S_RX_RESTART 0xD
|
||||
#define I2S_RX_TERMINATE 0xE
|
||||
#define I2S_RX_CLOSE 0xF
|
||||
#define I2S_RX_HW_PARAM 0x10
|
||||
#define I2S_RX_BUFFER 0x11
|
||||
#define I2S_RX_SUSPEND 0x12
|
||||
#define I2S_RX_RESUME 0x13
|
||||
#define SET_CODEC_VALUE 0x14
|
||||
#define GET_CODEC_VALUE 0x15
|
||||
#define I2S_TX_POINTER 0x16
|
||||
#define I2S_RX_POINTER 0x17
|
||||
|
||||
#define I2S_TYPE_A_NUM 0x18
|
||||
|
||||
#define WORK_MAX_NUM 0x30
|
||||
|
||||
#define I2S_TX_PERIOD_DONE 0x0
|
||||
#define I2S_RX_PERIOD_DONE 0x1
|
||||
|
||||
#define I2S_TYPE_C_NUM 0x2
|
||||
|
||||
#define I2S_CMD_MAX_NUM (I2S_TYPE_A_NUM + I2S_TYPE_C_NUM)
|
||||
|
||||
#define I2S_TYPE_A 0x0
|
||||
#define I2S_TYPE_B 0x1
|
||||
#define I2S_TYPE_C 0x2
|
||||
|
||||
#define I2S_RESP_NONE 0x0
|
||||
#define I2S_RESP_NOT_ALLOWED 0x1
|
||||
#define I2S_RESP_SUCCESS 0x2
|
||||
#define I2S_RESP_FAILED 0x3
|
||||
|
||||
#define RPMSG_S16_LE 0x0
|
||||
#define RPMSG_S24_LE 0x1
|
||||
#define RPMSG_S32_LE 0x2
|
||||
#define RPMSG_DSD_U16_LE 0x3
|
||||
#define RPMSG_DSD_U24_LE 0x4
|
||||
#define RPMSG_DSD_U32_LE 0x5
|
||||
|
||||
#define RPMSG_CH_LEFT 0x0
|
||||
#define RPMSG_CH_RIGHT 0x1
|
||||
#define RPMSG_CH_STEREO 0x2
|
||||
|
||||
struct i2s_param_s {
|
||||
unsigned char audioindex;
|
||||
unsigned char format;
|
||||
unsigned char channels;
|
||||
unsigned int rate;
|
||||
unsigned int buffer_addr; /* Register for SET_CODEC_VALUE*/
|
||||
unsigned int buffer_size; /* register value for SET_CODEC_VALUE*/
|
||||
unsigned int period_size;
|
||||
unsigned int buffer_tail;
|
||||
} __packed;
|
||||
|
||||
struct i2s_param_r {
|
||||
unsigned char audioindex;
|
||||
unsigned char resp;
|
||||
unsigned char reserved1[1];
|
||||
unsigned int buffer_offset; /* the consumed offset of buffer*/
|
||||
unsigned int reg_addr;
|
||||
unsigned int reg_data;
|
||||
unsigned char reserved2[4];
|
||||
unsigned int buffer_tail;
|
||||
} __packed;
|
||||
|
||||
/* struct of send message */
|
||||
struct i2s_rpmsg_s {
|
||||
struct imx_rpmsg_head header;
|
||||
struct i2s_param_s param;
|
||||
};
|
||||
|
||||
/* struct of received message */
|
||||
struct i2s_rpmsg_r {
|
||||
struct imx_rpmsg_head header;
|
||||
struct i2s_param_r param;
|
||||
};
|
||||
|
||||
struct i2s_rpmsg {
|
||||
struct i2s_rpmsg_s send_msg;
|
||||
struct i2s_rpmsg_r recv_msg;
|
||||
};
|
||||
|
||||
struct work_of_rpmsg {
|
||||
struct i2s_info *i2s_info;
|
||||
/* sent msg for each work */
|
||||
struct i2s_rpmsg msg;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
struct stream_timer {
|
||||
struct timer_list timer;
|
||||
struct snd_pcm_substream *substream;
|
||||
};
|
||||
|
||||
typedef void (*dma_callback)(void *arg);
|
||||
struct i2s_info {
|
||||
struct rpmsg_device *rpdev;
|
||||
struct device *dev;
|
||||
struct completion cmd_complete;
|
||||
|
||||
/* received msg (global) */
|
||||
struct i2s_rpmsg_r recv_msg;
|
||||
|
||||
struct i2s_rpmsg rpmsg[I2S_CMD_MAX_NUM];
|
||||
struct i2s_rpmsg period_done_msg[2];
|
||||
bool period_done_msg_enabled[2];
|
||||
|
||||
struct workqueue_struct *rpmsg_wq;
|
||||
struct work_of_rpmsg work_list[WORK_MAX_NUM];
|
||||
int work_write_index;
|
||||
int work_read_index;
|
||||
int msg_drop_count[2];
|
||||
int num_period[2];
|
||||
void *callback_param[2];
|
||||
int (*send_message)(struct i2s_rpmsg *msg, struct i2s_info *info);
|
||||
dma_callback callback[2];
|
||||
spinlock_t lock[2];
|
||||
spinlock_t wq_lock;
|
||||
struct mutex tx_lock;
|
||||
struct mutex i2c_lock;
|
||||
struct stream_timer stream_timer[2];
|
||||
int prealloc_buffer_size;
|
||||
};
|
||||
|
||||
struct fsl_rpmsg_i2s {
|
||||
struct platform_device *pdev;
|
||||
struct i2s_info i2s_info;
|
||||
struct pm_qos_request pm_qos_req;
|
||||
int codec_dummy;
|
||||
int codec_wm8960;
|
||||
int codec_cs42888;
|
||||
int codec_ak4497;
|
||||
int force_lpa;
|
||||
int version;
|
||||
int rates;
|
||||
u64 formats;
|
||||
int enable_lpa;
|
||||
};
|
||||
|
||||
#define RPMSG_CODEC_DRV_NAME_WM8960 "rpmsg-audio-codec-wm8960"
|
||||
#define RPMSG_CODEC_DRV_NAME_CS42888 "rpmsg-audio-codec-cs42888"
|
||||
#define RPMSG_CODEC_DRV_NAME_AK4497 "rpmsg-audio-codec-ak4497"
|
||||
|
||||
struct fsl_rpmsg_codec {
|
||||
int audioindex;
|
||||
|
||||
/*property for wm8960*/
|
||||
bool capless;
|
||||
bool shared_lrclk;
|
||||
|
||||
/*property for cs42xx8*/
|
||||
|
||||
char name[32];
|
||||
int num_adcs;
|
||||
};
|
||||
|
||||
#endif /* __FSL_RPMSG_I2S_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,78 +1,94 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2012-2013 Freescale Semiconductor, Inc.
|
||||
* Copyright 2012-2016 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __FSL_SAI_H
|
||||
#define __FSL_SAI_H
|
||||
|
||||
#include <linux/pm_qos.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
SNDRV_PCM_FMTBIT_S32_LE |\
|
||||
SNDRV_PCM_FMTBIT_DSD_U8 |\
|
||||
SNDRV_PCM_FMTBIT_DSD_U16_LE |\
|
||||
SNDRV_PCM_FMTBIT_DSD_U32_LE)
|
||||
|
||||
/* SAI Register Map Register */
|
||||
#define FSL_SAI_TCSR(ofs) (0x00 + ofs) /* SAI Transmit Control */
|
||||
#define FSL_SAI_TCR1(ofs) (0x04 + ofs) /* SAI Transmit Configuration 1 */
|
||||
#define FSL_SAI_TCR2(ofs) (0x08 + ofs) /* SAI Transmit Configuration 2 */
|
||||
#define FSL_SAI_TCR3(ofs) (0x0c + ofs) /* SAI Transmit Configuration 3 */
|
||||
#define FSL_SAI_TCR4(ofs) (0x10 + ofs) /* SAI Transmit Configuration 4 */
|
||||
#define FSL_SAI_TCR5(ofs) (0x14 + ofs) /* SAI Transmit Configuration 5 */
|
||||
#define FSL_SAI_TDR0 0x20 /* SAI Transmit Data 0 */
|
||||
#define FSL_SAI_TDR1 0x24 /* SAI Transmit Data 1 */
|
||||
#define FSL_SAI_TDR2 0x28 /* SAI Transmit Data 2 */
|
||||
#define FSL_SAI_TDR3 0x2C /* SAI Transmit Data 3 */
|
||||
#define FSL_SAI_TDR4 0x30 /* SAI Transmit Data 4 */
|
||||
#define FSL_SAI_TDR5 0x34 /* SAI Transmit Data 5 */
|
||||
#define FSL_SAI_TDR6 0x38 /* SAI Transmit Data 6 */
|
||||
#define FSL_SAI_TDR7 0x3C /* SAI Transmit Data 7 */
|
||||
#define FSL_SAI_TFR0 0x40 /* SAI Transmit FIFO 0 */
|
||||
#define FSL_SAI_TFR1 0x44 /* SAI Transmit FIFO 1 */
|
||||
#define FSL_SAI_TFR2 0x48 /* SAI Transmit FIFO 2 */
|
||||
#define FSL_SAI_TFR3 0x4C /* SAI Transmit FIFO 3 */
|
||||
#define FSL_SAI_TFR4 0x50 /* SAI Transmit FIFO 4 */
|
||||
#define FSL_SAI_TFR5 0x54 /* SAI Transmit FIFO 5 */
|
||||
#define FSL_SAI_TFR6 0x58 /* SAI Transmit FIFO 6 */
|
||||
#define FSL_SAI_TFR7 0x5C /* SAI Transmit FIFO 7 */
|
||||
#define FSL_SAI_VERID 0x00 /* SAI Version ID Register */
|
||||
#define FSL_SAI_PARAM 0x04 /* SAI Parameter Register */
|
||||
#define FSL_SAI_TCSR(offset) (0x00 + offset) /* SAI Transmit Control */
|
||||
#define FSL_SAI_TCR1(offset) (0x04 + offset) /* SAI Transmit Configuration 1 */
|
||||
#define FSL_SAI_TCR2(offset) (0x08 + offset) /* SAI Transmit Configuration 2 */
|
||||
#define FSL_SAI_TCR3(offset) (0x0c + offset) /* SAI Transmit Configuration 3 */
|
||||
#define FSL_SAI_TCR4(offset) (0x10 + offset) /* SAI Transmit Configuration 4 */
|
||||
#define FSL_SAI_TCR5(offset) (0x14 + offset) /* SAI Transmit Configuration 5 */
|
||||
#define FSL_SAI_TDR0 0x20 /* SAI Transmit Data */
|
||||
#define FSL_SAI_TDR1 0x24 /* SAI Transmit Data */
|
||||
#define FSL_SAI_TDR2 0x28 /* SAI Transmit Data */
|
||||
#define FSL_SAI_TDR3 0x2C /* SAI Transmit Data */
|
||||
#define FSL_SAI_TDR4 0x30 /* SAI Transmit Data */
|
||||
#define FSL_SAI_TDR5 0x34 /* SAI Transmit Data */
|
||||
#define FSL_SAI_TDR6 0x38 /* SAI Transmit Data */
|
||||
#define FSL_SAI_TDR7 0x3C /* SAI Transmit Data */
|
||||
#define FSL_SAI_TFR0 0x40 /* SAI Transmit FIFO */
|
||||
#define FSL_SAI_TFR1 0x44 /* SAI Transmit FIFO */
|
||||
#define FSL_SAI_TFR2 0x48 /* SAI Transmit FIFO */
|
||||
#define FSL_SAI_TFR3 0x4C /* SAI Transmit FIFO */
|
||||
#define FSL_SAI_TFR4 0x50 /* SAI Transmit FIFO */
|
||||
#define FSL_SAI_TFR5 0x54 /* SAI Transmit FIFO */
|
||||
#define FSL_SAI_TFR6 0x58 /* SAI Transmit FIFO */
|
||||
#define FSL_SAI_TFR7 0x5C /* SAI Transmit FIFO */
|
||||
#define FSL_SAI_TMR 0x60 /* SAI Transmit Mask */
|
||||
#define FSL_SAI_RCSR(ofs) (0x80 + ofs) /* SAI Receive Control */
|
||||
#define FSL_SAI_RCR1(ofs) (0x84 + ofs)/* SAI Receive Configuration 1 */
|
||||
#define FSL_SAI_RCR2(ofs) (0x88 + ofs) /* SAI Receive Configuration 2 */
|
||||
#define FSL_SAI_RCR3(ofs) (0x8c + ofs) /* SAI Receive Configuration 3 */
|
||||
#define FSL_SAI_RCR4(ofs) (0x90 + ofs) /* SAI Receive Configuration 4 */
|
||||
#define FSL_SAI_RCR5(ofs) (0x94 + ofs) /* SAI Receive Configuration 5 */
|
||||
#define FSL_SAI_RDR0 0xa0 /* SAI Receive Data 0 */
|
||||
#define FSL_SAI_RDR1 0xa4 /* SAI Receive Data 1 */
|
||||
#define FSL_SAI_RDR2 0xa8 /* SAI Receive Data 2 */
|
||||
#define FSL_SAI_RDR3 0xac /* SAI Receive Data 3 */
|
||||
#define FSL_SAI_RDR4 0xb0 /* SAI Receive Data 4 */
|
||||
#define FSL_SAI_RDR5 0xb4 /* SAI Receive Data 5 */
|
||||
#define FSL_SAI_RDR6 0xb8 /* SAI Receive Data 6 */
|
||||
#define FSL_SAI_RDR7 0xbc /* SAI Receive Data 7 */
|
||||
#define FSL_SAI_RFR0 0xc0 /* SAI Receive FIFO 0 */
|
||||
#define FSL_SAI_RFR1 0xc4 /* SAI Receive FIFO 1 */
|
||||
#define FSL_SAI_RFR2 0xc8 /* SAI Receive FIFO 2 */
|
||||
#define FSL_SAI_RFR3 0xcc /* SAI Receive FIFO 3 */
|
||||
#define FSL_SAI_RFR4 0xd0 /* SAI Receive FIFO 4 */
|
||||
#define FSL_SAI_RFR5 0xd4 /* SAI Receive FIFO 5 */
|
||||
#define FSL_SAI_RFR6 0xd8 /* SAI Receive FIFO 6 */
|
||||
#define FSL_SAI_RFR7 0xdc /* SAI Receive FIFO 7 */
|
||||
#define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */
|
||||
#define FSL_SAI_TTCTL 0x70 /* SAI Transmit Timestamp Control Register */
|
||||
#define FSL_SAI_TTCTN 0x74 /* SAI Transmit Timestamp Counter Register */
|
||||
#define FSL_SAI_TBCTN 0x78 /* SAI Transmit Bit Counter Register */
|
||||
#define FSL_SAI_TTCAP 0x7C /* SAI Transmit Timestamp Capture */
|
||||
|
||||
#define FSL_SAI_xCSR(tx, ofs) (tx ? FSL_SAI_TCSR(ofs) : FSL_SAI_RCSR(ofs))
|
||||
#define FSL_SAI_xCR1(tx, ofs) (tx ? FSL_SAI_TCR1(ofs) : FSL_SAI_RCR1(ofs))
|
||||
#define FSL_SAI_xCR2(tx, ofs) (tx ? FSL_SAI_TCR2(ofs) : FSL_SAI_RCR2(ofs))
|
||||
#define FSL_SAI_xCR3(tx, ofs) (tx ? FSL_SAI_TCR3(ofs) : FSL_SAI_RCR3(ofs))
|
||||
#define FSL_SAI_xCR4(tx, ofs) (tx ? FSL_SAI_TCR4(ofs) : FSL_SAI_RCR4(ofs))
|
||||
#define FSL_SAI_xCR5(tx, ofs) (tx ? FSL_SAI_TCR5(ofs) : FSL_SAI_RCR5(ofs))
|
||||
#define FSL_SAI_xDR(tx, ofs) (tx ? FSL_SAI_TDR(ofs) : FSL_SAI_RDR(ofs))
|
||||
#define FSL_SAI_xFR(tx, ofs) (tx ? FSL_SAI_TFR(ofs) : FSL_SAI_RFR(ofs))
|
||||
#define FSL_SAI_RCSR(offset) (0x80 + offset) /* SAI Receive Control */
|
||||
#define FSL_SAI_RCR1(offset) (0x84 + offset) /* SAI Receive Configuration 1 */
|
||||
#define FSL_SAI_RCR2(offset) (0x88 + offset) /* SAI Receive Configuration 2 */
|
||||
#define FSL_SAI_RCR3(offset) (0x8c + offset) /* SAI Receive Configuration 3 */
|
||||
#define FSL_SAI_RCR4(offset) (0x90 + offset) /* SAI Receive Configuration 4 */
|
||||
#define FSL_SAI_RCR5(offset) (0x94 + offset) /* SAI Receive Configuration 5 */
|
||||
#define FSL_SAI_RDR0 0xa0 /* SAI Receive Data */
|
||||
#define FSL_SAI_RDR1 0xa4 /* SAI Receive Data */
|
||||
#define FSL_SAI_RDR2 0xa8 /* SAI Receive Data */
|
||||
#define FSL_SAI_RDR3 0xac /* SAI Receive Data */
|
||||
#define FSL_SAI_RDR4 0xb0 /* SAI Receive Data */
|
||||
#define FSL_SAI_RDR5 0xb4 /* SAI Receive Data */
|
||||
#define FSL_SAI_RDR6 0xb8 /* SAI Receive Data */
|
||||
#define FSL_SAI_RDR7 0xbc /* SAI Receive Data */
|
||||
#define FSL_SAI_RFR0 0xc0 /* SAI Receive FIFO */
|
||||
#define FSL_SAI_RFR1 0xc4 /* SAI Receive FIFO */
|
||||
#define FSL_SAI_RFR2 0xc8 /* SAI Receive FIFO */
|
||||
#define FSL_SAI_RFR3 0xcc /* SAI Receive FIFO */
|
||||
#define FSL_SAI_RFR4 0xd0 /* SAI Receive FIFO */
|
||||
#define FSL_SAI_RFR5 0xd4 /* SAI Receive FIFO */
|
||||
#define FSL_SAI_RFR6 0xd8 /* SAI Receive FIFO */
|
||||
#define FSL_SAI_RFR7 0xdc /* SAI Receive FIFO */
|
||||
#define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */
|
||||
#define FSL_SAI_RTCTL 0xf0 /* SAI Receive Timestamp Control Register */
|
||||
#define FSL_SAI_RTCTN 0xf4 /* SAI Receive Timestamp Counter Register */
|
||||
#define FSL_SAI_RBCTN 0xf8 /* SAI Receive Bit Counter Register */
|
||||
#define FSL_SAI_RTCAP 0xfc /* SAI Receive Timestamp Capture */
|
||||
|
||||
#define FSL_SAI_MCTL 0x100 /* SAI MCLK Control Register */
|
||||
#define FSL_SAI_MDIV 0x104 /* SAI MCLK Divide Register */
|
||||
|
||||
#define FSL_SAI_xCSR(tx, off) (tx ? FSL_SAI_TCSR(off) : FSL_SAI_RCSR(off))
|
||||
#define FSL_SAI_xCR1(tx, off) (tx ? FSL_SAI_TCR1(off) : FSL_SAI_RCR1(off))
|
||||
#define FSL_SAI_xCR2(tx, off) (tx ? FSL_SAI_TCR2(off) : FSL_SAI_RCR2(off))
|
||||
#define FSL_SAI_xCR3(tx, off) (tx ? FSL_SAI_TCR3(off) : FSL_SAI_RCR3(off))
|
||||
#define FSL_SAI_xCR4(tx, off) (tx ? FSL_SAI_TCR4(off) : FSL_SAI_RCR4(off))
|
||||
#define FSL_SAI_xCR5(tx, off) (tx ? FSL_SAI_TCR5(off) : FSL_SAI_RCR5(off))
|
||||
#define FSL_SAI_xMR(tx) (tx ? FSL_SAI_TMR : FSL_SAI_RMR)
|
||||
|
||||
/* SAI Transmit/Receive Control Register */
|
||||
#define FSL_SAI_CSR_TERE BIT(31)
|
||||
#define FSL_SAI_CSR_SE BIT(30)
|
||||
#define FSL_SAI_CSR_FR BIT(25)
|
||||
#define FSL_SAI_CSR_SR BIT(24)
|
||||
#define FSL_SAI_CSR_xF_SHIFT 16
|
||||
|
@ -106,19 +122,29 @@
|
|||
#define FSL_SAI_CR2_MSEL(ID) ((ID) << 26)
|
||||
#define FSL_SAI_CR2_BCP BIT(25)
|
||||
#define FSL_SAI_CR2_BCD_MSTR BIT(24)
|
||||
#define FSL_SAI_CR2_BYP BIT(23) /* BCLK bypass */
|
||||
#define FSL_SAI_CR2_DIV_MASK 0xff
|
||||
|
||||
/* SAI Transmit and Receive Configuration 3 Register */
|
||||
#define FSL_SAI_CR3_TRCE BIT(16)
|
||||
#define FSL_SAI_CR3_TRCE_MASK GENMASK(23, 16)
|
||||
#define FSL_SAI_CR3_TRCE_MASK (0xff << 16)
|
||||
#define FSL_SAI_CR3_TRCE(x) (x << 16)
|
||||
#define FSL_SAI_CR3_WDFL(x) (x)
|
||||
#define FSL_SAI_CR3_WDFL_MASK 0x1f
|
||||
|
||||
/* SAI Transmit and Receive Configuration 4 Register */
|
||||
|
||||
#define FSL_SAI_CR4_FCONT BIT(28)
|
||||
#define FSL_SAI_CR4_FCOMB_SHIFT BIT(26)
|
||||
#define FSL_SAI_CR4_FCOMB_SOFT BIT(27)
|
||||
#define FSL_SAI_CR4_FCOMB_MASK (0x3 << 26)
|
||||
#define FSL_SAI_CR4_FPACK_8 (0x2 << 24)
|
||||
#define FSL_SAI_CR4_FPACK_16 (0x3 << 24)
|
||||
#define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16)
|
||||
#define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16)
|
||||
#define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8)
|
||||
#define FSL_SAI_CR4_SYWD_MASK (0x1f << 8)
|
||||
#define FSL_SAI_CR4_CHMOD (1 << 5)
|
||||
#define FSL_SAI_CR4_CHMOD_MASK (1 << 5)
|
||||
#define FSL_SAI_CR4_MF BIT(4)
|
||||
#define FSL_SAI_CR4_FSE BIT(3)
|
||||
#define FSL_SAI_CR4_FSP BIT(1)
|
||||
|
@ -132,6 +158,33 @@
|
|||
#define FSL_SAI_CR5_FBT(x) ((x) << 8)
|
||||
#define FSL_SAI_CR5_FBT_MASK (0x1f << 8)
|
||||
|
||||
/* SAI MCLK Control Register */
|
||||
#define FSL_SAI_MCTL_MCLK_EN BIT(30) /* MCLK Enable */
|
||||
#define FSL_SAI_MCTL_MSEL_MASK (0x3 << 24)
|
||||
#define FSL_SAI_MCTL_MSEL(ID) ((ID) << 24)
|
||||
#define FSL_SAI_MCTL_MSEL_BUS 0
|
||||
#define FSL_SAI_MCTL_MSEL_MCLK1 BIT(24)
|
||||
#define FSL_SAI_MCTL_MSEL_MCLK2 BIT(25)
|
||||
#define FSL_SAI_MCTL_MSEL_MCLK3 (BIT(24) | BIT(25))
|
||||
#define FSL_SAI_MCTL_DIV_EN BIT(23)
|
||||
#define FSL_SAI_MCTL_DIV_MASK 0xFF
|
||||
|
||||
/* SAI VERID Register */
|
||||
#define FSL_SAI_VER_ID_SHIFT 16
|
||||
#define FSL_SAI_VER_ID_MASK (0xFFFF << FSL_SAI_VER_ID_SHIFT)
|
||||
#define FSL_SAI_VER_EFIFO_EN BIT(0)
|
||||
#define FSL_SAI_VER_TSTMP_EN BIT(1)
|
||||
|
||||
/* SAI PARAM Register */
|
||||
#define FSL_SAI_PAR_SPF_SHIFT 16
|
||||
#define FSL_SAI_PAR_SPF_MASK (0x0F << FSL_SAI_PAR_SPF_SHIFT)
|
||||
#define FSL_SAI_PAR_WPF_SHIFT 8
|
||||
#define FSL_SAI_PAR_WPF_MASK (0x0F << FSL_SAI_PAR_WPF_SHIFT)
|
||||
#define FSL_SAI_PAR_DLN_MASK (0x0F)
|
||||
|
||||
/* SAI MCLK Divide Register */
|
||||
#define FSL_SAI_MDIV_MASK 0xFFFFF
|
||||
|
||||
/* SAI type */
|
||||
#define FSL_SAI_DMA BIT(0)
|
||||
#define FSL_SAI_USE_AC97 BIT(1)
|
||||
|
@ -155,11 +208,36 @@
|
|||
#define FSL_SAI_MAXBURST_TX 6
|
||||
#define FSL_SAI_MAXBURST_RX 6
|
||||
|
||||
#define SAI_FLAG_PMQOS BIT(0)
|
||||
|
||||
struct fsl_sai_soc_data {
|
||||
bool use_imx_pcm;
|
||||
bool use_edma;
|
||||
unsigned int fifo_depth;
|
||||
unsigned int reg_offset;
|
||||
unsigned int fifos;
|
||||
unsigned int dataline;
|
||||
unsigned int flags;
|
||||
unsigned char reg_offset;
|
||||
bool imx;
|
||||
/* True for EDMA because it needs period size multiple of maxburst */
|
||||
bool constrain_period_size;
|
||||
};
|
||||
|
||||
struct fsl_sai_verid {
|
||||
u32 id;
|
||||
bool timestamp_en;
|
||||
bool extfifo_en;
|
||||
bool loaded;
|
||||
};
|
||||
|
||||
struct fsl_sai_param {
|
||||
u32 spf; /* max slots per frame */
|
||||
u32 wpf; /* words in fifo */
|
||||
u32 dln; /* number of datalines implemented */
|
||||
};
|
||||
|
||||
struct fsl_sai_dl_cfg {
|
||||
unsigned int pins;
|
||||
unsigned int mask[2];
|
||||
unsigned int offset[2];
|
||||
};
|
||||
|
||||
struct fsl_sai {
|
||||
|
@ -167,21 +245,39 @@ struct fsl_sai {
|
|||
struct regmap *regmap;
|
||||
struct clk *bus_clk;
|
||||
struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
|
||||
struct clk *pll8k_clk;
|
||||
struct clk *pll11k_clk;
|
||||
|
||||
bool is_slave_mode;
|
||||
bool slave_mode[2];
|
||||
bool is_lsb_first;
|
||||
bool is_dsp_mode;
|
||||
bool is_multi_lane;
|
||||
bool synchronous[2];
|
||||
bool is_stream_opened[2];
|
||||
bool is_dsd;
|
||||
|
||||
int pcm_dl_cfg_cnt;
|
||||
int dsd_dl_cfg_cnt;
|
||||
struct fsl_sai_dl_cfg *pcm_dl_cfg;
|
||||
struct fsl_sai_dl_cfg *dsd_dl_cfg;
|
||||
|
||||
unsigned int masterflag[2];
|
||||
|
||||
unsigned int mclk_id[2];
|
||||
unsigned int mclk_streams;
|
||||
unsigned int slots;
|
||||
unsigned int slot_width;
|
||||
unsigned int bclk_ratio;
|
||||
unsigned int bitclk_ratio;
|
||||
|
||||
const struct fsl_sai_soc_data *soc_data;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
const struct fsl_sai_soc_data *soc;
|
||||
struct pm_qos_request pm_qos_req;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_state;
|
||||
|
||||
struct fsl_sai_verid verid;
|
||||
struct fsl_sai_param param;
|
||||
};
|
||||
|
||||
#define TX 1
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
|
||||
//
|
||||
// Copyright (C) 2013 Freescale Semiconductor, Inc.
|
||||
// Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
|
||||
//
|
||||
// Based on stmp3xxx_spdif_dai.c
|
||||
// Vladimir Barinov <vbarinov@embeddedalley.com>
|
||||
|
@ -11,11 +11,15 @@
|
|||
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/busfreq-imx.h>
|
||||
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
@ -42,6 +46,16 @@ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
|
|||
|
||||
#define DEFAULT_RXCLK_SRC 1
|
||||
|
||||
struct fsl_spdif_soc_data {
|
||||
bool imx;
|
||||
bool constrain_period_size;
|
||||
u32 tx_burst;
|
||||
u32 rx_burst;
|
||||
u32 interrupts;
|
||||
u64 tx_formats;
|
||||
u64 rx_rates;
|
||||
};
|
||||
|
||||
/*
|
||||
* SPDIF control structure
|
||||
* Defines channel status, subcode and Q sub
|
||||
|
@ -99,15 +113,58 @@ struct fsl_spdif_priv {
|
|||
u16 sysclk_df[SPDIF_TXRATE_MAX];
|
||||
u8 txclk_src[SPDIF_TXRATE_MAX];
|
||||
u8 rxclk_src;
|
||||
struct clk *txclk[SPDIF_TXRATE_MAX];
|
||||
struct clk *txclk[STC_TXCLK_SRC_MAX];
|
||||
struct clk *rxclk;
|
||||
struct clk *coreclk;
|
||||
struct clk *sysclk;
|
||||
struct clk *spbaclk;
|
||||
const struct fsl_spdif_soc_data *soc;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
/* regcache for SRPC */
|
||||
u32 regcache_srpc;
|
||||
struct clk *pll8k_clk;
|
||||
struct clk *pll11k_clk;
|
||||
};
|
||||
|
||||
static struct fsl_spdif_soc_data fsl_spdif_vf610 = {
|
||||
.imx = false,
|
||||
.tx_burst = FSL_SPDIF_TXFIFO_WML,
|
||||
.rx_burst = FSL_SPDIF_RXFIFO_WML,
|
||||
.interrupts = 1,
|
||||
.tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
|
||||
.rx_rates = FSL_SPDIF_RATES_CAPTURE,
|
||||
.constrain_period_size = false,
|
||||
};
|
||||
|
||||
static struct fsl_spdif_soc_data fsl_spdif_imx35 = {
|
||||
.imx = true,
|
||||
.tx_burst = FSL_SPDIF_TXFIFO_WML,
|
||||
.rx_burst = FSL_SPDIF_RXFIFO_WML,
|
||||
.interrupts = 1,
|
||||
.tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
|
||||
.rx_rates = FSL_SPDIF_RATES_CAPTURE,
|
||||
.constrain_period_size = false,
|
||||
};
|
||||
|
||||
static struct fsl_spdif_soc_data fsl_spdif_imx8qm = {
|
||||
.imx = true,
|
||||
.tx_burst = 2,
|
||||
.rx_burst = 2,
|
||||
.interrupts = 2,
|
||||
.tx_formats = SNDRV_PCM_FMTBIT_S24_LE,
|
||||
.rx_rates = (FSL_SPDIF_RATES_CAPTURE | SNDRV_PCM_RATE_192000),
|
||||
.constrain_period_size = true,
|
||||
};
|
||||
|
||||
static struct fsl_spdif_soc_data fsl_spdif_imx8mm = {
|
||||
.imx = true,
|
||||
.tx_burst = FSL_SPDIF_TXFIFO_WML,
|
||||
.rx_burst = FSL_SPDIF_RXFIFO_WML,
|
||||
.interrupts = 1,
|
||||
.tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
|
||||
.rx_rates = (FSL_SPDIF_RATES_CAPTURE | SNDRV_PCM_RATE_192000),
|
||||
.constrain_period_size = false,
|
||||
};
|
||||
|
||||
/* DPLL locked and lock loss interrupt handler */
|
||||
|
@ -378,7 +435,6 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
|||
u32 stc, mask, rate;
|
||||
u16 sysclk_df;
|
||||
u8 clk, txclk_df;
|
||||
int ret;
|
||||
|
||||
switch (sample_rate) {
|
||||
case 32000:
|
||||
|
@ -420,23 +476,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
|||
|
||||
sysclk_df = spdif_priv->sysclk_df[rate];
|
||||
|
||||
/* Don't mess up the clocks from other modules */
|
||||
if (clk != STC_TXCLK_SPDIF_ROOT)
|
||||
goto clk_set_bypass;
|
||||
|
||||
/* The S/PDIF block needs a clock of 64 * fs * txclk_df */
|
||||
ret = clk_set_rate(spdif_priv->txclk[rate],
|
||||
64 * sample_rate * txclk_df);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to set tx clock rate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_set_bypass:
|
||||
dev_dbg(&pdev->dev, "expected clock rate = %d\n",
|
||||
(64 * sample_rate * txclk_df * sysclk_df));
|
||||
dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
|
||||
clk_get_rate(spdif_priv->txclk[rate]));
|
||||
clk_get_rate(spdif_priv->txclk[clk]));
|
||||
|
||||
/* set fs field in consumer channel status */
|
||||
spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
|
||||
|
@ -462,25 +505,10 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
|||
struct platform_device *pdev = spdif_priv->pdev;
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 scr, mask;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/* Reset module and interrupts only for first initialization */
|
||||
if (!cpu_dai->active) {
|
||||
ret = clk_prepare_enable(spdif_priv->coreclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable core clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!IS_ERR(spdif_priv->spbaclk)) {
|
||||
ret = clk_prepare_enable(spdif_priv->spbaclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable spba clock\n");
|
||||
goto err_spbaclk;
|
||||
}
|
||||
}
|
||||
|
||||
ret = spdif_softreset(spdif_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to soft reset\n");
|
||||
|
@ -498,35 +526,30 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
|||
mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
|
||||
SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
|
||||
SCR_TXFIFO_FSEL_MASK;
|
||||
for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
|
||||
ret = clk_prepare_enable(spdif_priv->txclk[i]);
|
||||
if (ret)
|
||||
goto disable_txclk;
|
||||
}
|
||||
} else {
|
||||
scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
|
||||
mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
|
||||
SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
|
||||
ret = clk_prepare_enable(spdif_priv->rxclk);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
|
||||
|
||||
/* Power up SPDIF module */
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
|
||||
|
||||
if (spdif_priv->soc->constrain_period_size) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
spdif_priv->dma_params_tx.maxburst);
|
||||
else
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
spdif_priv->dma_params_rx.maxburst);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
disable_txclk:
|
||||
for (i--; i >= 0; i--)
|
||||
clk_disable_unprepare(spdif_priv->txclk[i]);
|
||||
err:
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
err_spbaclk:
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -536,20 +559,17 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 scr, mask, i;
|
||||
u32 scr, mask;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
scr = 0;
|
||||
mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
|
||||
SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
|
||||
SCR_TXFIFO_FSEL_MASK;
|
||||
for (i = 0; i < SPDIF_TXRATE_MAX; i++)
|
||||
clk_disable_unprepare(spdif_priv->txclk[i]);
|
||||
} else {
|
||||
scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
|
||||
mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
|
||||
SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
|
||||
clk_disable_unprepare(spdif_priv->rxclk);
|
||||
}
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
|
||||
|
||||
|
@ -558,9 +578,6 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
|
|||
spdif_intr_status_clear(spdif_priv);
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR,
|
||||
SCR_LOW_POWER, SCR_LOW_POWER);
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,14 +640,178 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
|
||||
struct clk *clk, u64 savesub,
|
||||
enum spdif_txrate index, bool round)
|
||||
{
|
||||
static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
|
||||
bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk);
|
||||
u64 rate_actual, sub;
|
||||
u32 arate;
|
||||
u16 sysclk_dfmin, sysclk_dfmax, sysclk_df;
|
||||
u8 txclk_df;
|
||||
|
||||
/* The sysclk has an extra divisor [2, 512] */
|
||||
sysclk_dfmin = is_sysclk ? 2 : 1;
|
||||
sysclk_dfmax = is_sysclk ? 512 : 1;
|
||||
|
||||
for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
|
||||
for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
|
||||
|
||||
rate_actual = clk_get_rate(clk);
|
||||
|
||||
arate = rate_actual / 64;
|
||||
arate /= txclk_df * sysclk_df;
|
||||
|
||||
if (arate == rate[index]) {
|
||||
/* We are lucky */
|
||||
savesub = 0;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
goto out;
|
||||
} else if (arate / rate[index] == 1) {
|
||||
/* A little bigger than expect */
|
||||
sub = (u64)(arate - rate[index]) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
} else if (rate[index] / arate == 1) {
|
||||
/* A little smaller than expect */
|
||||
sub = (u64)(rate[index] - arate) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return savesub;
|
||||
}
|
||||
|
||||
static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
|
||||
enum spdif_txrate index)
|
||||
{
|
||||
static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
|
||||
struct platform_device *pdev = spdif_priv->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
u64 savesub = 100000, ret;
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
|
||||
clk = spdif_priv->txclk[i];
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "no rxtx%d clock in devicetree\n", i);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
if (!clk_get_rate(clk))
|
||||
continue;
|
||||
|
||||
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
|
||||
i == STC_TXCLK_SPDIF_ROOT);
|
||||
if (savesub == ret)
|
||||
continue;
|
||||
|
||||
savesub = ret;
|
||||
spdif_priv->txclk_src[index] = i;
|
||||
|
||||
/* To quick catch a divisor, we allow a 0.1% deviation */
|
||||
if (savesub < 100)
|
||||
break;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
|
||||
spdif_priv->txclk_src[index], rate[index]);
|
||||
dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
|
||||
spdif_priv->txclk_df[index], rate[index]);
|
||||
if (clk_is_match(spdif_priv->txclk[spdif_priv->txclk_src[index]], spdif_priv->sysclk))
|
||||
dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
|
||||
spdif_priv->sysclk_df[index], rate[index]);
|
||||
dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
|
||||
rate[index], spdif_priv->txrate[index]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_spdif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct fsl_spdif_priv *data = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct platform_device *pdev = data->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk, *p, *pll = 0, *npll = 0;
|
||||
u64 ratio = freq;
|
||||
int ret, i;
|
||||
bool reparent = false;
|
||||
|
||||
if (dir != SND_SOC_CLOCK_OUT || freq == 0 || clk_id != STC_TXCLK_SPDIF_ROOT)
|
||||
return 0;
|
||||
|
||||
if (data->pll8k_clk == NULL || data->pll11k_clk == NULL)
|
||||
return 0;
|
||||
|
||||
clk = data->txclk[clk_id];
|
||||
if (IS_ERR_OR_NULL(clk)) {
|
||||
dev_err(dev, "no rxtx%d clock in devicetree\n", clk_id);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
p = clk;
|
||||
while (p && data->pll8k_clk && data->pll11k_clk) {
|
||||
struct clk *pp = clk_get_parent(p);
|
||||
|
||||
if (clk_is_match(pp, data->pll8k_clk) ||
|
||||
clk_is_match(pp, data->pll11k_clk)) {
|
||||
pll = pp;
|
||||
break;
|
||||
}
|
||||
p = pp;
|
||||
}
|
||||
|
||||
npll = (do_div(ratio, 8000) ? data->pll11k_clk : data->pll8k_clk);
|
||||
reparent = (pll && !clk_is_match(pll, npll));
|
||||
|
||||
clk_disable_unprepare(clk);
|
||||
if (reparent) {
|
||||
ret = clk_set_parent(p, npll);
|
||||
if (ret < 0)
|
||||
dev_warn(cpu_dai->dev, "failed to set parent %s: %d\n",
|
||||
__clk_get_name(npll), ret);
|
||||
}
|
||||
|
||||
ret = clk_set_rate(clk, freq);
|
||||
if (ret < 0)
|
||||
dev_warn(cpu_dai->dev, "failed to set clock rate (%u): %d\n",
|
||||
freq, ret);
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
|
||||
ret = fsl_spdif_probe_txclk(data, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops fsl_spdif_dai_ops = {
|
||||
.startup = fsl_spdif_startup,
|
||||
.set_sysclk = fsl_spdif_set_dai_sysclk,
|
||||
.hw_params = fsl_spdif_hw_params,
|
||||
.trigger = fsl_spdif_trigger,
|
||||
.shutdown = fsl_spdif_shutdown,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* FSL SPDIF IEC958 controller(mixer) functions
|
||||
*
|
||||
|
@ -769,19 +950,8 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
|
|||
}
|
||||
|
||||
/* Valid bit information */
|
||||
static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get valid good bit from interrupt status register */
|
||||
static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
|
||||
static int fsl_spdif_rx_vbit_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
|
@ -796,6 +966,68 @@ static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_spdif_tx_vbit_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 val;
|
||||
|
||||
regmap_read(regmap, REG_SPDIF_SCR, &val);
|
||||
val = (val & SCR_VAL_MASK) >> SCR_VAL_OFFSET;
|
||||
val = 1 - val;
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_spdif_tx_vbit_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 val = (1 - ucontrol->value.integer.value[0]) << SCR_VAL_OFFSET;
|
||||
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_VAL_MASK, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_spdif_rx_rcm_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 val;
|
||||
|
||||
regmap_read(regmap, REG_SPDIF_SCR, &val);
|
||||
val = (val & SCR_RAW_CAPTURE_MODE) ? 1 : 0;
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_spdif_rx_rcm_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct regmap *regmap = spdif_priv->regmap;
|
||||
u32 val = (ucontrol->value.integer.value[0] ? SCR_RAW_CAPTURE_MODE : 0);
|
||||
|
||||
if (val)
|
||||
cpu_dai->driver->capture.formats |= SNDRV_PCM_FMTBIT_S32_LE;
|
||||
else
|
||||
cpu_dai->driver->capture.formats &= ~SNDRV_PCM_FMTBIT_S32_LE;
|
||||
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_RAW_CAPTURE_MODE, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* DPLL lock information */
|
||||
static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
|
@ -863,18 +1095,6 @@ static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* User bit sync mode info */
|
||||
static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* User bit sync mode:
|
||||
* 1 CD User channel subcode
|
||||
|
@ -953,11 +1173,21 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
|
|||
/* Valid bit error controller */
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 V-Bit Errors",
|
||||
.name = "IEC958 Rx V-Bit Errors",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = fsl_spdif_vbit_info,
|
||||
.get = fsl_spdif_vbit_get,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = fsl_spdif_rx_vbit_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 Tx V-Bit",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_WRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = fsl_spdif_tx_vbit_get,
|
||||
.put = fsl_spdif_tx_vbit_put,
|
||||
},
|
||||
/* DPLL lock info get controller */
|
||||
{
|
||||
|
@ -975,10 +1205,20 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
|
|||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_WRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = fsl_spdif_usync_info,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = fsl_spdif_usync_get,
|
||||
.put = fsl_spdif_usync_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "IEC958 Rx Raw Capture Mode Bit",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_WRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = fsl_spdif_rx_rcm_get,
|
||||
.put = fsl_spdif_rx_rcm_put,
|
||||
},
|
||||
};
|
||||
|
||||
static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
|
||||
|
@ -1103,114 +1343,14 @@ static const struct regmap_config fsl_spdif_regmap_config = {
|
|||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
|
||||
struct clk *clk, u64 savesub,
|
||||
enum spdif_txrate index, bool round)
|
||||
{
|
||||
static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
|
||||
bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk);
|
||||
u64 rate_ideal, rate_actual, sub;
|
||||
u32 arate;
|
||||
u16 sysclk_dfmin, sysclk_dfmax, sysclk_df;
|
||||
u8 txclk_df;
|
||||
|
||||
/* The sysclk has an extra divisor [2, 512] */
|
||||
sysclk_dfmin = is_sysclk ? 2 : 1;
|
||||
sysclk_dfmax = is_sysclk ? 512 : 1;
|
||||
|
||||
for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
|
||||
for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
|
||||
rate_ideal = rate[index] * txclk_df * 64ULL;
|
||||
if (round)
|
||||
rate_actual = clk_round_rate(clk, rate_ideal);
|
||||
else
|
||||
rate_actual = clk_get_rate(clk);
|
||||
|
||||
arate = rate_actual / 64;
|
||||
arate /= txclk_df * sysclk_df;
|
||||
|
||||
if (arate == rate[index]) {
|
||||
/* We are lucky */
|
||||
savesub = 0;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
goto out;
|
||||
} else if (arate / rate[index] == 1) {
|
||||
/* A little bigger than expect */
|
||||
sub = (u64)(arate - rate[index]) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
} else if (rate[index] / arate == 1) {
|
||||
/* A little smaller than expect */
|
||||
sub = (u64)(rate[index] - arate) * 100000;
|
||||
do_div(sub, rate[index]);
|
||||
if (sub >= savesub)
|
||||
continue;
|
||||
savesub = sub;
|
||||
spdif_priv->txclk_df[index] = txclk_df;
|
||||
spdif_priv->sysclk_df[index] = sysclk_df;
|
||||
spdif_priv->txrate[index] = arate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return savesub;
|
||||
}
|
||||
|
||||
static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
|
||||
enum spdif_txrate index)
|
||||
{
|
||||
static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
|
||||
struct platform_device *pdev = spdif_priv->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
u64 savesub = 100000, ret;
|
||||
struct clk *clk;
|
||||
char tmp[16];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
|
||||
sprintf(tmp, "rxtx%d", i);
|
||||
clk = devm_clk_get(&pdev->dev, tmp);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "no rxtx%d clock in devicetree\n", i);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
if (!clk_get_rate(clk))
|
||||
continue;
|
||||
|
||||
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
|
||||
i == STC_TXCLK_SPDIF_ROOT);
|
||||
if (savesub == ret)
|
||||
continue;
|
||||
|
||||
savesub = ret;
|
||||
spdif_priv->txclk[index] = clk;
|
||||
spdif_priv->txclk_src[index] = i;
|
||||
|
||||
/* To quick catch a divisor, we allow a 0.1% deviation */
|
||||
if (savesub < 100)
|
||||
break;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
|
||||
spdif_priv->txclk_src[index], rate[index]);
|
||||
dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
|
||||
spdif_priv->txclk_df[index], rate[index]);
|
||||
if (clk_is_match(spdif_priv->txclk[index], spdif_priv->sysclk))
|
||||
dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
|
||||
spdif_priv->sysclk_df[index], rate[index]);
|
||||
dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
|
||||
rate[index], spdif_priv->txrate[index]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static const struct of_device_id fsl_spdif_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx8mm-spdif", .data = &fsl_spdif_imx8mm, },
|
||||
{ .compatible = "fsl,imx8qm-spdif", .data = &fsl_spdif_imx8qm, },
|
||||
{ .compatible = "fsl,imx35-spdif", .data = &fsl_spdif_imx35, },
|
||||
{ .compatible = "fsl,vf610-spdif", .data = &fsl_spdif_vf610, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
|
||||
|
||||
static int fsl_spdif_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -1218,8 +1358,11 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
struct fsl_spdif_priv *spdif_priv;
|
||||
struct spdif_mixer_control *ctrl;
|
||||
struct resource *res;
|
||||
const struct of_device_id *of_id;
|
||||
void __iomem *regs;
|
||||
int irq, ret, i;
|
||||
char tmp[16];
|
||||
int num_domains = 0;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
@ -1230,9 +1373,19 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
|
||||
spdif_priv->pdev = pdev;
|
||||
|
||||
of_id = of_match_device(fsl_spdif_dt_ids, &pdev->dev);
|
||||
if (!of_id || !of_id->data)
|
||||
return -EINVAL;
|
||||
|
||||
spdif_priv->soc = of_id->data;
|
||||
|
||||
/* Initialize this copy of the CPU DAI driver structure */
|
||||
memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
|
||||
spdif_priv->cpu_dai_drv.name = dev_name(&pdev->dev);
|
||||
spdif_priv->cpu_dai_drv.playback.formats =
|
||||
spdif_priv->soc->tx_formats;
|
||||
spdif_priv->cpu_dai_drv.capture.rates =
|
||||
spdif_priv->soc->rx_rates;
|
||||
|
||||
/* Get the addresses and IRQ */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -1248,8 +1401,10 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
|
||||
dev_name(&pdev->dev), spdif_priv);
|
||||
|
@ -1258,8 +1413,50 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (spdif_priv->soc->interrupts > 1) {
|
||||
irq = platform_get_irq(pdev, 1);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
|
||||
dev_name(&pdev->dev), spdif_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not claim irq %u\n", irq);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
num_domains = of_count_phandle_with_args(np, "power-domains",
|
||||
"#power-domain-cells");
|
||||
for (i = 0; i < num_domains; i++) {
|
||||
struct device *pd_dev;
|
||||
struct device_link *link;
|
||||
|
||||
pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i);
|
||||
if (IS_ERR(pd_dev))
|
||||
return PTR_ERR(pd_dev);
|
||||
|
||||
link = device_link_add(&pdev->dev, pd_dev,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (IS_ERR(link))
|
||||
return PTR_ERR(link);
|
||||
}
|
||||
|
||||
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
|
||||
sprintf(tmp, "rxtx%d", i);
|
||||
spdif_priv->txclk[i] = devm_clk_get(&pdev->dev, tmp);
|
||||
if (IS_ERR(spdif_priv->txclk[i])) {
|
||||
dev_err(&pdev->dev, "no rxtx%d clock in devicetree\n", i);
|
||||
return PTR_ERR(spdif_priv->txclk[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get system clock for rx clock rate calculation */
|
||||
spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5");
|
||||
spdif_priv->sysclk = spdif_priv->txclk[5];
|
||||
if (IS_ERR(spdif_priv->sysclk)) {
|
||||
dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n");
|
||||
return PTR_ERR(spdif_priv->sysclk);
|
||||
|
@ -1277,13 +1474,21 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
dev_warn(&pdev->dev, "no spba clock in devicetree\n");
|
||||
|
||||
/* Select clock source for rx/tx clock */
|
||||
spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
|
||||
spdif_priv->rxclk = spdif_priv->txclk[1];
|
||||
if (IS_ERR(spdif_priv->rxclk)) {
|
||||
dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n");
|
||||
return PTR_ERR(spdif_priv->rxclk);
|
||||
}
|
||||
spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
|
||||
|
||||
spdif_priv->pll8k_clk = devm_clk_get(&pdev->dev, "pll8k");
|
||||
if (IS_ERR(spdif_priv->pll8k_clk))
|
||||
spdif_priv->pll8k_clk = NULL;
|
||||
|
||||
spdif_priv->pll11k_clk = devm_clk_get(&pdev->dev, "pll11k");
|
||||
if (IS_ERR(spdif_priv->pll11k_clk))
|
||||
spdif_priv->pll11k_clk = NULL;
|
||||
|
||||
for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
|
||||
ret = fsl_spdif_probe_txclk(spdif_priv, i);
|
||||
if (ret)
|
||||
|
@ -1304,11 +1509,17 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
|
||||
spdif_priv->dpll_locked = false;
|
||||
|
||||
spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML;
|
||||
spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML;
|
||||
spdif_priv->dma_params_tx.maxburst = spdif_priv->soc->tx_burst;
|
||||
spdif_priv->dma_params_rx.maxburst = spdif_priv->soc->rx_burst;
|
||||
spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL;
|
||||
spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL;
|
||||
|
||||
/*Clear the val bit for Tx*/
|
||||
regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SCR,
|
||||
SCR_VAL_MASK, 1 << SCR_VAL_OFFSET);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
/* Register with ASoC */
|
||||
dev_set_drvdata(&pdev->dev, spdif_priv);
|
||||
|
||||
|
@ -1326,45 +1537,88 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int fsl_spdif_suspend(struct device *dev)
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_spdif_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
|
||||
&spdif_priv->regcache_srpc);
|
||||
ret = clk_prepare_enable(spdif_priv->coreclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable core clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regcache_cache_only(spdif_priv->regmap, true);
|
||||
regcache_mark_dirty(spdif_priv->regmap);
|
||||
if (!IS_ERR(spdif_priv->spbaclk)) {
|
||||
ret = clk_prepare_enable(spdif_priv->spbaclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable spba clock\n");
|
||||
goto disable_core_clk;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
|
||||
ret = clk_prepare_enable(spdif_priv->txclk[i]);
|
||||
if (ret)
|
||||
goto disable_spba_clk;
|
||||
}
|
||||
|
||||
static int fsl_spdif_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
|
||||
request_bus_freq(BUS_FREQ_HIGH);
|
||||
|
||||
regcache_cache_only(spdif_priv->regmap, false);
|
||||
regcache_mark_dirty(spdif_priv->regmap);
|
||||
|
||||
regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC,
|
||||
SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
|
||||
spdif_priv->regcache_srpc);
|
||||
|
||||
return regcache_sync(spdif_priv->regmap);
|
||||
ret = regcache_sync(spdif_priv->regmap);
|
||||
if (ret)
|
||||
goto disable_tx_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_tx_clk:
|
||||
disable_spba_clk:
|
||||
for (i--; i >= 0; i--)
|
||||
clk_disable_unprepare(spdif_priv->txclk[i]);
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
disable_core_clk:
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static int fsl_spdif_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
|
||||
&spdif_priv->regcache_srpc);
|
||||
regcache_cache_only(spdif_priv->regmap, true);
|
||||
|
||||
release_bus_freq(BUS_FREQ_HIGH);
|
||||
|
||||
for (i = 0; i < STC_TXCLK_SRC_MAX; i++)
|
||||
clk_disable_unprepare(spdif_priv->txclk[i]);
|
||||
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops fsl_spdif_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(fsl_spdif_runtime_suspend, fsl_spdif_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id fsl_spdif_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx35-spdif", },
|
||||
{ .compatible = "fsl,vf610-spdif", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
|
||||
|
||||
static struct platform_driver fsl_spdif_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-spdif-dai",
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#define SCR_TXFIFO_FSEL_IF4 (0x1 << SCR_TXFIFO_FSEL_OFFSET)
|
||||
#define SCR_TXFIFO_FSEL_IF8 (0x2 << SCR_TXFIFO_FSEL_OFFSET)
|
||||
#define SCR_TXFIFO_FSEL_IF12 (0x3 << SCR_TXFIFO_FSEL_OFFSET)
|
||||
#define SCR_RAW_CAPTURE_MODE (1 << 14)
|
||||
#define SCR_LOW_POWER (1 << 13)
|
||||
#define SCR_SOFT_RESET (1 << 12)
|
||||
#define SCR_TXFIFO_CTRL_OFFSET 10
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/busfreq-imx.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
|
@ -266,6 +268,7 @@ struct fsl_ssi {
|
|||
bool synchronous;
|
||||
bool use_dma;
|
||||
bool use_dual_fifo;
|
||||
bool use_dyna_fifo;
|
||||
bool has_ipg_clk_name;
|
||||
unsigned int fifo_depth;
|
||||
unsigned int slot_width;
|
||||
|
@ -644,7 +647,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
|||
* task from fifo0, fifo1 would be neglected at the end of each
|
||||
* period. But SSI would still access fifo1 with an invalid data.
|
||||
*/
|
||||
if (ssi->use_dual_fifo)
|
||||
if (ssi->use_dual_fifo || ssi->use_dyna_fifo)
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
|
||||
|
||||
|
@ -798,6 +801,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
|
|||
unsigned int sample_size = params_width(hw_params);
|
||||
u32 wl = SSI_SxCCR_WL(sample_size);
|
||||
int ret;
|
||||
struct fsl_ssi_regvals *vals = ssi->regvals;
|
||||
|
||||
if (fsl_ssi_is_i2s_master(ssi)) {
|
||||
ret = fsl_ssi_set_bclk(substream, dai, hw_params);
|
||||
|
@ -847,6 +851,24 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
|
|||
tx2 = tx || ssi->synchronous;
|
||||
regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl);
|
||||
|
||||
if (ssi->use_dyna_fifo) {
|
||||
if (channels == 1) {
|
||||
ssi->dma_params_tx.fifo_num = 1;
|
||||
ssi->dma_params_rx.fifo_num = 1;
|
||||
vals[RX].srcr &= ~SSI_SRCR_RFEN1;
|
||||
vals[TX].stcr &= ~SSI_STCR_TFEN1;
|
||||
vals[RX].scr &= ~SSI_SCR_TCH_EN;
|
||||
vals[TX].scr &= ~SSI_SCR_TCH_EN;
|
||||
} else {
|
||||
ssi->dma_params_tx.fifo_num = 2;
|
||||
ssi->dma_params_rx.fifo_num = 2;
|
||||
vals[RX].srcr |= SSI_SRCR_RFEN1;
|
||||
vals[TX].stcr |= SSI_STCR_TFEN1;
|
||||
vals[RX].scr |= SSI_SCR_TCH_EN;
|
||||
vals[TX].scr |= SSI_SCR_TCH_EN;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1324,6 +1346,8 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
|
|||
dev_dbg(dev, "failed to get baud clock: %ld\n",
|
||||
PTR_ERR(ssi->baudclk));
|
||||
|
||||
ssi->dma_params_rx.chan_name = "rx";
|
||||
ssi->dma_params_tx.chan_name = "tx";
|
||||
ssi->dma_params_tx.maxburst = ssi->dma_maxburst;
|
||||
ssi->dma_params_rx.maxburst = ssi->dma_maxburst;
|
||||
ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0;
|
||||
|
@ -1349,7 +1373,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
|
|||
if (ret)
|
||||
goto error_pcm;
|
||||
} else {
|
||||
ret = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE);
|
||||
ret = imx_pcm_platform_register(&pdev->dev);
|
||||
if (ret)
|
||||
goto error_pcm;
|
||||
}
|
||||
|
@ -1430,6 +1454,8 @@ static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi)
|
|||
if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL)
|
||||
ssi->use_dual_fifo = true;
|
||||
|
||||
if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
|
||||
ssi->use_dyna_fifo = true;
|
||||
/*
|
||||
* Backward compatible for older bindings by manually triggering the
|
||||
* machine driver's probe(). Use /compatible property, including the
|
||||
|
@ -1510,8 +1536,10 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
ssi->irq = platform_get_irq(pdev, 0);
|
||||
if (ssi->irq < 0)
|
||||
if (ssi->irq < 0) {
|
||||
dev_err(dev, "no irq for node %s\n", pdev->name);
|
||||
return ssi->irq;
|
||||
}
|
||||
|
||||
/* Set software limitations for synchronous mode except AC97 */
|
||||
if (ssi->synchronous && !fsl_ssi_is_ac97(ssi)) {
|
||||
|
@ -1548,6 +1576,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
dev_set_drvdata(dev, ssi);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
if (ssi->soc->imx) {
|
||||
ret = fsl_ssi_imx_probe(pdev, ssi, iomem);
|
||||
|
@ -1647,6 +1676,20 @@ static int fsl_ssi_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_ssi_runtime_resume(struct device *dev)
|
||||
{
|
||||
request_bus_freq(BUS_FREQ_AUDIO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_ssi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
release_bus_freq(BUS_FREQ_AUDIO);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int fsl_ssi_suspend(struct device *dev)
|
||||
{
|
||||
|
@ -1681,6 +1724,8 @@ static int fsl_ssi_resume(struct device *dev)
|
|||
|
||||
static const struct dev_pm_ops fsl_ssi_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
|
||||
SET_RUNTIME_PM_OPS(fsl_ssi_runtime_suspend, fsl_ssi_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver fsl_ssi_driver = {
|
||||
|
|
246
sound/soc/fsl/hdmi_pcm.S
Normal file
246
sound/soc/fsl/hdmi_pcm.S
Normal file
|
@ -0,0 +1,246 @@
|
|||
/**
|
||||
* Copyright (C) 2010-2014 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
.section .text
|
||||
|
||||
.global hdmi_dma_copy_16_neon_lut
|
||||
.global hdmi_dma_copy_16_neon_fast
|
||||
.global hdmi_dma_copy_24_neon_lut
|
||||
.global hdmi_dma_copy_24_neon_fast
|
||||
|
||||
|
||||
/**
|
||||
* hdmi_dma_copy_16_neon_lut
|
||||
* Convert pcm sample to iec sample. Pcm sample is 16 bits.
|
||||
* Frame index's between 0 and 47 inclusively. Channel count can be 1, 2, 4, 8.
|
||||
* Frame count should be multipliable by 4, and Sample count by 8.
|
||||
*
|
||||
* C Prototype
|
||||
* void hdmi_dma_copy_16_neon_lut(unsigned short *src, unsigned int *dst,
|
||||
* int samples, unsigned char *lookup_table);
|
||||
* Return value
|
||||
* None
|
||||
* Parameters
|
||||
* src Source PCM16 samples
|
||||
* dst Dest buffer to store pcm with header
|
||||
* samples Contains sample count (=frame_count * channel_count)
|
||||
* lookup_table Preconstructed header table. Channels interleaved.
|
||||
*/
|
||||
|
||||
hdmi_dma_copy_16_neon_lut:
|
||||
mov r12, #1 /* construct vector(1) */
|
||||
vdup.8 d6, r12
|
||||
|
||||
hdmi_dma_copy_16_neon_lut_start:
|
||||
|
||||
/* get 8 samples to q0 */
|
||||
vld1.16 {d0, d1}, [r0]! /* TODO: aligned */
|
||||
|
||||
/* pld [r1, #(64*4)] */
|
||||
|
||||
/* xor every bit */
|
||||
vcnt.8 q1, q0 /* count of 1s */
|
||||
vpadd.i8 d2, d2, d3 /* only care about the LST in every element */
|
||||
vand d2, d2, d6 /* clear other bits while keep the least bit */
|
||||
vshl.u8 d2, d2, #3 /* bit p: d2 = d2 << 3 */
|
||||
|
||||
/* get packet header */
|
||||
vld1.8 {d5}, [r3]!
|
||||
veor d4, d5, d2 /* xor bit c */
|
||||
|
||||
/* store: (d4 << 16 | q0) << 8 */
|
||||
vmovl.u8 q2, d4 /* expand from char to short */
|
||||
vzip.16 q0, q2
|
||||
vshl.u32 q0, q0, #8
|
||||
vshl.u32 q1, q2, #8
|
||||
vst1.32 {d0, d1, d2, d3}, [r1]!
|
||||
|
||||
/* decrease sample count */
|
||||
subs r2, r2, #8
|
||||
bne hdmi_dma_copy_16_neon_lut_start
|
||||
|
||||
mov pc, lr
|
||||
|
||||
/**
|
||||
* hdmi_dma_copy_16_neon_fast
|
||||
* Convert pcm sample to iec sample. Pcm sample is 16 bits.
|
||||
* Frame index's between 48 and 191 inclusively.
|
||||
* Channel count can be 1, 2, 4 or 8.
|
||||
* Frame count should be multipliable by 4, and Sample count by 8.
|
||||
*
|
||||
* C Prototype
|
||||
* void hdmi_dma_copy_16_neon_fast(unsigned short *src,
|
||||
* unsigned int *dst, int samples);
|
||||
* Return value
|
||||
* None
|
||||
* Parameters
|
||||
* src Source PCM16 samples
|
||||
* dst Dest buffer to store pcm with header
|
||||
* samples Contains sample count (=frame_count * channel_count)
|
||||
*/
|
||||
|
||||
hdmi_dma_copy_16_neon_fast:
|
||||
mov r12, #1 /* construct vector(1) */
|
||||
vdup.8 d6, r12
|
||||
|
||||
hdmi_dma_copy_16_neon_fast_start:
|
||||
/* get 8 samples to q0 */
|
||||
vld1.16 {d0, d1}, [r0]! /* TODO: aligned */
|
||||
|
||||
/* pld [r1, #(64*4)] */
|
||||
|
||||
/* xor every bit */
|
||||
vcnt.8 q1, q0 /* count of 1s */
|
||||
vpadd.i8 d2, d2, d3
|
||||
vand d2, d2, d6 /* clear other bits while keep the LST */
|
||||
/* finally we construct packet header */
|
||||
vshl.u8 d4, d2, #3 /* bit p: d2 = d2 << 3 */
|
||||
|
||||
/* get packet header: always 0 */
|
||||
|
||||
/* store: (d4 << 16 | q0) << 8 */
|
||||
vmovl.u8 q2, d4 /* expand from char to short */
|
||||
vzip.16 q0, q2
|
||||
vshl.u32 q0, q0, #8
|
||||
vshl.u32 q1, q2, #8
|
||||
vst1.32 {d0, d1, d2, d3}, [r1]!
|
||||
|
||||
/* decrease sample count */
|
||||
subs r2, r2, #8
|
||||
bne hdmi_dma_copy_16_neon_fast_start
|
||||
|
||||
mov pc, lr
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* hdmi_dma_copy_24_neon_lut
|
||||
* Convert pcm sample to iec sample. Pcm sample is 24 bits.
|
||||
* Frame index's between 0 and 47 inclusively. Channel count can be 1, 2, 4, 8.
|
||||
* Frame count should be multipliable by 4, and Sample count by 8.
|
||||
*
|
||||
* C Prototype
|
||||
* void hdmi_dma_copy_24_neon_lut(unsigned int *src, unsigned int *dst,
|
||||
* int samples, unsigned char *lookup_table);
|
||||
* Return value
|
||||
* None
|
||||
* Parameters
|
||||
* src Source PCM24 samples
|
||||
* dst Dest buffer to store pcm with header
|
||||
* samples Contains sample count (=frame_count * channel_count)
|
||||
* lookup_table Preconstructed header table. Channels interleaved.
|
||||
*/
|
||||
|
||||
hdmi_dma_copy_24_neon_lut:
|
||||
vpush {d8}
|
||||
|
||||
mov r12, #1 /* construct vector(1) */
|
||||
vdup.8 d8, r12
|
||||
|
||||
hdmi_dma_copy_24_neon_lut_start:
|
||||
|
||||
/* get 8 samples to q0 and q1 */
|
||||
vld1.32 {d0, d1, d2, d3}, [r0]! /* TODO: aligned */
|
||||
|
||||
/* pld [r1, #(64*4)] */
|
||||
|
||||
/* xor every bit */
|
||||
vcnt.8 q2, q0 /* count of 1s */
|
||||
vpadd.i8 d4, d4, d5 /* only care about the LSB in every element */
|
||||
vcnt.8 q3, q1
|
||||
vpadd.i8 d6, d6, d7
|
||||
vpadd.i8 d4, d4, d6 /* d4: contains xor result and other dirty bits */
|
||||
vand d4, d4, d8 /* clear other bits while keep the least bit */
|
||||
vshl.u8 d4, d4, #3 /* bit p: d4 = d4 << 3 */
|
||||
|
||||
/* get packet header */
|
||||
vld1.8 {d5}, [r3]!/* d5: original header */
|
||||
veor d5, d5, d4 /* fix bit p */
|
||||
|
||||
/* store: (d5 << 24 | q0) */
|
||||
vmovl.u8 q3, d5 /* expand from char to short */
|
||||
vmovl.u16 q2, d6 /* expand from short to int */
|
||||
vmovl.u16 q3, d7
|
||||
vshl.u32 q2, q2, #24
|
||||
vshl.u32 q3, q3, #24
|
||||
vorr q0, q0, q2
|
||||
vorr q1, q1, q3
|
||||
vst1.32 {d0, d1, d2, d3}, [r1]!
|
||||
|
||||
/* decrease sample count */
|
||||
subs r2, r2, #8
|
||||
bne hdmi_dma_copy_24_neon_lut_start
|
||||
|
||||
vpop {d8}
|
||||
mov pc, lr
|
||||
|
||||
/**
|
||||
* hdmi_dma_copy_24_neon_fast
|
||||
* Convert pcm sample to iec sample. Pcm sample is 24 bits.
|
||||
* Frame index's between 48 and 191 inclusively.
|
||||
* Channel count can be 1, 2, 4 or 8.
|
||||
* Frame count should be multipliable by 4, and Sample count by 8.
|
||||
*
|
||||
* C Prototype
|
||||
* void hdmi_dma_copy_24_neon_fast(unsigned int *src,
|
||||
* unsigned int *dst, int samples);
|
||||
* Return value
|
||||
* None
|
||||
* Parameters
|
||||
* src Source PCM24 samples
|
||||
* dst Dest buffer to store pcm with header
|
||||
* samples Contains sample count (=frame_count * channel_count)
|
||||
*/
|
||||
|
||||
hdmi_dma_copy_24_neon_fast:
|
||||
vpush {d8}
|
||||
|
||||
mov r12, #1 /* construct vector(1) */
|
||||
vdup.8 d8, r12
|
||||
|
||||
hdmi_dma_copy_24_neon_fast_start:
|
||||
/* get 8 samples to q0 and q1 */
|
||||
vld1.32 {d0, d1, d2, d3}, [r0]! /* TODO: aligned */
|
||||
|
||||
/* pld [r1, #(64*4)] */
|
||||
|
||||
/* xor every bit */
|
||||
vcnt.8 q2, q0 /* count of 1s */
|
||||
vpadd.i8 d4, d4, d5 /* only care about the LSB in every element */
|
||||
vcnt.8 q3, q1
|
||||
vpadd.i8 d6, d6, d7
|
||||
vpadd.i8 d4, d4, d6 /* d4: contains xor result and other dirty bits */
|
||||
vand d4, d4, d8 /* clear other bits while keep the least bit */
|
||||
vshl.u8 d4, d4, #3 /* bit p: d4 = d4 << 3 */
|
||||
|
||||
/* store: (d4 << 24 | q0) */
|
||||
vmovl.u8 q3, d4 /* expand from char to short */
|
||||
vmovl.u16 q2, d6 /* expand from short to int */
|
||||
vmovl.u16 q3, d7
|
||||
vshl.u32 q2, q2, #24
|
||||
vshl.u32 q3, q3, #24
|
||||
vorr q0, q0, q2
|
||||
vorr q1, q1, q3
|
||||
vst1.32 {d0, d1, d2, d3}, [r1]!
|
||||
|
||||
/* decrease sample count */
|
||||
subs r2, r2, #8
|
||||
bne hdmi_dma_copy_24_neon_fast_start
|
||||
|
||||
vpop {d8}
|
||||
mov pc, lr
|
417
sound/soc/fsl/imx-ak4458.c
Normal file
417
sound/soc/fsl/imx-ak4458.c
Normal file
|
@ -0,0 +1,417 @@
|
|||
/* i.MX AK4458 audio support
|
||||
*
|
||||
* Copyright 2017 NXP
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
|
||||
#include "fsl_sai.h"
|
||||
#include "fsl_dsd.h"
|
||||
|
||||
struct imx_ak4458_data {
|
||||
struct snd_soc_card card;
|
||||
int num_codec_conf;
|
||||
struct snd_soc_codec_conf *codec_conf;
|
||||
bool tdm_mode;
|
||||
int pdn_gpio;
|
||||
unsigned int slots;
|
||||
unsigned int slot_width;
|
||||
bool one2one_ratio;
|
||||
};
|
||||
|
||||
static struct snd_soc_dapm_widget imx_ak4458_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("Line Out", NULL),
|
||||
};
|
||||
|
||||
/**
|
||||
* Tables 3 & 4 - mapping LRCK fs and frame width
|
||||
*/
|
||||
static const struct imx_ak4458_fs_map {
|
||||
unsigned int rmin;
|
||||
unsigned int rmax;
|
||||
unsigned int wmin;
|
||||
unsigned int wmax;
|
||||
} fs_map[] = {
|
||||
/* Normal, < 32kHz */
|
||||
{ .rmin = 8000, .rmax = 24000, .wmin = 1024, .wmax = 1024, },
|
||||
/* Normal, 32kHz */
|
||||
{ .rmin = 32000, .rmax = 32000, .wmin = 256, .wmax = 1024, },
|
||||
/* Normal */
|
||||
{ .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 768, },
|
||||
/* Double */
|
||||
{ .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 512, },
|
||||
/* Quad */
|
||||
{ .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 256, },
|
||||
/* Oct */
|
||||
{ .rmin = 352800, .rmax = 384000, .wmin = 32, .wmax = 128, },
|
||||
/* Hex */
|
||||
{ .rmin = 705600, .rmax = 768000, .wmin = 16, .wmax = 64, },
|
||||
};
|
||||
|
||||
static const struct imx_ak4458_fs_mul {
|
||||
unsigned int min;
|
||||
unsigned int max;
|
||||
unsigned int mul;
|
||||
} fs_mul_tdm[] = {
|
||||
/*
|
||||
* Table 13 - Audio Interface Format
|
||||
* For TDM mode, MCLK should is set to
|
||||
* obtained from 2 * slots * slot_width
|
||||
*/
|
||||
{ .min = 128, .max = 128, .mul = 256 }, /* TDM128 */
|
||||
{ .min = 256, .max = 256, .mul = 512 }, /* TDM256 */
|
||||
{ .min = 512, .max = 512, .mul = 1024 }, /* TDM512 */
|
||||
};
|
||||
|
||||
static const u32 ak4458_rates[] = {
|
||||
8000, 11025, 16000, 22050,
|
||||
32000, 44100, 48000, 88200,
|
||||
96000, 176400, 192000, 352800,
|
||||
384000, 705600, 768000,
|
||||
};
|
||||
|
||||
static const u32 ak4458_rates_tdm[] = {
|
||||
8000, 16000, 32000,
|
||||
48000, 96000,
|
||||
};
|
||||
|
||||
static const u32 ak4458_channels[] = {
|
||||
1, 2, 4, 6, 8, 10, 12, 14, 16,
|
||||
};
|
||||
|
||||
static const u32 ak4458_channels_tdm[] = {
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
};
|
||||
|
||||
static unsigned long ak4458_get_mclk_rate(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct imx_ak4458_data *data = snd_soc_card_get_drvdata(rtd->card);
|
||||
unsigned int rate = params_rate(params);
|
||||
unsigned int width = data->slots * data->slot_width;
|
||||
int i, mode;
|
||||
|
||||
if (data->tdm_mode) {
|
||||
/* can be 128, 256 or 512 */
|
||||
mode = data->slots * data->slot_width;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fs_mul_tdm); i++) {
|
||||
/* min = max = slots * slots_width */
|
||||
if (mode != fs_mul_tdm[i].min)
|
||||
continue;
|
||||
return rate * fs_mul_tdm[i].mul;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < ARRAY_SIZE(fs_map); i++) {
|
||||
if (rate >= fs_map[i].rmin && rate <= fs_map[i].rmax) {
|
||||
width = max(width, fs_map[i].wmin);
|
||||
width = min(width, fs_map[i].wmax);
|
||||
|
||||
/* Adjust SAI bclk:mclk ratio */
|
||||
width *= data->one2one_ratio ? 1 : 2;
|
||||
|
||||
return rate * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Let DAI manage clk frequency by default */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_aif_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct device *dev = card->dev;
|
||||
struct imx_ak4458_data *data = snd_soc_card_get_drvdata(card);
|
||||
unsigned int channels = params_channels(params);
|
||||
unsigned int fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS;
|
||||
unsigned long mclk_freq;
|
||||
bool is_dsd = fsl_is_dsd(params);
|
||||
int ret, i;
|
||||
|
||||
if (is_dsd) {
|
||||
channels = 1;
|
||||
data->slots = 1;
|
||||
data->slot_width = params_width(params);
|
||||
fmt |= SND_SOC_DAIFMT_PDM;
|
||||
} else if (data->tdm_mode) {
|
||||
data->slots = 8;
|
||||
data->slot_width = 32;
|
||||
fmt |= SND_SOC_DAIFMT_DSP_B;
|
||||
} else {
|
||||
data->slots = 2;
|
||||
data->slot_width = params_physical_width(params);
|
||||
fmt |= SND_SOC_DAIFMT_I2S;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = snd_soc_dai_set_tdm_slot(cpu_dai,
|
||||
BIT(channels) - 1, BIT(channels) - 1,
|
||||
data->slots, data->slot_width);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < rtd->num_codecs; i++) {
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
|
||||
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, fmt);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set codec dai[%d] fmt: %d\n",
|
||||
i, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = snd_soc_dai_set_tdm_slot(codec_dai,
|
||||
BIT(channels) - 1, BIT(channels) - 1,
|
||||
data->slots, data->slot_width);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set codec dai[%d] tdm slot: %d\n",
|
||||
i, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* set MCLK freq */
|
||||
mclk_freq = ak4458_get_mclk_rate(substream, params);
|
||||
if (is_dsd)
|
||||
mclk_freq = 22579200;
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, FSL_SAI_CLK_MAST1, mclk_freq,
|
||||
SND_SOC_CLOCK_OUT);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to set cpui dai mclk1 rate (%lu): %d\n",
|
||||
mclk_freq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_aif_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct imx_ak4458_data *data = snd_soc_card_get_drvdata(card);
|
||||
static struct snd_pcm_hw_constraint_list constraint_rates;
|
||||
static struct snd_pcm_hw_constraint_list constraint_channels;
|
||||
int ret;
|
||||
|
||||
if (data->tdm_mode) {
|
||||
constraint_channels.list = ak4458_channels_tdm;
|
||||
constraint_channels.count = ARRAY_SIZE(ak4458_channels_tdm);
|
||||
constraint_rates.list = ak4458_rates_tdm;
|
||||
constraint_rates.count = ARRAY_SIZE(ak4458_rates_tdm);
|
||||
} else {
|
||||
constraint_channels.list = ak4458_channels;
|
||||
constraint_channels.count = ARRAY_SIZE(ak4458_channels);
|
||||
constraint_rates.list = ak4458_rates;
|
||||
constraint_rates.count = ARRAY_SIZE(ak4458_rates);
|
||||
}
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraint_channels);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraint_rates);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops imx_aif_ops = {
|
||||
.hw_params = imx_aif_hw_params,
|
||||
.startup = imx_aif_startup,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link_component ak4458_codecs[] = {
|
||||
{
|
||||
/* Playback */
|
||||
.dai_name = "ak4458-aif",
|
||||
},
|
||||
{
|
||||
/* Capture */
|
||||
.dai_name = "ak4458-aif",
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link imx_ak4458_dai = {
|
||||
.name = "ak4458",
|
||||
.stream_name = "Audio",
|
||||
.codecs = ak4458_codecs,
|
||||
.num_codecs = 2,
|
||||
.ignore_pmdown_time = 1,
|
||||
.ops = &imx_aif_ops,
|
||||
.playback_only = 1,
|
||||
};
|
||||
|
||||
static int imx_ak4458_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_ak4458_data *priv;
|
||||
struct device_node *cpu_np, *codec_np_0 = NULL, *codec_np_1 = NULL;
|
||||
struct platform_device *cpu_pdev;
|
||||
struct snd_soc_dai_link_component *dlc;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
dlc = devm_kzalloc(&pdev->dev, 2 * sizeof(*dlc), GFP_KERNEL);
|
||||
if (!dlc)
|
||||
return -ENOMEM;
|
||||
|
||||
cpu_np = of_parse_phandle(pdev->dev.of_node, "audio-cpu", 0);
|
||||
if (!cpu_np) {
|
||||
dev_err(&pdev->dev, "audio dai phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
codec_np_0 = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
|
||||
if (!codec_np_0) {
|
||||
dev_err(&pdev->dev, "audio codec phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
codec_np_1 = of_parse_phandle(pdev->dev.of_node, "audio-codec", 1);
|
||||
if (!codec_np_1) {
|
||||
dev_err(&pdev->dev, "audio codec phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cpu_pdev = of_find_device_by_node(cpu_np);
|
||||
if (!cpu_pdev) {
|
||||
dev_err(&pdev->dev, "failed to find SAI platform device\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (of_find_property(pdev->dev.of_node, "fsl,tdm", NULL))
|
||||
priv->tdm_mode = true;
|
||||
|
||||
priv->num_codec_conf = 2;
|
||||
priv->codec_conf = devm_kzalloc(&pdev->dev,
|
||||
priv->num_codec_conf * sizeof(struct snd_soc_codec_conf),
|
||||
GFP_KERNEL);
|
||||
if (!priv->codec_conf) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->codec_conf[0].name_prefix = "0";
|
||||
priv->codec_conf[0].of_node = codec_np_0;
|
||||
priv->codec_conf[1].name_prefix = "1";
|
||||
priv->codec_conf[1].of_node = codec_np_1;
|
||||
|
||||
ak4458_codecs[0].of_node = codec_np_0;
|
||||
ak4458_codecs[1].of_node = codec_np_1;
|
||||
|
||||
imx_ak4458_dai.cpus = &dlc[0];
|
||||
imx_ak4458_dai.num_cpus = 1;
|
||||
imx_ak4458_dai.platforms = &dlc[1];
|
||||
imx_ak4458_dai.num_platforms = 1;
|
||||
|
||||
imx_ak4458_dai.cpus->dai_name = dev_name(&cpu_pdev->dev);
|
||||
imx_ak4458_dai.platforms->of_node = cpu_np;
|
||||
|
||||
priv->card.num_links = 1;
|
||||
priv->card.dai_link = &imx_ak4458_dai;
|
||||
priv->card.dev = &pdev->dev;
|
||||
priv->card.owner = THIS_MODULE;
|
||||
priv->card.dapm_widgets = imx_ak4458_dapm_widgets;
|
||||
priv->card.num_dapm_widgets = ARRAY_SIZE(imx_ak4458_dapm_widgets);
|
||||
priv->card.codec_conf = priv->codec_conf;
|
||||
priv->card.num_configs = priv->num_codec_conf;
|
||||
priv->one2one_ratio = !of_device_is_compatible(pdev->dev.of_node,
|
||||
"fsl,imx-audio-ak4458-mq");
|
||||
|
||||
priv->pdn_gpio = of_get_named_gpio(pdev->dev.of_node, "ak4458,pdn-gpio", 0);
|
||||
if (gpio_is_valid(priv->pdn_gpio)) {
|
||||
ret = devm_gpio_request_one(&pdev->dev, priv->pdn_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "ak4458,pdn");
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to get pdn gpio\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
gpio_set_value_cansleep(priv->pdn_gpio, 0);
|
||||
usleep_range(1000, 2000);
|
||||
gpio_set_value_cansleep(priv->pdn_gpio, 1);
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
ret = snd_soc_of_parse_card_name(&priv->card, "model");
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
snd_soc_card_set_drvdata(&priv->card, priv);
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
if (cpu_np)
|
||||
of_node_put(cpu_np);
|
||||
if (codec_np_0)
|
||||
of_node_put(codec_np_0);
|
||||
if (codec_np_1)
|
||||
of_node_put(codec_np_1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_ak4458_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx-audio-ak4458", },
|
||||
{ .compatible = "fsl,imx-audio-ak4458-mq", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_ak4458_dt_ids);
|
||||
|
||||
static struct platform_driver imx_ak4458_driver = {
|
||||
.driver = {
|
||||
.name = "imx-ak4458",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = imx_ak4458_dt_ids,
|
||||
},
|
||||
.probe = imx_ak4458_probe,
|
||||
};
|
||||
module_platform_driver(imx_ak4458_driver);
|
||||
|
||||
MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
|
||||
MODULE_DESCRIPTION("Freescale i.MX AK4458 ASoC machine driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:imx-ak4458");
|
269
sound/soc/fsl/imx-ak4497.c
Normal file
269
sound/soc/fsl/imx-ak4497.c
Normal file
|
@ -0,0 +1,269 @@
|
|||
/* i.MX AK4458 audio support
|
||||
*
|
||||
* Copyright 2017 NXP
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
|
||||
#include "fsl_sai.h"
|
||||
|
||||
struct imx_ak4497_data {
|
||||
struct snd_soc_card card;
|
||||
bool one2one_ratio;
|
||||
};
|
||||
|
||||
static struct snd_soc_dapm_widget imx_ak4497_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("Line Out", NULL),
|
||||
};
|
||||
|
||||
static const struct imx_ak4497_fs_mul {
|
||||
unsigned int min;
|
||||
unsigned int max;
|
||||
unsigned int mul;
|
||||
} fs_mul[] = {
|
||||
/**
|
||||
* Table 7 - mapping multiplier and speed mode
|
||||
* Tables 8 & 9 - mapping speed mode and LRCK fs
|
||||
*/
|
||||
{ .min = 8000, .max = 32000, .mul = 1024 }, /* Normal, <= 32kHz */
|
||||
{ .min = 44100, .max = 48000, .mul = 512 }, /* Normal */
|
||||
{ .min = 88200, .max = 96000, .mul = 256 }, /* Double */
|
||||
{ .min = 176400, .max = 192000, .mul = 128 }, /* Quad */
|
||||
{ .min = 352800, .max = 384000, .mul = 2*64 }, /* Oct */
|
||||
{ .min = 705600, .max = 768000, .mul = 2*32 }, /* Hex */
|
||||
};
|
||||
|
||||
static bool imx_ak4497_is_dsd(struct snd_pcm_hw_params *params)
|
||||
{
|
||||
snd_pcm_format_t format = params_format(params);
|
||||
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_DSD_U8:
|
||||
case SNDRV_PCM_FORMAT_DSD_U16_LE:
|
||||
case SNDRV_PCM_FORMAT_DSD_U16_BE:
|
||||
case SNDRV_PCM_FORMAT_DSD_U32_LE:
|
||||
case SNDRV_PCM_FORMAT_DSD_U32_BE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long imx_ak4497_compute_freq(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
unsigned int rate = params_rate(params);
|
||||
int i;
|
||||
|
||||
/* Find the appropriate MCLK freq */
|
||||
for (i = 0; i < ARRAY_SIZE(fs_mul); i++) {
|
||||
if (rate >= fs_mul[i].min && rate <= fs_mul[i].max)
|
||||
return rate * fs_mul[i].mul;
|
||||
}
|
||||
|
||||
/* Let DAI manage MCLK frequency */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_aif_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct device *dev = card->dev;
|
||||
struct imx_ak4497_data *priv = snd_soc_card_get_drvdata(card);
|
||||
unsigned int channels = params_channels(params);
|
||||
unsigned int fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS;
|
||||
unsigned long freq = imx_ak4497_compute_freq(substream, params);
|
||||
bool is_dsd = imx_ak4497_is_dsd(params);
|
||||
int ret;
|
||||
|
||||
fmt |= (is_dsd ? SND_SOC_DAIFMT_PDM : SND_SOC_DAIFMT_I2S);
|
||||
|
||||
if (is_dsd && freq > 22579200 && priv->one2one_ratio)
|
||||
freq = 22579200;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, FSL_SAI_CLK_MAST1, freq,
|
||||
SND_SOC_CLOCK_OUT);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set cpu dai mclk1 rate(%lu): %d\n",
|
||||
freq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, fmt);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set codec dai fmt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (is_dsd)
|
||||
ret = snd_soc_dai_set_tdm_slot(cpu_dai,
|
||||
0x1, 0x1,
|
||||
1, params_width(params));
|
||||
else
|
||||
ret = snd_soc_dai_set_tdm_slot(cpu_dai,
|
||||
BIT(channels) - 1, BIT(channels) - 1,
|
||||
2, params_physical_width(params));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const u32 support_rates[] = {
|
||||
8000, 11025, 16000, 22050,
|
||||
32000, 44100, 48000, 88200,
|
||||
96000, 176400, 192000, 352800,
|
||||
384000, 705600, 768000,
|
||||
};
|
||||
|
||||
static int imx_aif_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int ret = 0;
|
||||
static struct snd_pcm_hw_constraint_list constraint_rates;
|
||||
|
||||
constraint_rates.list = support_rates;
|
||||
constraint_rates.count = ARRAY_SIZE(support_rates);
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraint_rates);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops imx_aif_ops = {
|
||||
.startup = imx_aif_startup,
|
||||
.hw_params = imx_aif_hw_params,
|
||||
};
|
||||
|
||||
SND_SOC_DAILINK_DEFS(hifi,
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "ak4497-aif")),
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()));
|
||||
|
||||
static struct snd_soc_dai_link imx_ak4497_dai = {
|
||||
.name = "ak4497",
|
||||
.stream_name = "Audio",
|
||||
.ops = &imx_aif_ops,
|
||||
.playback_only = 1,
|
||||
SND_SOC_DAILINK_REG(hifi),
|
||||
};
|
||||
|
||||
static int imx_ak4497_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_ak4497_data *priv;
|
||||
struct device_node *cpu_np, *codec_np = NULL;
|
||||
struct platform_device *cpu_pdev;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
cpu_np = of_parse_phandle(pdev->dev.of_node, "audio-cpu", 0);
|
||||
if (!cpu_np) {
|
||||
dev_err(&pdev->dev, "audio dai phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
|
||||
if (!codec_np) {
|
||||
dev_err(&pdev->dev, "audio codec phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cpu_pdev = of_find_device_by_node(cpu_np);
|
||||
if (!cpu_pdev) {
|
||||
dev_err(&pdev->dev, "failed to find SAI platform device\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
imx_ak4497_dai.codecs->of_node = codec_np;
|
||||
imx_ak4497_dai.cpus->dai_name = dev_name(&cpu_pdev->dev);
|
||||
imx_ak4497_dai.platforms->of_node = cpu_np;
|
||||
imx_ak4497_dai.playback_only = 1;
|
||||
|
||||
priv->card.dai_link = &imx_ak4497_dai;
|
||||
priv->card.num_links = 1;
|
||||
priv->card.dev = &pdev->dev;
|
||||
priv->card.owner = THIS_MODULE;
|
||||
priv->card.dapm_widgets = imx_ak4497_dapm_widgets;
|
||||
priv->card.num_dapm_widgets = ARRAY_SIZE(imx_ak4497_dapm_widgets);
|
||||
priv->one2one_ratio = !of_device_is_compatible(pdev->dev.of_node,
|
||||
"fsl,imx-audio-ak4497-mq");
|
||||
|
||||
ret = snd_soc_of_parse_card_name(&priv->card, "model");
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
snd_soc_card_set_drvdata(&priv->card, priv);
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
if (cpu_np)
|
||||
of_node_put(cpu_np);
|
||||
if (codec_np)
|
||||
of_node_put(codec_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_ak4497_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx-audio-ak4497", },
|
||||
{ .compatible = "fsl,imx-audio-ak4497-mq", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_ak4497_dt_ids);
|
||||
|
||||
static struct platform_driver imx_ak4497_driver = {
|
||||
.driver = {
|
||||
.name = "imx-ak4497",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = imx_ak4497_dt_ids,
|
||||
},
|
||||
.probe = imx_ak4497_probe,
|
||||
};
|
||||
module_platform_driver(imx_ak4497_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
|
||||
MODULE_DESCRIPTION("Freescale i.MX AK4497 ASoC machine driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:imx-ak4497");
|
485
sound/soc/fsl/imx-ak5558.c
Normal file
485
sound/soc/fsl/imx-ak5558.c
Normal file
|
@ -0,0 +1,485 @@
|
|||
/* i.MX AK5558 audio support
|
||||
*
|
||||
* Copyright 2017 NXP
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
|
||||
#include "fsl_sai.h"
|
||||
#include "../codecs/ak5558.h"
|
||||
|
||||
|
||||
struct imx_ak5558_data {
|
||||
struct snd_soc_card card;
|
||||
bool tdm_mode;
|
||||
unsigned long slots;
|
||||
unsigned long slot_width;
|
||||
bool one2one_ratio;
|
||||
struct platform_device *asrc_pdev;
|
||||
u32 asrc_rate;
|
||||
u32 asrc_format;
|
||||
};
|
||||
|
||||
/*
|
||||
* imx_ack5558_fs_mul - sampling frequency multiplier
|
||||
*
|
||||
* min <= fs <= max, MCLK = mul * LRCK
|
||||
*/
|
||||
struct imx_ak5558_fs_mul {
|
||||
unsigned int min;
|
||||
unsigned int max;
|
||||
unsigned int mul;
|
||||
};
|
||||
|
||||
/*
|
||||
* Auto MCLK selection based on LRCK for Normal Mode
|
||||
* (Table 4 from datasheet)
|
||||
*/
|
||||
static const struct imx_ak5558_fs_mul fs_mul[] = {
|
||||
{ .min = 8000, .max = 32000, .mul = 1024 },
|
||||
{ .min = 44100, .max = 48000, .mul = 512 },
|
||||
{ .min = 88200, .max = 96000, .mul = 256 },
|
||||
{ .min = 176400, .max = 192000, .mul = 128 },
|
||||
{ .min = 352800, .max = 384000, .mul = 64 },
|
||||
{ .min = 705600, .max = 768000, .mul = 32 },
|
||||
};
|
||||
|
||||
/*
|
||||
* MCLK and BCLK selection based on TDM mode
|
||||
* because of SAI we also add the restriction: MCLK >= 2 * BCLK
|
||||
* (Table 9 from datasheet)
|
||||
*/
|
||||
static const struct imx_ak5558_fs_mul fs_mul_tdm[] = {
|
||||
{ .min = 128, .max = 128, .mul = 256 },
|
||||
{ .min = 256, .max = 256, .mul = 512 },
|
||||
{ .min = 512, .max = 512, .mul = 1024 },
|
||||
};
|
||||
|
||||
static struct snd_soc_dapm_widget imx_ak5558_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("Line In", NULL),
|
||||
};
|
||||
|
||||
static const u32 ak5558_rates[] = {
|
||||
8000, 11025, 16000, 22050,
|
||||
32000, 44100, 48000, 88200,
|
||||
96000, 176400, 192000, 352800,
|
||||
384000, 705600, 768000,
|
||||
};
|
||||
|
||||
static const u32 ak5558_tdm_rates[] = {
|
||||
8000, 16000, 32000,
|
||||
48000, 96000
|
||||
};
|
||||
|
||||
static const u32 ak5558_channels[] = {
|
||||
1, 2, 4, 6, 8,
|
||||
};
|
||||
|
||||
static unsigned long ak5558_get_mclk_rate(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct imx_ak5558_data *data = snd_soc_card_get_drvdata(rtd->card);
|
||||
unsigned int rate = params_rate(params);
|
||||
unsigned int freq = 0; /* Let DAI manage clk frequency by default */
|
||||
int mode;
|
||||
int i;
|
||||
|
||||
if (data->tdm_mode) {
|
||||
mode = data->slots * data->slot_width;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fs_mul_tdm); i++) {
|
||||
/* min = max = slots * slots_width */
|
||||
if (mode != fs_mul_tdm[i].min)
|
||||
continue;
|
||||
freq = rate * fs_mul_tdm[i].mul;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < ARRAY_SIZE(fs_mul); i++) {
|
||||
if (rate < fs_mul[i].min || rate > fs_mul[i].max)
|
||||
continue;
|
||||
freq = rate * fs_mul[i].mul;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static int imx_aif_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct device *dev = card->dev;
|
||||
struct imx_ak5558_data *data = snd_soc_card_get_drvdata(card);
|
||||
unsigned int channels = params_channels(params);
|
||||
unsigned long mclk_freq;
|
||||
unsigned int fmt;
|
||||
int ret;
|
||||
|
||||
if (data->tdm_mode)
|
||||
fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS;
|
||||
else
|
||||
fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS;
|
||||
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, fmt);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set codec dai fmt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (data->tdm_mode) {
|
||||
/* support TDM256 (8 slots * 32 bits/per slot) */
|
||||
data->slots = 8;
|
||||
data->slot_width = 32;
|
||||
|
||||
ret = snd_soc_dai_set_tdm_slot(cpu_dai,
|
||||
BIT(channels) - 1, BIT(channels) - 1,
|
||||
data->slots, data->slot_width);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_tdm_slot(codec_dai,
|
||||
BIT(channels) - 1, BIT(channels) - 1,
|
||||
8, 32);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set codec dai fmt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/* normal mode (I2S) */
|
||||
data->slots = 2;
|
||||
data->slot_width = params_physical_width(params);
|
||||
|
||||
ret = snd_soc_dai_set_tdm_slot(cpu_dai,
|
||||
BIT(channels) - 1, BIT(channels) - 1,
|
||||
data->slots, data->slot_width);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
mclk_freq = ak5558_get_mclk_rate(substream, params);
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, FSL_SAI_CLK_MAST1, mclk_freq,
|
||||
SND_SOC_CLOCK_OUT);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to set cpu_dai mclk1 rate %lu\n",
|
||||
mclk_freq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_ak5558_hw_rule_rate(struct snd_pcm_hw_params *p,
|
||||
struct snd_pcm_hw_rule *r)
|
||||
{
|
||||
struct imx_ak5558_data *data = r->private;
|
||||
struct snd_interval t = { .min = 8000, .max = 8000, };
|
||||
unsigned int fs;
|
||||
unsigned long mclk_freq;
|
||||
int i;
|
||||
|
||||
fs = hw_param_interval(p, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
|
||||
fs *= data->tdm_mode ? 8 : 2;
|
||||
|
||||
/* Identify maximum supported rate */
|
||||
for (i = 0; i < ARRAY_SIZE(ak5558_rates); i++) {
|
||||
mclk_freq = fs * ak5558_rates[i];
|
||||
/* Adjust SAI bclk:mclk ratio */
|
||||
mclk_freq *= data->one2one_ratio ? 1 : 2;
|
||||
|
||||
/* Skip rates for which MCLK is beyond supported value */
|
||||
if (mclk_freq > 36864000)
|
||||
continue;
|
||||
|
||||
if (t.max < ak5558_rates[i])
|
||||
t.max = ak5558_rates[i];
|
||||
}
|
||||
|
||||
return snd_interval_refine(hw_param_interval(p, r->var), &t);
|
||||
}
|
||||
|
||||
static int imx_aif_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct imx_ak5558_data *data = snd_soc_card_get_drvdata(card);
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraint_rates;
|
||||
static struct snd_pcm_hw_constraint_list constraint_channels;
|
||||
int ret;
|
||||
|
||||
if (data->tdm_mode) {
|
||||
constraint_rates.list = ak5558_tdm_rates;
|
||||
constraint_rates.count = ARRAY_SIZE(ak5558_tdm_rates);
|
||||
} else {
|
||||
constraint_rates.list = ak5558_rates;
|
||||
constraint_rates.count = ARRAY_SIZE(ak5558_rates);
|
||||
}
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraint_rates);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
constraint_channels.list = ak5558_channels;
|
||||
constraint_channels.count = ARRAY_SIZE(ak5558_channels);
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraint_channels);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return snd_pcm_hw_rule_add(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, imx_ak5558_hw_rule_rate, data,
|
||||
SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops imx_aif_ops = {
|
||||
.hw_params = imx_aif_hw_params,
|
||||
.startup = imx_aif_startup,
|
||||
};
|
||||
|
||||
static struct snd_soc_ops imx_aif_ops_be = {
|
||||
.hw_params = imx_aif_hw_params,
|
||||
};
|
||||
|
||||
static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct imx_ak5558_data *priv = snd_soc_card_get_drvdata(card);
|
||||
struct snd_interval *rate;
|
||||
struct snd_mask *mask;
|
||||
|
||||
if (!priv->asrc_pdev)
|
||||
return -EINVAL;
|
||||
|
||||
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
rate->max = priv->asrc_rate;
|
||||
rate->min = priv->asrc_rate;
|
||||
|
||||
mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
snd_mask_none(mask);
|
||||
snd_mask_set(mask, priv->asrc_format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SND_SOC_DAILINK_DEFS(hifi,
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "ak5558-aif")),
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()));
|
||||
|
||||
SND_SOC_DAILINK_DEFS(hifi_fe,
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()),
|
||||
DAILINK_COMP_ARRAY(COMP_DUMMY()),
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()));
|
||||
|
||||
SND_SOC_DAILINK_DEFS(hifi_be,
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "ak5558-aif")),
|
||||
DAILINK_COMP_ARRAY(COMP_DUMMY()));
|
||||
|
||||
static struct snd_soc_dai_link imx_ak5558_dai[] = {
|
||||
{
|
||||
.name = "ak5558",
|
||||
.stream_name = "Audio",
|
||||
.ops = &imx_aif_ops,
|
||||
.capture_only = 1,
|
||||
SND_SOC_DAILINK_REG(hifi),
|
||||
},
|
||||
{
|
||||
.name = "HiFi-ASRC-FE",
|
||||
.stream_name = "HiFi-ASRC-FE",
|
||||
.dynamic = 1,
|
||||
.ignore_pmdown_time = 1,
|
||||
.dpcm_playback = 0,
|
||||
.dpcm_capture = 1,
|
||||
.dpcm_merged_chan = 1,
|
||||
SND_SOC_DAILINK_REG(hifi_fe),
|
||||
},
|
||||
{
|
||||
.name = "HiFi-ASRC-BE",
|
||||
.stream_name = "HiFi-ASRC-BE",
|
||||
.no_pcm = 1,
|
||||
.ignore_pmdown_time = 1,
|
||||
.dpcm_playback = 0,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &imx_aif_ops_be,
|
||||
.be_hw_params_fixup = be_hw_params_fixup,
|
||||
SND_SOC_DAILINK_REG(hifi_be),
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
{"CPU-Capture", NULL, "Capture"},
|
||||
{"ASRC-Capture", NULL, "CPU-Capture"},
|
||||
};
|
||||
|
||||
static int imx_ak5558_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_ak5558_data *priv;
|
||||
struct device_node *cpu_np, *codec_np = NULL;
|
||||
struct platform_device *cpu_pdev;
|
||||
struct device_node *asrc_np = NULL;
|
||||
struct platform_device *asrc_pdev = NULL;
|
||||
int ret;
|
||||
u32 width;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
cpu_np = of_parse_phandle(pdev->dev.of_node, "audio-cpu", 0);
|
||||
if (!cpu_np) {
|
||||
dev_err(&pdev->dev, "audio dai phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
|
||||
if (!codec_np) {
|
||||
dev_err(&pdev->dev, "audio codec phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cpu_pdev = of_find_device_by_node(cpu_np);
|
||||
if (!cpu_pdev) {
|
||||
dev_err(&pdev->dev, "failed to find SAI platform device\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
asrc_np = of_parse_phandle(pdev->dev.of_node, "asrc-controller", 0);
|
||||
if (asrc_np) {
|
||||
asrc_pdev = of_find_device_by_node(asrc_np);
|
||||
priv->asrc_pdev = asrc_pdev;
|
||||
}
|
||||
|
||||
if (of_find_property(pdev->dev.of_node, "fsl,tdm", NULL))
|
||||
priv->tdm_mode = true;
|
||||
|
||||
imx_ak5558_dai[0].codecs->of_node = codec_np;
|
||||
imx_ak5558_dai[0].cpus->dai_name = dev_name(&cpu_pdev->dev);
|
||||
imx_ak5558_dai[0].platforms->of_node = cpu_np;
|
||||
imx_ak5558_dai[0].capture_only = 1;
|
||||
|
||||
priv->card.dai_link = &imx_ak5558_dai[0];
|
||||
priv->card.num_links = 1;
|
||||
priv->card.dapm_routes = audio_map;
|
||||
priv->card.num_dapm_routes = 1;
|
||||
|
||||
/*if there is no asrc controller, we only enable one device*/
|
||||
if (asrc_pdev) {
|
||||
imx_ak5558_dai[1].cpus->of_node = asrc_np;
|
||||
imx_ak5558_dai[1].platforms->of_node = asrc_np;
|
||||
|
||||
imx_ak5558_dai[2].codecs->of_node = codec_np;
|
||||
imx_ak5558_dai[2].cpus->of_node = cpu_np;
|
||||
priv->card.num_links = 3;
|
||||
priv->card.num_dapm_routes += 1;
|
||||
|
||||
ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",
|
||||
&priv->asrc_rate);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get output rate\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get output rate\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (width == 24)
|
||||
priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
|
||||
else
|
||||
priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
|
||||
}
|
||||
|
||||
priv->card.dev = &pdev->dev;
|
||||
priv->card.owner = THIS_MODULE;
|
||||
priv->card.dapm_widgets = imx_ak5558_dapm_widgets;
|
||||
priv->card.num_dapm_widgets = ARRAY_SIZE(imx_ak5558_dapm_widgets);
|
||||
priv->one2one_ratio = !of_device_is_compatible(pdev->dev.of_node,
|
||||
"fsl,imx-audio-ak5558-mq");
|
||||
|
||||
ret = snd_soc_of_parse_card_name(&priv->card, "model");
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
snd_soc_card_set_drvdata(&priv->card, priv);
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
if (cpu_np)
|
||||
of_node_put(cpu_np);
|
||||
if (codec_np)
|
||||
of_node_put(codec_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_ak5558_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx-audio-ak5558", },
|
||||
{ .compatible = "fsl,imx-audio-ak5558-mq", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_ak5558_dt_ids);
|
||||
|
||||
static struct platform_driver imx_ak5558_driver = {
|
||||
.driver = {
|
||||
.name = "imx-ak5558",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = imx_ak5558_dt_ids,
|
||||
},
|
||||
.probe = imx_ak5558_probe,
|
||||
};
|
||||
module_platform_driver(imx_ak5558_driver);
|
||||
|
||||
MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
|
||||
MODULE_DESCRIPTION("Freescale i.MX AK5558 ASoC machine driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:imx-ak5558");
|
561
sound/soc/fsl/imx-cdnhdmi.c
Normal file
561
sound/soc/fsl/imx-cdnhdmi.c
Normal file
|
@ -0,0 +1,561 @@
|
|||
/*
|
||||
* Copyright 2017-2018 NXP
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/hdmi-codec.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include "fsl_sai.h"
|
||||
|
||||
#define SUPPORT_RATE_NUM 10
|
||||
#define SUPPORT_CHANNEL_NUM 10
|
||||
|
||||
struct imx_cdnhdmi_data {
|
||||
struct snd_soc_dai_link dai;
|
||||
struct snd_soc_card card;
|
||||
int protocol;
|
||||
u32 support_rates[SUPPORT_RATE_NUM];
|
||||
u32 support_rates_num;
|
||||
u32 support_channels[SUPPORT_CHANNEL_NUM];
|
||||
u32 support_channels_num;
|
||||
u32 edid_rates[SUPPORT_RATE_NUM];
|
||||
u32 edid_rates_count;
|
||||
u32 edid_channels[SUPPORT_CHANNEL_NUM];
|
||||
u32 edid_channels_count;
|
||||
uint8_t eld[MAX_ELD_BYTES];
|
||||
};
|
||||
|
||||
static int imx_cdnhdmi_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
|
||||
static struct snd_pcm_hw_constraint_list constraint_rates;
|
||||
static struct snd_pcm_hw_constraint_list constraint_channels;
|
||||
int ret;
|
||||
|
||||
constraint_rates.list = data->support_rates;
|
||||
constraint_rates.count = data->support_rates_num;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraint_rates);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
constraint_channels.list = data->support_channels;
|
||||
constraint_channels.count = data->support_channels_num;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraint_channels);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct device *dev = card->dev;
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
int ret;
|
||||
|
||||
/* set cpu DAI configuration */
|
||||
if (tx)
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai,
|
||||
SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS);
|
||||
else
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai,
|
||||
SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBM_CFM);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
if (of_device_is_compatible(dev->of_node,
|
||||
"fsl,imx8mq-evk-cdnhdmi"))
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, FSL_SAI_CLK_MAST1,
|
||||
256 * params_rate(params),
|
||||
SND_SOC_CLOCK_OUT);
|
||||
else
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, 0,
|
||||
0,
|
||||
tx ? SND_SOC_CLOCK_OUT : SND_SOC_CLOCK_IN);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0, 2, 32);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops imx_cdnhdmi_ops = {
|
||||
.startup = imx_cdnhdmi_startup,
|
||||
.hw_params = imx_cdnhdmi_hw_params,
|
||||
};
|
||||
|
||||
static const unsigned int eld_rates[] = {
|
||||
32000,
|
||||
44100,
|
||||
48000,
|
||||
88200,
|
||||
96000,
|
||||
176400,
|
||||
192000,
|
||||
};
|
||||
|
||||
static unsigned int sad_max_channels(const u8 *sad)
|
||||
{
|
||||
return 1 + (sad[0] & 7);
|
||||
}
|
||||
|
||||
static int get_edid_info(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = list_first_entry(
|
||||
&card->rtd_list, struct snd_soc_pcm_runtime, list);
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_component *component = codec_dai->component;
|
||||
struct hdmi_codec_pdata *hcd = component->dev->platform_data;
|
||||
struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
|
||||
int i, j, ret;
|
||||
const u8 *sad;
|
||||
unsigned int channel_max = 0;
|
||||
unsigned int rate_mask = 0;
|
||||
unsigned int rate_mask_eld = 0;
|
||||
|
||||
ret = hcd->ops->get_eld(component->dev->parent, hcd->data,
|
||||
data->eld, sizeof(data->eld));
|
||||
sad = drm_eld_sad(data->eld);
|
||||
if (sad) {
|
||||
for (j = 0; j < data->support_rates_num; j++) {
|
||||
for (i = 0; i < ARRAY_SIZE(eld_rates); i++)
|
||||
if (eld_rates[i] == data->support_rates[j])
|
||||
rate_mask |= BIT(i);
|
||||
}
|
||||
|
||||
for (i = drm_eld_sad_count(data->eld); i > 0; i--, sad += 3) {
|
||||
if (rate_mask & sad[1])
|
||||
channel_max = max(channel_max, sad_max_channels(sad));
|
||||
|
||||
if (sad_max_channels(sad) >= 2)
|
||||
rate_mask_eld |= sad[1];
|
||||
}
|
||||
}
|
||||
|
||||
rate_mask = rate_mask & rate_mask_eld;
|
||||
|
||||
data->edid_rates_count = 0;
|
||||
data->edid_channels_count = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eld_rates); i++) {
|
||||
if (rate_mask & BIT(i)) {
|
||||
data->edid_rates[data->edid_rates_count] = eld_rates[i];
|
||||
data->edid_rates_count++;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < data->support_channels_num; i++) {
|
||||
if (data->support_channels[i] <= channel_max) {
|
||||
data->edid_channels[data->edid_channels_count]
|
||||
= data->support_channels[i];
|
||||
data->edid_channels_count++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_channels_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
|
||||
|
||||
get_edid_info(card);
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = data->edid_channels_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_channels_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
|
||||
int i;
|
||||
|
||||
get_edid_info(card);
|
||||
|
||||
for (i = 0 ; i < data->edid_channels_count ; i++)
|
||||
uvalue->value.integer.value[i] = data->edid_channels[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_rates_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
|
||||
|
||||
get_edid_info(card);
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = data->edid_rates_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_rates_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
|
||||
int i;
|
||||
|
||||
get_edid_info(card);
|
||||
|
||||
for (i = 0 ; i < data->edid_rates_count; i++)
|
||||
uvalue->value.integer.value[i] = data->edid_rates[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_formats_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_formats_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
uvalue->value.integer.value[0] = 16;
|
||||
uvalue->value.integer.value[1] = 24;
|
||||
uvalue->value.integer.value[2] = 32;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_edid_rx_info(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = list_first_entry(
|
||||
&card->rtd_list, struct snd_soc_pcm_runtime, list);
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_component *component = codec_dai->component;
|
||||
struct hdmi_codec_pdata *hcd = component->dev->platform_data;
|
||||
struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
|
||||
int ret;
|
||||
|
||||
ret = hcd->ops->get_eld(component->dev->parent, hcd->data,
|
||||
data->eld, sizeof(data->eld));
|
||||
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
data->edid_rates[0] = data->eld[0] +
|
||||
(data->eld[1] << 8) +
|
||||
(data->eld[2] << 16) +
|
||||
(data->eld[3] << 24);
|
||||
|
||||
data->edid_channels[0] = data->eld[4] +
|
||||
(data->eld[5] << 8) +
|
||||
(data->eld[6] << 16) +
|
||||
(data->eld[7] << 24);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_rx_channels_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 2;
|
||||
uinfo->value.integer.max = 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_rx_channels_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
|
||||
int ret;
|
||||
|
||||
ret = get_edid_rx_info(card);
|
||||
if (ret)
|
||||
return ret;
|
||||
uvalue->value.integer.value[0] = data->edid_channels[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_rx_rates_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 16000;
|
||||
uinfo->value.integer.max = 192000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cdnhdmi_rx_rates_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
|
||||
int ret;
|
||||
|
||||
ret = get_edid_rx_info(card);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uvalue->value.integer.value[0] = data->edid_rates[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new imx_cdnhdmi_ctrls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HDMI Support Channels",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = imx_cdnhdmi_channels_info,
|
||||
.get = imx_cdnhdmi_channels_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HDMI Support Rates",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = imx_cdnhdmi_rates_info,
|
||||
.get = imx_cdnhdmi_rates_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HDMI Support Formats",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = imx_cdnhdmi_formats_info,
|
||||
.get = imx_cdnhdmi_formats_get,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new imx_cdnhdmi_rx_ctrls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HDMI Rx Channels",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = imx_cdnhdmi_rx_channels_info,
|
||||
.get = imx_cdnhdmi_rx_channels_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HDMI Rx Rates",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = imx_cdnhdmi_rx_rates_info,
|
||||
.get = imx_cdnhdmi_rx_rates_get,
|
||||
},
|
||||
};
|
||||
|
||||
static int imx_cdnhdmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *cpu_np, *cdnhdmi_np = NULL;
|
||||
struct platform_device *cpu_pdev;
|
||||
struct imx_cdnhdmi_data *data;
|
||||
struct snd_soc_dai_link_component *dlc;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
dlc = devm_kzalloc(&pdev->dev, 3 * sizeof(*dlc), GFP_KERNEL);
|
||||
if (!dlc)
|
||||
return -ENOMEM;
|
||||
|
||||
cpu_np = of_parse_phandle(pdev->dev.of_node, "audio-cpu", 0);
|
||||
if (!cpu_np) {
|
||||
dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cpu_pdev = of_find_device_by_node(cpu_np);
|
||||
if (!cpu_pdev) {
|
||||
dev_err(&pdev->dev, "failed to find SAI platform device\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < SUPPORT_RATE_NUM; i++) {
|
||||
ret = of_property_read_u32_index(pdev->dev.of_node,
|
||||
"constraint-rate",
|
||||
i, &data->support_rates[i]);
|
||||
if (!ret)
|
||||
data->support_rates_num = i + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (data->support_rates_num == 0) {
|
||||
data->support_rates[0] = 48000;
|
||||
data->support_rates[1] = 96000;
|
||||
data->support_rates[2] = 32000;
|
||||
data->support_rates[3] = 192000;
|
||||
data->support_rates_num = 4;
|
||||
}
|
||||
|
||||
data->support_channels[0] = 2;
|
||||
data->support_channels[1] = 4;
|
||||
data->support_channels[2] = 8;
|
||||
data->support_channels_num = 3;
|
||||
|
||||
of_property_read_u32(pdev->dev.of_node, "protocol",
|
||||
&data->protocol);
|
||||
|
||||
data->dai.cpus = &dlc[0];
|
||||
data->dai.num_cpus = 1;
|
||||
data->dai.platforms = &dlc[1];
|
||||
data->dai.num_platforms = 1;
|
||||
data->dai.codecs = &dlc[2];
|
||||
data->dai.num_codecs = 1;
|
||||
|
||||
data->dai.name = "imx8 hdmi";
|
||||
data->dai.stream_name = "imx8 hdmi";
|
||||
data->dai.cpus->dai_name = dev_name(&cpu_pdev->dev);
|
||||
data->dai.platforms->of_node = cpu_np;
|
||||
data->dai.ops = &imx_cdnhdmi_ops;
|
||||
data->dai.playback_only = true;
|
||||
data->dai.capture_only = false;
|
||||
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS;
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "hdmi-out")) {
|
||||
data->dai.playback_only = true;
|
||||
data->dai.capture_only = false;
|
||||
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS;
|
||||
data->dai.codecs->dai_name = "i2s-hifi";
|
||||
data->dai.codecs->name = "hdmi-audio-codec.1";
|
||||
data->card.controls = imx_cdnhdmi_ctrls;
|
||||
data->card.num_controls = ARRAY_SIZE(imx_cdnhdmi_ctrls);
|
||||
}
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "hdmi-in")) {
|
||||
data->dai.playback_only = false;
|
||||
data->dai.capture_only = true;
|
||||
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBM_CFM;
|
||||
data->dai.codecs->dai_name = "i2s-hifi";
|
||||
data->dai.codecs->name = "hdmi-audio-codec.2";
|
||||
data->card.controls = imx_cdnhdmi_rx_ctrls;
|
||||
data->card.num_controls = ARRAY_SIZE(imx_cdnhdmi_rx_ctrls);
|
||||
}
|
||||
|
||||
if ((data->dai.playback_only && data->dai.capture_only)
|
||||
|| (!data->dai.playback_only && !data->dai.capture_only)) {
|
||||
dev_err(&pdev->dev, "Wrongly enable HDMI DAI link\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->card.dev = &pdev->dev;
|
||||
data->card.owner = THIS_MODULE;
|
||||
ret = snd_soc_of_parse_card_name(&data->card, "model");
|
||||
if (ret)
|
||||
goto fail;
|
||||
data->card.num_links = 1;
|
||||
data->card.dai_link = &data->dai;
|
||||
|
||||
|
||||
platform_set_drvdata(pdev, &data->card);
|
||||
snd_soc_card_set_drvdata(&data->card, data);
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (cpu_np)
|
||||
of_node_put(cpu_np);
|
||||
if (cdnhdmi_np)
|
||||
of_node_put(cdnhdmi_np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_cdnhdmi_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx8mq-evk-cdnhdmi", },
|
||||
{ .compatible = "fsl,imx-audio-cdnhdmi", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_cdnhdmi_dt_ids);
|
||||
|
||||
static struct platform_driver imx_cdnhdmi_driver = {
|
||||
.driver = {
|
||||
.name = "imx-cdnhdmi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = imx_cdnhdmi_dt_ids,
|
||||
},
|
||||
.probe = imx_cdnhdmi_probe,
|
||||
};
|
||||
module_platform_driver(imx_cdnhdmi_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("Freescale i.MX hdmi audio ASoC machine driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:imx-cdnhdmi");
|
502
sound/soc/fsl/imx-cs42888.c
Normal file
502
sound/soc/fsl/imx-cs42888.c
Normal file
|
@ -0,0 +1,502 @@
|
|||
/*
|
||||
* Copyright (C) 2010-2016 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "fsl_esai.h"
|
||||
|
||||
#define CODEC_CLK_EXTER_OSC 1
|
||||
#define CODEC_CLK_ESAI_HCKT 2
|
||||
#define SUPPORT_RATE_NUM 10
|
||||
|
||||
struct imx_priv {
|
||||
struct clk *codec_clk;
|
||||
struct clk *esai_clk;
|
||||
unsigned int mclk_freq;
|
||||
unsigned int esai_freq;
|
||||
struct platform_device *pdev;
|
||||
struct platform_device *asrc_pdev;
|
||||
u32 asrc_rate;
|
||||
u32 asrc_format;
|
||||
bool is_codec_master;
|
||||
bool is_codec_rpmsg;
|
||||
bool is_stream_in_use[2];
|
||||
bool is_stream_tdm[2];
|
||||
};
|
||||
|
||||
static struct imx_priv card_priv;
|
||||
|
||||
static int imx_cs42888_surround_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct imx_priv *priv = &card_priv;
|
||||
struct device *dev = &priv->pdev->dev;
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
u32 channels = params_channels(params);
|
||||
u32 max_tdm_rate;
|
||||
u32 dai_format;
|
||||
int ret = 0;
|
||||
|
||||
priv->is_stream_tdm[tx] = channels > 1 && channels % 2;
|
||||
dai_format = SND_SOC_DAIFMT_NB_NF |
|
||||
(priv->is_stream_tdm[tx] ? SND_SOC_DAIFMT_DSP_A :
|
||||
SND_SOC_DAIFMT_LEFT_J);
|
||||
|
||||
priv->is_stream_in_use[tx] = true;
|
||||
|
||||
if (priv->is_stream_in_use[!tx] &&
|
||||
(priv->is_stream_tdm[tx] != priv->is_stream_tdm[!tx])) {
|
||||
|
||||
dev_err(dev, "Don't support different fmt for tx & rx\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->mclk_freq = clk_get_rate(priv->codec_clk);
|
||||
priv->esai_freq = clk_get_rate(priv->esai_clk);
|
||||
|
||||
if (priv->is_codec_master) {
|
||||
/* TDM is not supported by codec in master mode */
|
||||
if (priv->is_stream_tdm[tx]) {
|
||||
dev_err(dev, "%d channels are not supported in codec master mode\n",
|
||||
channels);
|
||||
return -EINVAL;
|
||||
}
|
||||
dai_format |= SND_SOC_DAIFMT_CBM_CFM;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, ESAI_HCKT_EXTAL,
|
||||
priv->mclk_freq, SND_SOC_CLOCK_IN);
|
||||
else
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, ESAI_HCKR_EXTAL,
|
||||
priv->mclk_freq, SND_SOC_CLOCK_IN);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
|
||||
priv->mclk_freq, SND_SOC_CLOCK_OUT);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set codec sysclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} else {
|
||||
dai_format |= SND_SOC_DAIFMT_CBS_CFS;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, ESAI_HCKT_EXTAL,
|
||||
priv->mclk_freq, SND_SOC_CLOCK_OUT);
|
||||
else
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, ESAI_HCKR_EXTAL,
|
||||
priv->mclk_freq, SND_SOC_CLOCK_OUT);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
|
||||
priv->mclk_freq, SND_SOC_CLOCK_IN);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set codec sysclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* set cpu DAI configuration */
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
/* set i.MX active slot mask */
|
||||
if (priv->is_stream_tdm[tx]) {
|
||||
/* 2 required by ESAI BCLK divisors, 8 slots, 32 width */
|
||||
if (priv->is_codec_master)
|
||||
max_tdm_rate = priv->mclk_freq / (8*32);
|
||||
else
|
||||
max_tdm_rate = priv->esai_freq / (2*8*32);
|
||||
if (params_rate(params) > max_tdm_rate) {
|
||||
dev_err(dev,
|
||||
"maximum supported sampling rate for %d channels is %dKHz\n",
|
||||
channels, max_tdm_rate / 1000);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Per datasheet, the codec expects 8 slots and 32 bits
|
||||
* for every slot in TDM mode.
|
||||
*/
|
||||
snd_soc_dai_set_tdm_slot(cpu_dai,
|
||||
BIT(channels) - 1, BIT(channels) - 1,
|
||||
8, 32);
|
||||
} else
|
||||
snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32);
|
||||
|
||||
/* set codec DAI configuration */
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set codec dai fmt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cs42888_surround_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct imx_priv *priv = &card_priv;
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
|
||||
priv->is_stream_in_use[tx] = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_cs42888_surround_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
static struct snd_pcm_hw_constraint_list constraint_rates;
|
||||
struct imx_priv *priv = &card_priv;
|
||||
struct device *dev = &priv->pdev->dev;
|
||||
static u32 support_rates[SUPPORT_RATE_NUM];
|
||||
int ret;
|
||||
|
||||
priv->mclk_freq = clk_get_rate(priv->codec_clk);
|
||||
|
||||
if (priv->mclk_freq % 12288000 == 0) {
|
||||
support_rates[0] = 48000;
|
||||
support_rates[1] = 96000;
|
||||
support_rates[2] = 192000;
|
||||
constraint_rates.list = support_rates;
|
||||
constraint_rates.count = 3;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraint_rates);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else
|
||||
dev_warn(dev, "mclk may be not supported %d\n", priv->mclk_freq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops imx_cs42888_surround_ops = {
|
||||
.startup = imx_cs42888_surround_startup,
|
||||
.hw_params = imx_cs42888_surround_hw_params,
|
||||
.hw_free = imx_cs42888_surround_hw_free,
|
||||
};
|
||||
|
||||
/**
|
||||
* imx_cs42888_surround_startup() is to set constrain for hw parameter, but
|
||||
* backend use same runtime as frontend, for p2p backend need to use different
|
||||
* parameter, so backend can't use the startup.
|
||||
*/
|
||||
static struct snd_soc_ops imx_cs42888_surround_ops_be = {
|
||||
.hw_params = imx_cs42888_surround_hw_params,
|
||||
.hw_free = imx_cs42888_surround_hw_free,
|
||||
};
|
||||
|
||||
|
||||
static const struct snd_soc_dapm_widget imx_cs42888_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("Line Out Jack", NULL),
|
||||
SND_SOC_DAPM_LINE("Line In Jack", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
/* Line out jack */
|
||||
{"Line Out Jack", NULL, "AOUT1L"},
|
||||
{"Line Out Jack", NULL, "AOUT1R"},
|
||||
{"Line Out Jack", NULL, "AOUT2L"},
|
||||
{"Line Out Jack", NULL, "AOUT2R"},
|
||||
{"Line Out Jack", NULL, "AOUT3L"},
|
||||
{"Line Out Jack", NULL, "AOUT3R"},
|
||||
{"Line Out Jack", NULL, "AOUT4L"},
|
||||
{"Line Out Jack", NULL, "AOUT4R"},
|
||||
{"AIN1L", NULL, "Line In Jack"},
|
||||
{"AIN1R", NULL, "Line In Jack"},
|
||||
{"AIN2L", NULL, "Line In Jack"},
|
||||
{"AIN2R", NULL, "Line In Jack"},
|
||||
{"Playback", NULL, "CPU-Playback"},/* dai route for be and fe */
|
||||
{"CPU-Capture", NULL, "Capture"},
|
||||
{"CPU-Playback", NULL, "ASRC-Playback"},
|
||||
{"ASRC-Capture", NULL, "CPU-Capture"},
|
||||
};
|
||||
|
||||
static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params) {
|
||||
|
||||
struct imx_priv *priv = &card_priv;
|
||||
struct snd_interval *rate;
|
||||
struct snd_mask *mask;
|
||||
|
||||
if (!priv->asrc_pdev)
|
||||
return -EINVAL;
|
||||
|
||||
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
rate->max = rate->min = priv->asrc_rate;
|
||||
|
||||
mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
snd_mask_none(mask);
|
||||
snd_mask_set(mask, priv->asrc_format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SND_SOC_DAILINK_DEFS(hifi,
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42888")),
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()));
|
||||
|
||||
SND_SOC_DAILINK_DEFS(hifi_fe,
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()),
|
||||
DAILINK_COMP_ARRAY(COMP_DUMMY()),
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()));
|
||||
|
||||
SND_SOC_DAILINK_DEFS(hifi_be,
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42888")),
|
||||
DAILINK_COMP_ARRAY(COMP_DUMMY()));
|
||||
|
||||
static struct snd_soc_dai_link imx_cs42888_dai[] = {
|
||||
{
|
||||
.name = "HiFi",
|
||||
.stream_name = "HiFi",
|
||||
.ops = &imx_cs42888_surround_ops,
|
||||
.ignore_pmdown_time = 1,
|
||||
SND_SOC_DAILINK_REG(hifi),
|
||||
},
|
||||
{
|
||||
.name = "HiFi-ASRC-FE",
|
||||
.stream_name = "HiFi-ASRC-FE",
|
||||
.dynamic = 1,
|
||||
.ignore_pmdown_time = 1,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.dpcm_merged_chan = 1,
|
||||
SND_SOC_DAILINK_REG(hifi_fe),
|
||||
},
|
||||
{
|
||||
.name = "HiFi-ASRC-BE",
|
||||
.stream_name = "HiFi-ASRC-BE",
|
||||
.no_pcm = 1,
|
||||
.ignore_pmdown_time = 1,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &imx_cs42888_surround_ops_be,
|
||||
.be_hw_params_fixup = be_hw_params_fixup,
|
||||
SND_SOC_DAILINK_REG(hifi_be),
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_card_imx_cs42888 = {
|
||||
.name = "cs42888-audio",
|
||||
.dai_link = imx_cs42888_dai,
|
||||
.dapm_widgets = imx_cs42888_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(imx_cs42888_dapm_widgets),
|
||||
.dapm_routes = audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function will register the snd_soc_pcm_link drivers.
|
||||
*/
|
||||
static int imx_cs42888_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *esai_np, *codec_np;
|
||||
struct device_node *asrc_np = NULL;
|
||||
struct platform_device *esai_pdev;
|
||||
struct platform_device *asrc_pdev = NULL;
|
||||
struct imx_priv *priv = &card_priv;
|
||||
int ret;
|
||||
u32 width;
|
||||
|
||||
priv->pdev = pdev;
|
||||
priv->asrc_pdev = NULL;
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "codec-rpmsg"))
|
||||
priv->is_codec_rpmsg = true;
|
||||
|
||||
esai_np = of_parse_phandle(pdev->dev.of_node, "esai-controller", 0);
|
||||
codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
|
||||
if (!esai_np || !codec_np) {
|
||||
dev_err(&pdev->dev, "phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
asrc_np = of_parse_phandle(pdev->dev.of_node, "asrc-controller", 0);
|
||||
if (asrc_np) {
|
||||
asrc_pdev = of_find_device_by_node(asrc_np);
|
||||
priv->asrc_pdev = asrc_pdev;
|
||||
}
|
||||
|
||||
esai_pdev = of_find_device_by_node(esai_np);
|
||||
if (!esai_pdev) {
|
||||
dev_err(&pdev->dev, "failed to find ESAI platform device\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (priv->is_codec_rpmsg) {
|
||||
struct platform_device *codec_dev;
|
||||
|
||||
codec_dev = of_find_device_by_node(codec_np);
|
||||
if (!codec_dev || !codec_dev->dev.driver) {
|
||||
dev_err(&pdev->dev, "failed to find codec platform device\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
|
||||
if (IS_ERR(priv->codec_clk)) {
|
||||
ret = PTR_ERR(priv->codec_clk);
|
||||
dev_err(&codec_dev->dev, "failed to get codec clk: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
} else {
|
||||
struct i2c_client *codec_dev;
|
||||
|
||||
codec_dev = of_find_i2c_device_by_node(codec_np);
|
||||
if (!codec_dev || !codec_dev->dev.driver) {
|
||||
dev_err(&pdev->dev, "failed to find codec platform device\n");
|
||||
ret = -EPROBE_DEFER;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
|
||||
if (IS_ERR(priv->codec_clk)) {
|
||||
ret = PTR_ERR(priv->codec_clk);
|
||||
dev_err(&codec_dev->dev, "failed to get codec clk: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->is_codec_rpmsg) {
|
||||
imx_cs42888_dai[0].codecs->name = "rpmsg-audio-codec-cs42888";
|
||||
imx_cs42888_dai[0].codecs->dai_name = "cs42888";
|
||||
} else {
|
||||
imx_cs42888_dai[0].codecs->of_node = codec_np;
|
||||
}
|
||||
|
||||
/*if there is no asrc controller, we only enable one device*/
|
||||
if (!asrc_pdev) {
|
||||
imx_cs42888_dai[0].cpus->dai_name = dev_name(&esai_pdev->dev);
|
||||
imx_cs42888_dai[0].platforms->of_node = esai_np;
|
||||
snd_soc_card_imx_cs42888.num_links = 1;
|
||||
snd_soc_card_imx_cs42888.num_dapm_routes =
|
||||
ARRAY_SIZE(audio_map) - 2;
|
||||
} else {
|
||||
imx_cs42888_dai[0].cpus->dai_name = dev_name(&esai_pdev->dev);
|
||||
imx_cs42888_dai[0].platforms->of_node = esai_np;
|
||||
imx_cs42888_dai[1].cpus->of_node = asrc_np;
|
||||
imx_cs42888_dai[1].platforms->of_node = asrc_np;
|
||||
imx_cs42888_dai[2].cpus->dai_name = dev_name(&esai_pdev->dev);
|
||||
snd_soc_card_imx_cs42888.num_links = 3;
|
||||
|
||||
if (priv->is_codec_rpmsg) {
|
||||
imx_cs42888_dai[2].codecs->name = "rpmsg-audio-codec-cs42888";
|
||||
imx_cs42888_dai[2].codecs->dai_name = "cs42888";
|
||||
} else {
|
||||
imx_cs42888_dai[2].codecs->of_node = codec_np;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",
|
||||
&priv->asrc_rate);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get output rate\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get output rate\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (width == 24)
|
||||
priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
|
||||
else
|
||||
priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
|
||||
}
|
||||
|
||||
priv->esai_clk = devm_clk_get(&esai_pdev->dev, "extal");
|
||||
if (IS_ERR(priv->esai_clk)) {
|
||||
ret = PTR_ERR(priv->esai_clk);
|
||||
dev_err(&esai_pdev->dev, "failed to get cpu clk: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->is_codec_master = false;
|
||||
if (of_property_read_bool(pdev->dev.of_node, "codec-master"))
|
||||
priv->is_codec_master = true;
|
||||
|
||||
snd_soc_card_imx_cs42888.dev = &pdev->dev;
|
||||
|
||||
platform_set_drvdata(pdev, &snd_soc_card_imx_cs42888);
|
||||
|
||||
ret = snd_soc_register_card(&snd_soc_card_imx_cs42888);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
|
||||
fail:
|
||||
if (asrc_np)
|
||||
of_node_put(asrc_np);
|
||||
if (esai_np)
|
||||
of_node_put(esai_np);
|
||||
if (codec_np)
|
||||
of_node_put(codec_np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_cs42888_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_card(&snd_soc_card_imx_cs42888);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_cs42888_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx-audio-cs42888", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver imx_cs42888_driver = {
|
||||
.probe = imx_cs42888_probe,
|
||||
.remove = imx_cs42888_remove,
|
||||
.driver = {
|
||||
.name = "imx-cs42888",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = imx_cs42888_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(imx_cs42888_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("ALSA SoC cs42888 Machine Layer Driver");
|
||||
MODULE_ALIAS("platform:imx-cs42888");
|
||||
MODULE_LICENSE("GPL");
|
227
sound/soc/fsl/imx-dsp.c
Normal file
227
sound/soc/fsl/imx-dsp.c
Normal file
|
@ -0,0 +1,227 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0+
|
||||
//
|
||||
// DSP machine driver
|
||||
//
|
||||
// Copyright (c) 2012-2013 by Tensilica Inc. ALL RIGHTS RESERVED.
|
||||
// Copyright 2018 NXP
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
|
||||
struct imx_dsp_audio_data {
|
||||
struct snd_soc_dai_link dai[2];
|
||||
struct snd_soc_card card;
|
||||
};
|
||||
|
||||
static int imx_dsp_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
int ret;
|
||||
u32 dai_format = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_LEFT_J |
|
||||
SND_SOC_DAIFMT_CBS_CFS;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
|
||||
24576000, SND_SOC_CLOCK_IN);
|
||||
if (ret) {
|
||||
dev_err(rtd->dev, "failed to set codec sysclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
|
||||
if (ret) {
|
||||
dev_err(rtd->dev, "failed to set codec dai fmt: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops imx_dsp_ops_be = {
|
||||
.hw_params = imx_dsp_hw_params,
|
||||
};
|
||||
|
||||
static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params) {
|
||||
|
||||
struct snd_interval *rate;
|
||||
struct snd_interval *channels;
|
||||
struct snd_mask *mask;
|
||||
|
||||
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
rate->max = rate->min = 48000;
|
||||
|
||||
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
channels->max = channels->min = 2;
|
||||
|
||||
mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
snd_mask_none(mask);
|
||||
snd_mask_set(mask, SNDRV_PCM_FORMAT_S16_LE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_route imx_dsp_audio_map[] = {
|
||||
{"Playback", NULL, "Compress Playback"},/* dai route for be and fe */
|
||||
{"Playback", NULL, "Playback"},
|
||||
};
|
||||
|
||||
static int imx_dsp_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *cpu_np=NULL, *codec_np=NULL, *platform_np=NULL;
|
||||
struct snd_soc_dai_link_component *comp;
|
||||
struct platform_device *cpu_pdev;
|
||||
struct imx_dsp_audio_data *data;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
comp = devm_kzalloc(&pdev->dev, 6 * sizeof(*comp), GFP_KERNEL);
|
||||
if (!comp) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0);
|
||||
if (!cpu_np) {
|
||||
dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cpu_pdev = of_find_device_by_node(cpu_np);
|
||||
if (!cpu_pdev) {
|
||||
dev_err(&pdev->dev, "failed to find rpmsg platform device\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
|
||||
if (!codec_np) {
|
||||
dev_err(&pdev->dev, "phandle missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
platform_np = of_parse_phandle(pdev->dev.of_node, "audio-platform", 0);
|
||||
if (!platform_np) {
|
||||
dev_err(&pdev->dev, "platform missing or invalid\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
data->dai[0].cpus = &comp[0];
|
||||
data->dai[0].codecs = &comp[1];
|
||||
data->dai[0].platforms = &comp[2];
|
||||
|
||||
data->dai[0].num_cpus = 1;
|
||||
data->dai[0].num_codecs = 1;
|
||||
data->dai[0].num_platforms = 1;
|
||||
|
||||
data->dai[0].name = "dsp hifi fe";
|
||||
data->dai[0].stream_name = "dsp hifi fe";
|
||||
data->dai[0].codecs->dai_name = "snd-soc-dummy-dai";
|
||||
data->dai[0].codecs->name = "snd-soc-dummy";
|
||||
data->dai[0].cpus->dai_name = dev_name(&cpu_pdev->dev);
|
||||
data->dai[0].cpus->of_node = cpu_np;
|
||||
data->dai[0].platforms->of_node = platform_np;
|
||||
data->dai[0].playback_only = true;
|
||||
data->dai[0].capture_only = false;
|
||||
data->dai[0].dpcm_playback = 1;
|
||||
data->dai[0].dpcm_capture = 0;
|
||||
data->dai[0].dynamic = 1,
|
||||
data->dai[0].ignore_pmdown_time = 1,
|
||||
data->dai[0].dai_fmt = SND_SOC_DAIFMT_LEFT_J |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS;
|
||||
|
||||
data->dai[1].cpus = &comp[3];
|
||||
data->dai[1].codecs = &comp[4];
|
||||
data->dai[1].platforms = &comp[5];
|
||||
|
||||
data->dai[1].num_cpus = 1;
|
||||
data->dai[1].num_codecs = 1;
|
||||
data->dai[1].num_platforms = 1;
|
||||
|
||||
data->dai[1].name = "dsp hifi be";
|
||||
data->dai[1].stream_name = "dsp hifi be";
|
||||
data->dai[1].codecs->dai_name = "cs42888";
|
||||
data->dai[1].codecs->of_node = codec_np;
|
||||
data->dai[1].cpus->dai_name = "snd-soc-dummy-dai";
|
||||
data->dai[1].cpus->name = "snd-soc-dummy";
|
||||
data->dai[1].platforms->name = "snd-soc-dummy";
|
||||
data->dai[1].playback_only = true;
|
||||
data->dai[1].capture_only = false;
|
||||
data->dai[1].dpcm_playback = 1;
|
||||
data->dai[1].dpcm_capture = 0;
|
||||
data->dai[1].no_pcm = 1,
|
||||
data->dai[1].ignore_pmdown_time = 1,
|
||||
data->dai[1].dai_fmt = SND_SOC_DAIFMT_LEFT_J |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS;
|
||||
data->dai[1].ops = &imx_dsp_ops_be;
|
||||
data->dai[1].be_hw_params_fixup = be_hw_params_fixup;
|
||||
|
||||
data->card.dapm_routes = imx_dsp_audio_map;
|
||||
data->card.num_dapm_routes = ARRAY_SIZE(imx_dsp_audio_map);
|
||||
data->card.num_links = 2;
|
||||
data->card.dai_link = data->dai;
|
||||
|
||||
data->card.dev = &pdev->dev;
|
||||
data->card.owner = THIS_MODULE;
|
||||
ret = snd_soc_of_parse_card_name(&data->card, "model");
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
platform_set_drvdata(pdev, &data->card);
|
||||
snd_soc_card_set_drvdata(&data->card, data);
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (cpu_np)
|
||||
of_node_put(cpu_np);
|
||||
if (codec_np)
|
||||
of_node_put(codec_np);
|
||||
if (platform_np)
|
||||
of_node_put(platform_np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_dsp_audio_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx-dsp-audio", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_dsp_audio_dt_ids);
|
||||
|
||||
static struct platform_driver imx_dsp_audio_driver = {
|
||||
.driver = {
|
||||
.name = "imx-dsp-audio",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = imx_dsp_audio_dt_ids,
|
||||
},
|
||||
.probe = imx_dsp_audio_probe,
|
||||
};
|
||||
module_platform_driver(imx_dsp_audio_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
1193
sound/soc/fsl/imx-hdmi-dma.c
Normal file
1193
sound/soc/fsl/imx-hdmi-dma.c
Normal file
File diff suppressed because it is too large
Load Diff
117
sound/soc/fsl/imx-hdmi.c
Normal file
117
sound/soc/fsl/imx-hdmi.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* ASoC HDMI Transmitter driver for IMX development boards
|
||||
*
|
||||
* Copyright (C) 2011-2016 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* based on stmp3780_devb_hdmi.c
|
||||
*
|
||||
* Vladimir Barinov <vbarinov@embeddedalley.com>
|
||||
*
|
||||
* Copyright 2008 SigmaTel, Inc
|
||||
* Copyright 2008 Embedded Alley Solutions, Inc
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/mfd/mxc-hdmi-core.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "imx-hdmi.h"
|
||||
|
||||
SND_SOC_DAILINK_DEFS(hifi,
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY()),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("hdmi-audio-codec", "i2s-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("imx-hdmi-audio")));
|
||||
|
||||
/* imx digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link imx_hdmi_dai_link = {
|
||||
.name = "i.MX HDMI Audio Tx",
|
||||
.stream_name = "i.MX HDMI Audio Tx",
|
||||
SND_SOC_DAILINK_REG(hifi),
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_card_imx_hdmi = {
|
||||
.name = "imx-hdmi-soc",
|
||||
.dai_link = &imx_hdmi_dai_link,
|
||||
.num_links = 1,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int imx_hdmi_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *hdmi_np, *np = pdev->dev.of_node;
|
||||
struct snd_soc_card *card = &snd_soc_card_imx_hdmi;
|
||||
struct platform_device *hdmi_pdev;
|
||||
int ret = 0;
|
||||
|
||||
if (!hdmi_get_registered()) {
|
||||
dev_err(&pdev->dev, "initialize HDMI-audio failed. load HDMI-video first!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hdmi_np = of_parse_phandle(np, "hdmi-controller", 0);
|
||||
if (!hdmi_np) {
|
||||
dev_err(&pdev->dev, "failed to find hdmi-audio cpudai\n");
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
hdmi_pdev = of_find_device_by_node(hdmi_np);
|
||||
if (!hdmi_pdev) {
|
||||
dev_err(&pdev->dev, "failed to find SSI platform device\n");
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
card->dai_link->cpus->dai_name = dev_name(&hdmi_pdev->dev);
|
||||
|
||||
platform_set_drvdata(pdev, card);
|
||||
|
||||
ret = snd_soc_register_card(card);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "failed to register card: %d\n", ret);
|
||||
|
||||
end:
|
||||
if (hdmi_np)
|
||||
of_node_put(hdmi_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_hdmi_audio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_unregister_card(card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_hdmi_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx-audio-hdmi", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids);
|
||||
|
||||
static struct platform_driver imx_hdmi_audio_driver = {
|
||||
.probe = imx_hdmi_audio_probe,
|
||||
.remove = imx_hdmi_audio_remove,
|
||||
.driver = {
|
||||
.of_match_table = imx_hdmi_dt_ids,
|
||||
.name = "imx-audio-hdmi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(imx_hdmi_audio_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("IMX HDMI TX ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:imx-audio-hdmi");
|
106
sound/soc/fsl/imx-hdmi.h
Normal file
106
sound/soc/fsl/imx-hdmi.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __IMX_HDMI_H
|
||||
#define __IMX_HDMI_H
|
||||
|
||||
struct imx_hdmi_sdma_params {
|
||||
dma_addr_t phyaddr;
|
||||
u32 buffer_num;
|
||||
int dma;
|
||||
};
|
||||
|
||||
struct imx_hdmi {
|
||||
struct snd_soc_dai_driver cpu_dai_drv;
|
||||
struct platform_device *codec_dev;
|
||||
struct platform_device *dma_dev;
|
||||
struct platform_device *pdev;
|
||||
struct clk *isfr_clk;
|
||||
struct clk *iahb_clk;
|
||||
struct clk *mipi_core_clk;
|
||||
};
|
||||
|
||||
#define HDMI_MAX_RATES 7
|
||||
#define HDMI_MAX_SAMPLE_SIZE 3
|
||||
#define HDMI_MAX_CHANNEL_CONSTRAINTS 4
|
||||
|
||||
#define MXC_HDMI_RATES_PLAYBACK \
|
||||
(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
|
||||
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
|
||||
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
|
||||
|
||||
#define MXC_HDMI_FORMATS_PLAYBACK \
|
||||
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
|
||||
|
||||
union hdmi_audio_header_t {
|
||||
uint64_t U;
|
||||
struct {
|
||||
unsigned consumer:1;
|
||||
unsigned linear_pcm:1;
|
||||
unsigned copyright:1;
|
||||
unsigned pre_emphasis:3;
|
||||
unsigned mode:2;
|
||||
|
||||
unsigned category_code:8;
|
||||
|
||||
unsigned source:4;
|
||||
unsigned channel:4;
|
||||
|
||||
unsigned sample_freq:4;
|
||||
unsigned clock_acc:2;
|
||||
unsigned reserved0:2;
|
||||
|
||||
unsigned word_length:4;
|
||||
unsigned org_sample_freq:4;
|
||||
|
||||
unsigned cgms_a:2;
|
||||
unsigned reserved1:6;
|
||||
|
||||
unsigned reserved2:8;
|
||||
|
||||
unsigned reserved3:8;
|
||||
} B;
|
||||
unsigned char status[8];
|
||||
};
|
||||
|
||||
union hdmi_audio_dma_data_t {
|
||||
uint32_t U;
|
||||
struct {
|
||||
unsigned data:24;
|
||||
unsigned v:1;
|
||||
unsigned u:1;
|
||||
unsigned c:1;
|
||||
unsigned p:1;
|
||||
unsigned b:1;
|
||||
unsigned reserved:3;
|
||||
} B;
|
||||
};
|
||||
|
||||
extern union hdmi_audio_header_t iec_header;
|
||||
|
||||
#define hdmi_audio_writeb(reg, bit, val) \
|
||||
do { \
|
||||
hdmi_mask_writeb(val, HDMI_ ## reg, \
|
||||
HDMI_ ## reg ## _ ## bit ## _OFFSET, \
|
||||
HDMI_ ## reg ## _ ## bit ## _MASK); \
|
||||
pr_debug("Set reg: HDMI_" #reg " (0x%x) "\
|
||||
"bit: HDMI_" #reg "_" #bit " (%d) to val: %x\n", \
|
||||
HDMI_ ## reg, HDMI_ ## reg ## _ ## bit ## _OFFSET, val); \
|
||||
} while (0)
|
||||
|
||||
#endif /* __IMX_HDMI_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user