mirror of
https://github.com/brain-hackers/linux-brain.git
synced 2024-06-09 23:36:23 +09:00
Merge remote-tracking branch 'origin/audio/rpmsg' into audio/next
* origin/audio/rpmsg: (59 commits) LF-215: ASoC: fsl_rpmsg_i2s: Enable WQ_FREEZABLE for workqueue ASoC: imx-pcm-rpmsg: Remove the prtd ASoC: imx-pcm-rpmsg: Fix writecombine/wc build error ASoC: rpmsg_ak4497: ignore suspend with DAPM Revert "ASoC: soc-pcm: remove soc_rtdcom_ack()" ...
This commit is contained in:
commit
d7ba9dc826
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";
|
||||
};
|
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>;
|
||||
};
|
|
@ -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
|
||||
|
@ -1439,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
|
||||
|
|
|
@ -275,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
|
||||
|
@ -559,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
|
||||
|
|
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
|
@ -90,6 +90,13 @@ config SND_SOC_FSL_DSP
|
|||
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.
|
||||
|
||||
|
@ -103,6 +110,11 @@ 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
|
||||
|
@ -405,6 +417,20 @@ config SND_SOC_IMX_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)
|
||||
|
|
|
@ -29,6 +29,7 @@ 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
|
||||
|
@ -43,6 +44,7 @@ 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
|
||||
|
@ -63,6 +65,7 @@ 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
|
||||
|
@ -90,6 +93,7 @@ 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
|
||||
|
@ -103,6 +107,7 @@ 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
|
||||
|
|
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 */
|
893
sound/soc/fsl/imx-pcm-rpmsg.c
Normal file
893
sound/soc/fsl/imx-pcm-rpmsg.c
Normal file
|
@ -0,0 +1,893 @@
|
|||
/*
|
||||
* imx-rpmsg-platform.c -- ALSA Soc Audio Layer
|
||||
*
|
||||
* 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/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/imx_rpmsg.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "imx-pcm.h"
|
||||
#include "fsl_rpmsg_i2s.h"
|
||||
#include "../../core/pcm_local.h"
|
||||
|
||||
#define DRV_NAME "imx_pcm_rpmsg"
|
||||
|
||||
struct i2s_info *i2s_info_g;
|
||||
|
||||
static struct snd_pcm_hardware imx_rpmsg_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
.buffer_bytes_max = IMX_SAI_DMABUF_SIZE,
|
||||
.period_bytes_min = 512,
|
||||
.period_bytes_max = 65532, /* Limited by SDMA engine */
|
||||
.periods_min = 2,
|
||||
.periods_max = 6000,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static int imx_rpmsg_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_HW_PARAM];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_HW_PARAM];
|
||||
|
||||
rpmsg->send_msg.param.rate = params_rate(params);
|
||||
|
||||
if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
|
||||
rpmsg->send_msg.param.format = RPMSG_S16_LE;
|
||||
else if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE)
|
||||
rpmsg->send_msg.param.format = RPMSG_S24_LE;
|
||||
else if (params_format(params) == SNDRV_PCM_FORMAT_DSD_U16_LE)
|
||||
rpmsg->send_msg.param.format = SNDRV_PCM_FORMAT_DSD_U16_LE;
|
||||
else if (params_format(params) == SNDRV_PCM_FORMAT_DSD_U32_LE)
|
||||
rpmsg->send_msg.param.format = SNDRV_PCM_FORMAT_DSD_U32_LE;
|
||||
else
|
||||
rpmsg->send_msg.param.format = RPMSG_S32_LE;
|
||||
|
||||
if (params_channels(params) == 1)
|
||||
rpmsg->send_msg.param.channels = RPMSG_CH_LEFT;
|
||||
else
|
||||
rpmsg->send_msg.param.channels = RPMSG_CH_STEREO;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
runtime->dma_bytes = params_buffer_bytes(params);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg->send_msg.header.cmd = I2S_TX_HW_PARAM;
|
||||
else
|
||||
rpmsg->send_msg.header.cmd = I2S_RX_HW_PARAM;
|
||||
|
||||
i2s_info->send_message(rpmsg, i2s_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rpmsg_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_pcm_set_runtime_buffer(substream, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t imx_rpmsg_pcm_pointer(
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
unsigned int pos = 0;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
int buffer_tail = 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM];
|
||||
|
||||
buffer_tail = rpmsg->recv_msg.param.buffer_tail;
|
||||
pos = buffer_tail * snd_pcm_lib_period_bytes(substream);
|
||||
|
||||
return bytes_to_frames(substream->runtime, pos);
|
||||
}
|
||||
|
||||
static void imx_rpmsg_timer_callback(struct timer_list *t)
|
||||
{
|
||||
struct stream_timer *stream_timer =
|
||||
from_timer(stream_timer, t, timer);
|
||||
struct snd_pcm_substream *substream = stream_timer->substream;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
unsigned long flags;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM];
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg->send_msg.header.cmd = I2S_TX_PERIOD_DONE;
|
||||
else
|
||||
rpmsg->send_msg.header.cmd = I2S_RX_PERIOD_DONE;
|
||||
|
||||
spin_lock_irqsave(&i2s_info->wq_lock, flags);
|
||||
if (i2s_info->work_write_index != i2s_info->work_read_index) {
|
||||
int index = i2s_info->work_write_index;
|
||||
memcpy(&i2s_info->work_list[index].msg, rpmsg,
|
||||
sizeof(struct i2s_rpmsg_s));
|
||||
queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work);
|
||||
i2s_info->work_write_index++;
|
||||
i2s_info->work_write_index %= WORK_MAX_NUM;
|
||||
} else
|
||||
i2s_info->msg_drop_count[substream->stream]++;
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
}
|
||||
|
||||
static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
int cmd;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_OPEN];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_OPEN];
|
||||
|
||||
imx_rpmsg_pcm_hardware.buffer_bytes_max =
|
||||
i2s_info->prealloc_buffer_size;
|
||||
imx_rpmsg_pcm_hardware.period_bytes_max =
|
||||
imx_rpmsg_pcm_hardware.buffer_bytes_max / 2;
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &imx_rpmsg_pcm_hardware);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg->send_msg.header.cmd = I2S_TX_OPEN;
|
||||
else
|
||||
rpmsg->send_msg.header.cmd = I2S_RX_OPEN;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
cmd = I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM;
|
||||
i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0;
|
||||
i2s_info->rpmsg[cmd].recv_msg.param.buffer_tail = 0;
|
||||
i2s_info->rpmsg[I2S_TX_POINTER].recv_msg.param.buffer_offset = 0;
|
||||
} else {
|
||||
cmd = I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM;
|
||||
i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0;
|
||||
i2s_info->rpmsg[cmd].recv_msg.param.buffer_tail = 0;
|
||||
i2s_info->rpmsg[I2S_RX_POINTER].recv_msg.param.buffer_offset = 0;
|
||||
}
|
||||
|
||||
i2s_info->send_message(rpmsg, i2s_info);
|
||||
|
||||
ret = snd_pcm_hw_constraint_integer(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
i2s_info->msg_drop_count[substream->stream] = 0;
|
||||
|
||||
/*create thread*/
|
||||
i2s_info->stream_timer[substream->stream].substream = substream;
|
||||
|
||||
timer_setup(&i2s_info->stream_timer[substream->stream].timer,
|
||||
imx_rpmsg_timer_callback, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_rpmsg_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_CLOSE];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_CLOSE];
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg->send_msg.header.cmd = I2S_TX_CLOSE;
|
||||
else
|
||||
rpmsg->send_msg.header.cmd = I2S_RX_CLOSE;
|
||||
flush_workqueue(i2s_info->rpmsg_wq);
|
||||
i2s_info->send_message(rpmsg, i2s_info);
|
||||
|
||||
del_timer(&i2s_info->stream_timer[substream->stream].timer);
|
||||
|
||||
rtd->dai_link->ignore_suspend = 0;
|
||||
|
||||
if (i2s_info->msg_drop_count[substream->stream])
|
||||
dev_warn(rtd->dev, "Msg is dropped!, number is %d\n",
|
||||
i2s_info->msg_drop_count[substream->stream]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_rpmsg_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
|
||||
/* NON-MMAP mode, NONBLOCK, Version 2, enable lpa in dts
|
||||
* four condition to determine the lpa is enabled.
|
||||
*/
|
||||
if ((runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
|
||||
runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) &&
|
||||
rpmsg_i2s->version == 2 &&
|
||||
rpmsg_i2s->enable_lpa) {
|
||||
rtd->dai_link->ignore_suspend = 1;
|
||||
rpmsg_i2s->force_lpa = 1;
|
||||
} else
|
||||
rpmsg_i2s->force_lpa = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rpmsg_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
return dma_mmap_wc(substream->pcm->card->dev, vma,
|
||||
runtime->dma_area,
|
||||
runtime->dma_addr,
|
||||
runtime->dma_bytes);
|
||||
}
|
||||
|
||||
static void imx_rpmsg_pcm_dma_complete(void *arg)
|
||||
{
|
||||
struct snd_pcm_substream *substream = arg;
|
||||
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
|
||||
static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
unsigned long flags;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_BUFFER];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_BUFFER];
|
||||
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg->send_msg.header.cmd = I2S_TX_BUFFER;
|
||||
else
|
||||
rpmsg->send_msg.header.cmd = I2S_RX_BUFFER;
|
||||
|
||||
rpmsg->send_msg.param.buffer_addr = substream->runtime->dma_addr;
|
||||
rpmsg->send_msg.param.buffer_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
rpmsg->send_msg.param.period_size = snd_pcm_lib_period_bytes(substream);
|
||||
rpmsg->send_msg.param.buffer_tail = 0;
|
||||
|
||||
i2s_info->num_period[substream->stream] =
|
||||
rpmsg->send_msg.param.buffer_size /
|
||||
rpmsg->send_msg.param.period_size;
|
||||
|
||||
i2s_info->callback[substream->stream] = imx_rpmsg_pcm_dma_complete;
|
||||
i2s_info->callback_param[substream->stream] = substream;
|
||||
|
||||
spin_lock_irqsave(&i2s_info->wq_lock, flags);
|
||||
if (i2s_info->work_write_index != i2s_info->work_read_index) {
|
||||
int index = i2s_info->work_write_index;
|
||||
memcpy(&i2s_info->work_list[index].msg, rpmsg,
|
||||
sizeof(struct i2s_rpmsg_s));
|
||||
queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work);
|
||||
i2s_info->work_write_index++;
|
||||
i2s_info->work_write_index %= WORK_MAX_NUM;
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rpmsg_async_issue_pending(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
unsigned long flags;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_START];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_START];
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg->send_msg.header.cmd = I2S_TX_START;
|
||||
else
|
||||
rpmsg->send_msg.header.cmd = I2S_RX_START;
|
||||
|
||||
spin_lock_irqsave(&i2s_info->wq_lock, flags);
|
||||
if (i2s_info->work_write_index != i2s_info->work_read_index) {
|
||||
int index = i2s_info->work_write_index;
|
||||
memcpy(&i2s_info->work_list[index].msg, rpmsg,
|
||||
sizeof(struct i2s_rpmsg_s));
|
||||
queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work);
|
||||
i2s_info->work_write_index++;
|
||||
i2s_info->work_write_index %= WORK_MAX_NUM;
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rpmsg_restart(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
unsigned long flags;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_RESTART];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_RESTART];
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg->send_msg.header.cmd = I2S_TX_RESTART;
|
||||
else
|
||||
rpmsg->send_msg.header.cmd = I2S_RX_RESTART;
|
||||
|
||||
spin_lock_irqsave(&i2s_info->wq_lock, flags);
|
||||
if (i2s_info->work_write_index != i2s_info->work_read_index) {
|
||||
int index = i2s_info->work_write_index;
|
||||
memcpy(&i2s_info->work_list[index].msg, rpmsg,
|
||||
sizeof(struct i2s_rpmsg_s));
|
||||
queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work);
|
||||
i2s_info->work_write_index++;
|
||||
i2s_info->work_write_index %= WORK_MAX_NUM;
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
return -EPIPE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rpmsg_pause(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
unsigned long flags;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_PAUSE];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_PAUSE];
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg->send_msg.header.cmd = I2S_TX_PAUSE;
|
||||
else
|
||||
rpmsg->send_msg.header.cmd = I2S_RX_PAUSE;
|
||||
|
||||
spin_lock_irqsave(&i2s_info->wq_lock, flags);
|
||||
if (i2s_info->work_write_index != i2s_info->work_read_index) {
|
||||
int index = i2s_info->work_write_index;
|
||||
memcpy(&i2s_info->work_list[index].msg, rpmsg,
|
||||
sizeof(struct i2s_rpmsg_s));
|
||||
queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work);
|
||||
i2s_info->work_write_index++;
|
||||
i2s_info->work_write_index %= WORK_MAX_NUM;
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
return -EPIPE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rpmsg_terminate_all(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
int cmd;
|
||||
unsigned long flags;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_TERMINATE];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_TERMINATE];
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg->send_msg.header.cmd = I2S_TX_TERMINATE;
|
||||
else
|
||||
rpmsg->send_msg.header.cmd = I2S_RX_TERMINATE;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
cmd = I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM;
|
||||
i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0;
|
||||
i2s_info->rpmsg[cmd].recv_msg.param.buffer_tail = 0;
|
||||
i2s_info->rpmsg[I2S_TX_POINTER].recv_msg.param.buffer_offset = 0;
|
||||
} else {
|
||||
cmd = I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM;
|
||||
i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0;
|
||||
i2s_info->rpmsg[cmd].recv_msg.param.buffer_tail = 0;
|
||||
i2s_info->rpmsg[I2S_RX_POINTER].recv_msg.param.buffer_offset = 0;
|
||||
}
|
||||
|
||||
del_timer(&i2s_info->stream_timer[substream->stream].timer);
|
||||
|
||||
spin_lock_irqsave(&i2s_info->wq_lock, flags);
|
||||
if (i2s_info->work_write_index != i2s_info->work_read_index) {
|
||||
int index = i2s_info->work_write_index;
|
||||
memcpy(&i2s_info->work_list[index].msg, rpmsg,
|
||||
sizeof(struct i2s_rpmsg_s));
|
||||
queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work);
|
||||
i2s_info->work_write_index++;
|
||||
i2s_info->work_write_index %= WORK_MAX_NUM;
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
ret = imx_rpmsg_pcm_prepare_and_submit(substream);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = imx_rpmsg_async_issue_pending(substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
if (rpmsg_i2s->force_lpa)
|
||||
break;
|
||||
/* fall through */
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
ret = imx_rpmsg_restart(substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
if (!rpmsg_i2s->force_lpa) {
|
||||
if (runtime->info & SNDRV_PCM_INFO_PAUSE)
|
||||
ret = imx_rpmsg_pause(substream);
|
||||
else
|
||||
ret = imx_rpmsg_terminate_all(substream);
|
||||
}
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
ret = imx_rpmsg_pause(substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
ret = imx_rpmsg_terminate_all(substream);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int imx_rpmsg_pcm_ack(struct snd_pcm_substream *substream)
|
||||
{
|
||||
/*send the hw_avail size through rpmsg*/
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
int buffer_tail = 0;
|
||||
int writen_num = 0;
|
||||
snd_pcm_sframes_t avail;
|
||||
unsigned long flags;
|
||||
|
||||
if (!rpmsg_i2s->force_lpa)
|
||||
return 0;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg = &i2s_info->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM];
|
||||
else
|
||||
rpmsg = &i2s_info->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM];
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rpmsg->send_msg.header.cmd = I2S_TX_PERIOD_DONE;
|
||||
else
|
||||
rpmsg->send_msg.header.cmd = I2S_RX_PERIOD_DONE;
|
||||
|
||||
rpmsg->send_msg.header.type = I2S_TYPE_C;
|
||||
|
||||
buffer_tail = (frames_to_bytes(runtime, runtime->control->appl_ptr) %
|
||||
snd_pcm_lib_buffer_bytes(substream));
|
||||
buffer_tail = buffer_tail / snd_pcm_lib_period_bytes(substream);
|
||||
|
||||
if (buffer_tail != rpmsg->send_msg.param.buffer_tail) {
|
||||
writen_num = buffer_tail - rpmsg->send_msg.param.buffer_tail;
|
||||
if (writen_num < 0)
|
||||
writen_num += runtime->periods;
|
||||
|
||||
rpmsg->send_msg.param.buffer_tail = buffer_tail;
|
||||
|
||||
spin_lock_irqsave(&i2s_info->lock[substream->stream], flags);
|
||||
memcpy(&i2s_info->period_done_msg[substream->stream], rpmsg,
|
||||
sizeof(struct i2s_rpmsg_s));
|
||||
|
||||
i2s_info->period_done_msg_enabled[substream->stream] = true;
|
||||
spin_unlock_irqrestore(&i2s_info->lock[substream->stream], flags);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
avail = snd_pcm_playback_hw_avail(runtime);
|
||||
else
|
||||
avail = snd_pcm_capture_hw_avail(runtime);
|
||||
|
||||
if ((avail - writen_num * runtime->period_size) <= runtime->period_size) {
|
||||
spin_lock_irqsave(&i2s_info->wq_lock, flags);
|
||||
if (i2s_info->work_write_index != i2s_info->work_read_index) {
|
||||
int index = i2s_info->work_write_index;
|
||||
memcpy(&i2s_info->work_list[index].msg, rpmsg,
|
||||
sizeof(struct i2s_rpmsg_s));
|
||||
queue_work(i2s_info->rpmsg_wq,
|
||||
&i2s_info->work_list[index].work);
|
||||
i2s_info->work_write_index++;
|
||||
i2s_info->work_write_index %= WORK_MAX_NUM;
|
||||
} else
|
||||
i2s_info->msg_drop_count[substream->stream]++;
|
||||
spin_unlock_irqrestore(&i2s_info->wq_lock, flags);
|
||||
} else {
|
||||
if (rpmsg_i2s->force_lpa && !timer_pending(&i2s_info->stream_timer[substream->stream].timer)) {
|
||||
int time_msec;
|
||||
time_msec = (int)(runtime->period_size*1000/runtime->rate);
|
||||
mod_timer(&i2s_info->stream_timer[substream->stream].timer,
|
||||
jiffies + msecs_to_jiffies(time_msec));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops imx_rpmsg_pcm_ops = {
|
||||
.open = imx_rpmsg_pcm_open,
|
||||
.close = imx_rpmsg_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = imx_rpmsg_pcm_hw_params,
|
||||
.hw_free = imx_rpmsg_pcm_hw_free,
|
||||
.trigger = imx_rpmsg_pcm_trigger,
|
||||
.pointer = imx_rpmsg_pcm_pointer,
|
||||
.mmap = imx_rpmsg_pcm_mmap,
|
||||
.ack = imx_rpmsg_pcm_ack,
|
||||
.prepare = imx_rpmsg_pcm_prepare,
|
||||
};
|
||||
|
||||
static int imx_rpmsg_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
|
||||
int stream, int size)
|
||||
{
|
||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||
|
||||
buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
buf->dev.dev = pcm->card->dev;
|
||||
buf->private_data = NULL;
|
||||
buf->area = dma_alloc_wc(pcm->card->dev, size,
|
||||
&buf->addr, GFP_KERNEL);
|
||||
if (!buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->bytes = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void imx_rpmsg_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
|
||||
for (stream = SNDRV_PCM_STREAM_PLAYBACK;
|
||||
stream < SNDRV_PCM_STREAM_LAST; stream++) {
|
||||
substream = pcm->streams[stream].substream;
|
||||
if (!substream)
|
||||
continue;
|
||||
|
||||
buf = &substream->dma_buffer;
|
||||
if (!buf->area)
|
||||
continue;
|
||||
|
||||
dma_free_wc(pcm->card->dev, buf->bytes,
|
||||
buf->area, buf->addr);
|
||||
buf->area = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int imx_rpmsg_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev);
|
||||
struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info;
|
||||
int ret;
|
||||
|
||||
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
|
||||
ret = imx_rpmsg_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_PLAYBACK,
|
||||
i2s_info->prealloc_buffer_size);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
|
||||
ret = imx_rpmsg_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_CAPTURE,
|
||||
i2s_info->prealloc_buffer_size);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
/* free preallocated buffers in case of error */
|
||||
if (ret)
|
||||
imx_rpmsg_pcm_free_dma_buffers(pcm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_component_driver imx_rpmsg_soc_component = {
|
||||
.name = DRV_NAME,
|
||||
.ops = &imx_rpmsg_pcm_ops,
|
||||
.pcm_new = imx_rpmsg_pcm_new,
|
||||
.pcm_free = imx_rpmsg_pcm_free_dma_buffers,
|
||||
};
|
||||
|
||||
int imx_rpmsg_platform_register(struct device *dev)
|
||||
{
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev);
|
||||
struct snd_soc_component *component;
|
||||
int ret;
|
||||
|
||||
i2s_info_g = &rpmsg_i2s->i2s_info;
|
||||
|
||||
ret = devm_snd_soc_register_component(dev, &imx_rpmsg_soc_component,
|
||||
NULL, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
component = snd_soc_lookup_component(dev, DRV_NAME);
|
||||
if (!component)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
component->debugfs_prefix = "dma";
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(imx_rpmsg_platform_register);
|
||||
|
||||
static int i2s_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
|
||||
void *priv, u32 src)
|
||||
{
|
||||
struct i2s_rpmsg_r *msg = (struct i2s_rpmsg_r *)data;
|
||||
struct i2s_rpmsg *rpmsg;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(&rpdev->dev, "get from%d: cmd:%d. %d\n",
|
||||
src, msg->header.cmd, msg->param.resp);
|
||||
|
||||
if (msg->header.type == I2S_TYPE_C) {
|
||||
if (msg->header.cmd == I2S_TX_PERIOD_DONE) {
|
||||
spin_lock_irqsave(&i2s_info_g->lock[0], flags);
|
||||
rpmsg = &i2s_info_g->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM];
|
||||
|
||||
if (msg->header.major == 1 && msg->header.minor == 2)
|
||||
rpmsg->recv_msg.param.buffer_tail =
|
||||
msg->param.buffer_tail;
|
||||
else
|
||||
rpmsg->recv_msg.param.buffer_tail++;
|
||||
|
||||
rpmsg->recv_msg.param.buffer_tail %=
|
||||
i2s_info_g->num_period[0];
|
||||
|
||||
spin_unlock_irqrestore(&i2s_info_g->lock[0], flags);
|
||||
i2s_info_g->callback[0](i2s_info_g->callback_param[0]);
|
||||
|
||||
} else if (msg->header.cmd == I2S_RX_PERIOD_DONE) {
|
||||
spin_lock_irqsave(&i2s_info_g->lock[1], flags);
|
||||
rpmsg = &i2s_info_g->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM];
|
||||
|
||||
if (msg->header.major == 1 && msg->header.minor == 2)
|
||||
rpmsg->recv_msg.param.buffer_tail =
|
||||
msg->param.buffer_tail;
|
||||
else
|
||||
rpmsg->recv_msg.param.buffer_tail++;
|
||||
|
||||
rpmsg->recv_msg.param.buffer_tail %=
|
||||
i2s_info_g->num_period[1];
|
||||
spin_unlock_irqrestore(&i2s_info_g->lock[1], flags);
|
||||
i2s_info_g->callback[1](i2s_info_g->callback_param[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->header.type == I2S_TYPE_B) {
|
||||
memcpy(&i2s_info_g->recv_msg, msg, sizeof(struct i2s_rpmsg_r));
|
||||
complete(&i2s_info_g->cmd_complete);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2s_rpmsg_probe(struct rpmsg_device *rpdev)
|
||||
{
|
||||
struct platform_device *codec_pdev;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s = NULL;
|
||||
struct fsl_rpmsg_codec rpmsg_codec[3];
|
||||
int ret;
|
||||
|
||||
if (!i2s_info_g)
|
||||
return 0;
|
||||
|
||||
i2s_info_g->rpdev = rpdev;
|
||||
|
||||
init_completion(&i2s_info_g->cmd_complete);
|
||||
|
||||
dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
|
||||
rpdev->src, rpdev->dst);
|
||||
|
||||
rpmsg_i2s = container_of(i2s_info_g, struct fsl_rpmsg_i2s, i2s_info);
|
||||
|
||||
if (rpmsg_i2s->codec_wm8960) {
|
||||
rpmsg_codec[0].audioindex = rpmsg_i2s->codec_wm8960 >> 16;
|
||||
rpmsg_codec[0].shared_lrclk = true;
|
||||
rpmsg_codec[0].capless = false;
|
||||
codec_pdev = platform_device_register_data(
|
||||
&rpmsg_i2s->pdev->dev,
|
||||
RPMSG_CODEC_DRV_NAME_WM8960,
|
||||
PLATFORM_DEVID_NONE,
|
||||
&rpmsg_codec[0], sizeof(struct fsl_rpmsg_codec));
|
||||
if (IS_ERR(codec_pdev)) {
|
||||
dev_err(&rpdev->dev,
|
||||
"failed to register rpmsg audio codec\n");
|
||||
ret = PTR_ERR(codec_pdev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (rpmsg_i2s->codec_cs42888) {
|
||||
rpmsg_codec[1].audioindex = rpmsg_i2s->codec_cs42888 >> 16;
|
||||
strcpy(rpmsg_codec[1].name, "cs42888");
|
||||
rpmsg_codec[1].num_adcs = 2;
|
||||
|
||||
codec_pdev = platform_device_register_data(
|
||||
&rpmsg_i2s->pdev->dev,
|
||||
RPMSG_CODEC_DRV_NAME_CS42888,
|
||||
PLATFORM_DEVID_NONE,
|
||||
&rpmsg_codec[1], sizeof(struct fsl_rpmsg_codec));
|
||||
if (IS_ERR(codec_pdev)) {
|
||||
dev_err(&rpdev->dev,
|
||||
"failed to register rpmsg audio codec\n");
|
||||
ret = PTR_ERR(codec_pdev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (rpmsg_i2s->codec_ak4497) {
|
||||
rpmsg_codec[2].audioindex = rpmsg_i2s->codec_ak4497 >> 16;
|
||||
codec_pdev = platform_device_register_data(
|
||||
&rpmsg_i2s->pdev->dev,
|
||||
RPMSG_CODEC_DRV_NAME_AK4497,
|
||||
PLATFORM_DEVID_NONE,
|
||||
&rpmsg_codec[2], sizeof(struct fsl_rpmsg_codec));
|
||||
if (IS_ERR(codec_pdev)) {
|
||||
dev_err(&rpdev->dev,
|
||||
"failed to register rpmsg audio codec\n");
|
||||
ret = PTR_ERR(codec_pdev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i2s_rpmsg_remove(struct rpmsg_device *rpdev)
|
||||
{
|
||||
dev_info(&rpdev->dev, "i2s rpmsg driver is removed\n");
|
||||
}
|
||||
|
||||
static struct rpmsg_device_id i2s_rpmsg_id_table[] = {
|
||||
{ .name = "rpmsg-audio-channel" },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct rpmsg_driver i2s_rpmsg_driver = {
|
||||
.drv.name = "i2s_rpmsg",
|
||||
.drv.owner = THIS_MODULE,
|
||||
.id_table = i2s_rpmsg_id_table,
|
||||
.probe = i2s_rpmsg_probe,
|
||||
.callback = i2s_rpmsg_cb,
|
||||
.remove = i2s_rpmsg_remove,
|
||||
};
|
||||
|
||||
static int __init i2s_rpmsg_init(void)
|
||||
{
|
||||
return register_rpmsg_driver(&i2s_rpmsg_driver);
|
||||
}
|
||||
|
||||
static void __exit i2s_rpmsg_exit(void)
|
||||
{
|
||||
unregister_rpmsg_driver(&i2s_rpmsg_driver);
|
||||
}
|
||||
module_init(i2s_rpmsg_init);
|
||||
module_exit(i2s_rpmsg_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
175
sound/soc/fsl/imx-rpmsg.c
Normal file
175
sound/soc/fsl/imx-rpmsg.c
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (C) 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/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 <linux/pinctrl/consumer.h>
|
||||
#include "fsl_rpmsg_i2s.h"
|
||||
|
||||
struct imx_rpmsg_data {
|
||||
struct snd_soc_dai_link dai[1];
|
||||
struct snd_soc_card card;
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget imx_wm8960_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Main MIC", NULL),
|
||||
};
|
||||
|
||||
static int imx_rpmsg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *cpu_np;
|
||||
struct platform_device *cpu_pdev;
|
||||
struct imx_rpmsg_data *data;
|
||||
struct fsl_rpmsg_i2s *rpmsg_i2s;
|
||||
struct snd_soc_dai_link_component *dlc;
|
||||
int ret;
|
||||
|
||||
dlc = devm_kzalloc(&pdev->dev, 3 * sizeof(*dlc), GFP_KERNEL);
|
||||
if (!dlc)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
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;
|
||||
}
|
||||
|
||||
rpmsg_i2s = platform_get_drvdata(cpu_pdev);
|
||||
|
||||
data->dai[0].cpus = &dlc[0];
|
||||
data->dai[0].num_cpus = 1;
|
||||
data->dai[0].platforms = &dlc[1];
|
||||
data->dai[0].num_platforms = 1;
|
||||
data->dai[0].codecs = &dlc[2];
|
||||
data->dai[0].num_codecs = 1;
|
||||
|
||||
data->dai[0].name = "rpmsg hifi";
|
||||
data->dai[0].stream_name = "rpmsg hifi";
|
||||
data->dai[0].dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBM_CFM;
|
||||
|
||||
if (rpmsg_i2s->codec_wm8960) {
|
||||
data->dai[0].codecs->dai_name = "rpmsg-wm8960-hifi";
|
||||
data->dai[0].codecs->name = "rpmsg-audio-codec-wm8960";
|
||||
}
|
||||
|
||||
if (rpmsg_i2s->codec_dummy) {
|
||||
data->dai[0].codecs->dai_name = "snd-soc-dummy-dai";
|
||||
data->dai[0].codecs->name = "snd-soc-dummy";
|
||||
}
|
||||
|
||||
if (rpmsg_i2s->codec_ak4497) {
|
||||
data->dai[0].codecs->dai_name = "rpmsg-ak4497-aif";
|
||||
data->dai[0].codecs->name = "rpmsg-audio-codec-ak4497";
|
||||
data->dai[0].dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS;
|
||||
}
|
||||
|
||||
data->dai[0].cpus->dai_name = dev_name(&cpu_pdev->dev);
|
||||
data->dai[0].platforms->of_node = cpu_np;
|
||||
data->dai[0].playback_only = true;
|
||||
data->dai[0].capture_only = true;
|
||||
data->card.num_links = 1;
|
||||
data->card.dai_link = data->dai;
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "rpmsg-out"))
|
||||
data->dai[0].capture_only = false;
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "rpmsg-in"))
|
||||
data->dai[0].playback_only = false;
|
||||
|
||||
if (data->dai[0].playback_only && data->dai[0].capture_only) {
|
||||
dev_err(&pdev->dev, "no enabled rpmsg DAI link\n");
|
||||
ret = -EINVAL;
|
||||
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;
|
||||
|
||||
if (rpmsg_i2s->codec_wm8960) {
|
||||
ret = snd_soc_of_parse_audio_routing(&data->card,
|
||||
"audio-routing");
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
data->card.dapm_widgets = imx_wm8960_dapm_widgets;
|
||||
data->card.num_dapm_widgets =
|
||||
ARRAY_SIZE(imx_wm8960_dapm_widgets);
|
||||
}
|
||||
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_rpmsg_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx-audio-rpmsg", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_rpmsg_dt_ids);
|
||||
|
||||
static struct platform_driver imx_rpmsg_driver = {
|
||||
.driver = {
|
||||
.name = "imx-audio-rpmsg",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = imx_rpmsg_dt_ids,
|
||||
},
|
||||
.probe = imx_rpmsg_probe,
|
||||
};
|
||||
module_platform_driver(imx_rpmsg_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("Freescale i.MX rpmsg audio ASoC machine driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:imx-rpmsg");
|
|
@ -2818,6 +2818,26 @@ static void soc_pcm_private_free(struct snd_pcm *pcm)
|
|||
snd_soc_pcm_component_free(pcm);
|
||||
}
|
||||
|
||||
static int soc_rtdcom_ack(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_rtdcom_list *rtdcom;
|
||||
struct snd_soc_component *component;
|
||||
|
||||
for_each_rtdcom(rtd, rtdcom) {
|
||||
component = rtdcom->component;
|
||||
|
||||
if (!component->driver->ops ||
|
||||
!component->driver->ops->ack)
|
||||
continue;
|
||||
|
||||
/* FIXME. it returns 1st ask now */
|
||||
return component->driver->ops->ack(substream);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* create a new pcm */
|
||||
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
|
||||
{
|
||||
|
@ -2941,6 +2961,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
|
|||
if (!ops)
|
||||
continue;
|
||||
|
||||
if (ops->ack)
|
||||
rtd->ops.ack = soc_rtdcom_ack;
|
||||
if (ops->copy_user)
|
||||
rtd->ops.copy_user = snd_soc_pcm_component_copy_user;
|
||||
if (ops->page)
|
||||
|
|
Loading…
Reference in New Issue
Block a user