Commit def69f21 authored by Mark Brown's avatar Mark Brown

Merge series "Qualcomm's lpass-hdmi ASoC driver to support audio over dp port"...

Merge series "Qualcomm's lpass-hdmi ASoC driver to support audio over dp port" from Srinivasa Rao Mandadapu <srivasam@codeaurora.org>:

These patches are to support audio over DP port on Qualcomm's SC7180 LPASS
Asoc. It includes machine driver, cpu driver, platform driver updates for
HDMI path support, device tree documention, lpass variant structure
optimization and configuration changes.
These patches depends on the DP patch series
https://patchwork.kernel.org/project/dri-devel/list/?series=332029
https://lore.kernel.org/patchwork/project/lkml/list/?series=464856

changes since V10:
    -- Moved hdmi regmap functions from lpass-hdmi.c to lpass-cpu.c
    -- Moved QCOM_REGMAP_FIELD_ALLOC macro from lpass-hdmi.c to lpass.h
changes since V9:
    -- Removed unused structures lpass_hdmi.h
changes since V8:
    -- Removed redundant structure wrapper for reg map field memebrs
    -- Updated lpass_hdmi_regmap_volatile API with appropriate registers as true
       and others as false.
changes since V7:
    -- Fixed typo errors
    -- Created Separate patch for buffer size change
changes since V6:
    -- Removed compile time define flag, which used for enabling
     HDMI code, based on corresponding config param is included.
    -- Updated reg map alloc API with reg map bulk API.
    -- Removed unnecessary line splits
changes since V5:
    -- Removed unused struct regmap *map in lpass_platform_alloc_hdmidmactl_fields.
    -- DMA alloc and free API signature change in lpass-apq8016.c, lpass-ipq806x.c
    -- Keeping API "irqreturn_t lpass_platform_hdmiif_irq" under ifdef macro
Changes Since v4:
    -- Updated with single compatible node for both I2S and HDMI.
Changes Since v3:
    -- Removed id in lpass variant structure and used snd_soc_dai_driver id.
Changes Since v2:
    -- Audio buffer size(i.e. LPASS_PLATFORM_BUFFER_SIZE) in lpass-platform.c increased.
Changes Since v1:
    -- Commit messages are updated
    -- Addressed Rob Herring review comments

V Sujith Kumar Reddy (7):
  ASoC: Add sc7180-lpass binding header hdmi define
  ASoC: dt-bindings: Add dt binding for lpass hdmi
  Asoc:qcom:lpass-cpu:Update dts property read API
  Asoc: qcom: lpass:Update lpaif_dmactl members order
  ASoC: qcom: Add support for lpass hdmi driver
  Asoc: qcom: lpass-platform : Increase buffer size
  ASoC: qcom: sc7180: Add support for audio over DP

 .../devicetree/bindings/sound/qcom,lpass-cpu.yaml  |  74 ++--
 include/dt-bindings/sound/sc7180-lpass.h           |   1 +
 sound/soc/qcom/Kconfig                             |   5 +
 sound/soc/qcom/Makefile                            |   2 +
 sound/soc/qcom/lpass-apq8016.c                     |   4 +-
 sound/soc/qcom/lpass-cpu.c                         | 249 ++++++++++++-
 sound/soc/qcom/lpass-hdmi.c                        | 258 ++++++++++++++
 sound/soc/qcom/lpass-hdmi.h                        | 102 ++++++
 sound/soc/qcom/lpass-ipq806x.c                     |   4 +-
 sound/soc/qcom/lpass-lpaif-reg.h                   |  49 ++-
 sound/soc/qcom/lpass-platform.c                    | 395 +++++++++++++++++----
 sound/soc/qcom/lpass-sc7180.c                      | 116 +++++-
 sound/soc/qcom/lpass.h                             | 124 ++++++-
 13 files changed, 1240 insertions(+), 143 deletions(-)
 create mode 100644 sound/soc/qcom/lpass-hdmi.c
 create mode 100644 sound/soc/qcom/lpass-hdmi.h

--
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc.,
is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
parents 18096cb0 2ad63dc8
...@@ -24,9 +24,10 @@ properties: ...@@ -24,9 +24,10 @@ properties:
- qcom,sc7180-lpass-cpu - qcom,sc7180-lpass-cpu
reg: reg:
maxItems: 1 maxItems: 2
description: LPAIF core registers description: LPAIF core registers
reg-names:
maxItems: 2
clocks: clocks:
minItems: 3 minItems: 3
maxItems: 6 maxItems: 6
...@@ -36,15 +37,16 @@ properties: ...@@ -36,15 +37,16 @@ properties:
maxItems: 6 maxItems: 6
interrupts: interrupts:
maxItems: 1 maxItems: 2
description: LPAIF DMA buffer interrupt description: LPAIF DMA buffer interrupt
interrupt-names:
maxItems: 2
qcom,adsp: qcom,adsp:
$ref: /schemas/types.yaml#/definitions/phandle $ref: /schemas/types.yaml#/definitions/phandle
description: Phandle for the audio DSP node description: Phandle for the audio DSP node
iommus: iommus:
maxItems: 1 maxItems: 2
description: Phandle to apps_smmu node with sid mask description: Phandle to apps_smmu node with sid mask
power-domains: power-domains:
...@@ -60,10 +62,12 @@ properties: ...@@ -60,10 +62,12 @@ properties:
const: 0 const: 0
patternProperties: patternProperties:
"(^mi2s-[0-9a-f]$|mi2s)": "^dai-link@[0-9a-f]$":
type: object type: object
description: Required properties for each DAI description: |
LPASS CPU dai node for each I2S device. Bindings of each node
depends on the specific driver providing the functionality and
properties.
properties: properties:
reg: reg:
maxItems: 1 maxItems: 1
...@@ -85,9 +89,11 @@ patternProperties: ...@@ -85,9 +89,11 @@ patternProperties:
required: required:
- compatible - compatible
- reg - reg
- reg-names
- clocks - clocks
- clock-names - clock-names
- interrupts - interrupts
- interrupt-names
- '#sound-dai-cells' - '#sound-dai-cells'
additionalProperties: false additionalProperties: false
...@@ -134,13 +140,32 @@ allOf: ...@@ -134,13 +140,32 @@ allOf:
then: then:
properties: properties:
clock-names: clock-names:
items: oneOf:
- const: pcnoc-sway-clk - items: #for I2S
- const: audio-core - const: pcnoc-sway-clk
- const: mclk0 - const: audio-core
- const: pcnoc-mport-clk - const: mclk0
- const: mi2s-bit-clk0 - const: pcnoc-mport-clk
- const: mi2s-bit-clk1 - const: mi2s-bit-clk0
- const: mi2s-bit-clk1
- items: #for HDMI
- const: pcnoc-sway-clk
- const: audio-core
- const: pcnoc-mport-clk
reg-names:
anyOf:
- items: #for I2S
- const: lpass-lpaif
- items: #for I2S and HDMI
- const: lpass-hdmiif
- const: lpass-lpaif
interrupt-names:
anyOf:
- items: #for I2S
- const: lpass-irq-lpaif
- items: #for I2S and HDMI
- const: lpass-irq-lpaif
- const: lpass-irq-hdmi
required: required:
- iommus - iommus
- power-domains - power-domains
...@@ -152,12 +177,15 @@ examples: ...@@ -152,12 +177,15 @@ examples:
soc { soc {
#address-cells = <2>; #address-cells = <2>;
#size-cells = <2>; #size-cells = <2>;
lpass@62f00000 { lpass@62d80000 {
compatible = "qcom,sc7180-lpass-cpu"; compatible = "qcom,sc7180-lpass-cpu";
reg = <0 0x62f00000 0 0x29000>; reg = <0 0x62d87000 0 0x68000>,
<0 0x62f00000 0 0x29000>;
iommus = <&apps_smmu 0x1020 0>; reg-names = "lpass-hdmiif",
"lpass-lpaif";
iommus = <&apps_smmu 0x1020 0>,
<&apps_smmu 0x1032 0>;
power-domains = <&lpass_hm 0>; power-domains = <&lpass_hm 0>;
clocks = <&gcc 131>, clocks = <&gcc 131>,
...@@ -171,14 +199,16 @@ examples: ...@@ -171,14 +199,16 @@ examples:
"mclk0", "pcnoc-mport-clk", "mclk0", "pcnoc-mport-clk",
"mi2s-bit-clk0", "mi2s-bit-clk1"; "mi2s-bit-clk0", "mi2s-bit-clk1";
interrupts = <0 160 1>; interrupts = <0 160 1>,
<0 268 1>;
interrupt-names = "lpass-irq-lpaif",
"lpass-irq-hdmi";
#sound-dai-cells = <1>; #sound-dai-cells = <1>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
/* Optional to set different MI2S SD lines */ /* Optional to set different MI2S SD lines */
mi2s-primary@0 { dai-link@0 {
reg = <MI2S_PRIMARY>; reg = <MI2S_PRIMARY>;
qcom,playback-sd-lines = <1>; qcom,playback-sd-lines = <1>;
qcom,capture-sd-lines = <0>; qcom,capture-sd-lines = <0>;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#define MI2S_PRIMARY 0 #define MI2S_PRIMARY 0
#define MI2S_SECONDARY 1 #define MI2S_SECONDARY 1
#define LPASS_DP_RX 2
#define LPASS_MCLK0 0 #define LPASS_MCLK0 0
......
...@@ -12,6 +12,10 @@ config SND_SOC_LPASS_CPU ...@@ -12,6 +12,10 @@ config SND_SOC_LPASS_CPU
tristate tristate
select REGMAP_MMIO select REGMAP_MMIO
config SND_SOC_LPASS_HDMI
tristate
select REGMAP_MMIO
config SND_SOC_LPASS_PLATFORM config SND_SOC_LPASS_PLATFORM
tristate tristate
select REGMAP_MMIO select REGMAP_MMIO
...@@ -30,6 +34,7 @@ config SND_SOC_LPASS_SC7180 ...@@ -30,6 +34,7 @@ config SND_SOC_LPASS_SC7180
tristate tristate
select SND_SOC_LPASS_CPU select SND_SOC_LPASS_CPU
select SND_SOC_LPASS_PLATFORM select SND_SOC_LPASS_PLATFORM
select SND_SOC_LPASS_HDMI
config SND_SOC_STORM config SND_SOC_STORM
tristate "ASoC I2S support for Storm boards" tristate "ASoC I2S support for Storm boards"
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
# Platform # Platform
snd-soc-lpass-cpu-objs := lpass-cpu.o snd-soc-lpass-cpu-objs := lpass-cpu.o
snd-soc-lpass-hdmi-objs := lpass-hdmi.o
snd-soc-lpass-platform-objs := lpass-platform.o snd-soc-lpass-platform-objs := lpass-platform.o
snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
snd-soc-lpass-apq8016-objs := lpass-apq8016.o snd-soc-lpass-apq8016-objs := lpass-apq8016.o
snd-soc-lpass-sc7180-objs := lpass-sc7180.o snd-soc-lpass-sc7180-objs := lpass-sc7180.o
obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
obj-$(CONFIG_SND_SOC_LPASS_HDMI) += snd-soc-lpass-hdmi.o
obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
......
...@@ -125,7 +125,7 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { ...@@ -125,7 +125,7 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
}; };
static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,
int direction) int direction, unsigned int dai_id)
{ {
struct lpass_variant *v = drvdata->variant; struct lpass_variant *v = drvdata->variant;
int chan = 0; int chan = 0;
...@@ -151,7 +151,7 @@ static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, ...@@ -151,7 +151,7 @@ static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,
return chan; return chan;
} }
static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
{ {
clear_bit(chan, &drvdata->dma_ch_bit_map); clear_bit(chan, &drvdata->dma_ch_bit_map);
......
...@@ -478,6 +478,206 @@ static struct regmap_config lpass_cpu_regmap_config = { ...@@ -478,6 +478,206 @@ static struct regmap_config lpass_cpu_regmap_config = {
.cache_type = REGCACHE_FLAT, .cache_type = REGCACHE_FLAT,
}; };
static int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
unsigned int i;
struct lpass_hdmi_tx_ctl *tx_ctl;
struct regmap_field *legacy_en;
struct lpass_vbit_ctrl *vbit_ctl;
struct regmap_field *tx_parity;
struct lpass_dp_metadata_ctl *meta_ctl;
struct lpass_sstream_ctl *sstream_ctl;
struct regmap_field *ch_msb;
struct regmap_field *ch_lsb;
struct lpass_hdmitx_dmactl *tx_dmactl;
int rval;
tx_ctl = devm_kzalloc(dev, sizeof(*tx_ctl), GFP_KERNEL);
if (!tx_ctl)
return -ENOMEM;
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->soft_reset, tx_ctl->soft_reset);
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->force_reset, tx_ctl->force_reset);
drvdata->tx_ctl = tx_ctl;
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->legacy_en, legacy_en);
drvdata->hdmitx_legacy_en = legacy_en;
vbit_ctl = devm_kzalloc(dev, sizeof(*vbit_ctl), GFP_KERNEL);
if (!vbit_ctl)
return -ENOMEM;
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->replace_vbit, vbit_ctl->replace_vbit);
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->vbit_stream, vbit_ctl->vbit_stream);
drvdata->vbit_ctl = vbit_ctl;
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->calc_en, tx_parity);
drvdata->hdmitx_parity_calc_en = tx_parity;
meta_ctl = devm_kzalloc(dev, sizeof(*meta_ctl), GFP_KERNEL);
if (!meta_ctl)
return -ENOMEM;
rval = devm_regmap_field_bulk_alloc(dev, map, &meta_ctl->mute, &v->mute, 7);
if (rval)
return rval;
drvdata->meta_ctl = meta_ctl;
sstream_ctl = devm_kzalloc(dev, sizeof(*sstream_ctl), GFP_KERNEL);
if (!sstream_ctl)
return -ENOMEM;
rval = devm_regmap_field_bulk_alloc(dev, map, &sstream_ctl->sstream_en, &v->sstream_en, 9);
if (rval)
return rval;
drvdata->sstream_ctl = sstream_ctl;
for (i = 0; i < LPASS_MAX_HDMI_DMA_CHANNELS; i++) {
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->msb_bits, ch_msb);
drvdata->hdmitx_ch_msb[i] = ch_msb;
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->lsb_bits, ch_lsb);
drvdata->hdmitx_ch_lsb[i] = ch_lsb;
tx_dmactl = devm_kzalloc(dev, sizeof(*tx_dmactl), GFP_KERNEL);
if (!tx_dmactl)
return -ENOMEM;
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_chs, tx_dmactl->use_hw_chs);
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_usr, tx_dmactl->use_hw_usr);
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_chs_sel, tx_dmactl->hw_chs_sel);
QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_usr_sel, tx_dmactl->hw_usr_sel);
drvdata->hdmi_tx_dmactl[i] = tx_dmactl;
}
return 0;
}
static bool lpass_hdmi_regmap_writeable(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
int i;
if (reg == LPASS_HDMI_TX_CTL_ADDR(v))
return true;
if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v))
return true;
if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v))
return true;
if (reg == LPASS_HDMI_TX_PARITY_ADDR(v))
return true;
if (reg == LPASS_HDMI_TX_DP_ADDR(v))
return true;
if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v))
return true;
if (reg == LPASS_HDMITX_APP_IRQEN_REG(v))
return true;
if (reg == LPASS_HDMITX_APP_IRQCLEAR_REG(v))
return true;
for (i = 0; i < v->hdmi_rdma_channels; i++) {
if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i))
return true;
if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i))
return true;
if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i))
return true;
}
for (i = 0; i < v->rdma_channels; ++i) {
if (reg == LPAIF_HDMI_RDMACTL_REG(v, i))
return true;
if (reg == LPAIF_HDMI_RDMABASE_REG(v, i))
return true;
if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i))
return true;
if (reg == LPAIF_HDMI_RDMAPER_REG(v, i))
return true;
}
return false;
}
static bool lpass_hdmi_regmap_readable(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
int i;
if (reg == LPASS_HDMI_TX_CTL_ADDR(v))
return true;
if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v))
return true;
if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v))
return true;
for (i = 0; i < v->hdmi_rdma_channels; i++) {
if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i))
return true;
if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i))
return true;
if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i))
return true;
}
if (reg == LPASS_HDMI_TX_PARITY_ADDR(v))
return true;
if (reg == LPASS_HDMI_TX_DP_ADDR(v))
return true;
if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v))
return true;
if (reg == LPASS_HDMITX_APP_IRQEN_REG(v))
return true;
if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v))
return true;
for (i = 0; i < v->rdma_channels; ++i) {
if (reg == LPAIF_HDMI_RDMACTL_REG(v, i))
return true;
if (reg == LPAIF_HDMI_RDMABASE_REG(v, i))
return true;
if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i))
return true;
if (reg == LPAIF_HDMI_RDMAPER_REG(v, i))
return true;
if (reg == LPAIF_HDMI_RDMACURR_REG(v, i))
return true;
}
return false;
}
static bool lpass_hdmi_regmap_volatile(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
int i;
if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v))
return true;
if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v))
return true;
for (i = 0; i < v->rdma_channels; ++i) {
if (reg == LPAIF_HDMI_RDMACURR_REG(v, i))
return true;
}
return false;
}
struct regmap_config lpass_hdmi_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.writeable_reg = lpass_hdmi_regmap_writeable,
.readable_reg = lpass_hdmi_regmap_readable,
.volatile_reg = lpass_hdmi_regmap_volatile,
.cache_type = REGCACHE_FLAT,
};
static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev, static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev,
struct device_node *node, struct device_node *node,
const char *name) const char *name)
...@@ -535,13 +735,17 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev, ...@@ -535,13 +735,17 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
dev_err(dev, "valid dai id not found: %d\n", ret); dev_err(dev, "valid dai id not found: %d\n", ret);
continue; continue;
} }
if (id == LPASS_DP_RX) {
data->mi2s_playback_sd_mode[id] = data->hdmi_port_enable = 1;
of_lpass_cpu_parse_sd_lines(dev, node, dev_err(dev, "HDMI Port is enabled: %d\n", id);
"qcom,playback-sd-lines"); } else {
data->mi2s_capture_sd_mode[id] = data->mi2s_playback_sd_mode[id] =
of_lpass_cpu_parse_sd_lines(dev, node, of_lpass_cpu_parse_sd_lines(dev, node,
"qcom,playback-sd-lines");
data->mi2s_capture_sd_mode[id] =
of_lpass_cpu_parse_sd_lines(dev, node,
"qcom,capture-sd-lines"); "qcom,capture-sd-lines");
}
} }
} }
...@@ -575,7 +779,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) ...@@ -575,7 +779,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
of_lpass_cpu_parse_dai_data(dev, drvdata); of_lpass_cpu_parse_dai_data(dev, drvdata);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
drvdata->lpaif = devm_ioremap_resource(dev, res); drvdata->lpaif = devm_ioremap_resource(dev, res);
if (IS_ERR((void const __force *)drvdata->lpaif)) { if (IS_ERR((void const __force *)drvdata->lpaif)) {
...@@ -596,6 +800,27 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) ...@@ -596,6 +800,27 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
return PTR_ERR(drvdata->lpaif_map); return PTR_ERR(drvdata->lpaif_map);
} }
if (drvdata->hdmi_port_enable) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-hdmiif");
drvdata->hdmiif = devm_ioremap_resource(dev, res);
if (IS_ERR((void const __force *)drvdata->hdmiif)) {
dev_err(dev, "error mapping reg resource: %ld\n",
PTR_ERR((void const __force *)drvdata->hdmiif));
return PTR_ERR((void const __force *)drvdata->hdmiif);
}
lpass_hdmi_regmap_config.max_register = LPAIF_HDMI_RDMAPER_REG(variant,
variant->hdmi_rdma_channels);
drvdata->hdmiif_map = devm_regmap_init_mmio(dev, drvdata->hdmiif,
&lpass_hdmi_regmap_config);
if (IS_ERR(drvdata->hdmiif_map)) {
dev_err(dev, "error initializing regmap: %ld\n",
PTR_ERR(drvdata->hdmiif_map));
return PTR_ERR(drvdata->hdmiif_map);
}
}
if (variant->init) { if (variant->init) {
ret = variant->init(pdev); ret = variant->init(pdev);
if (ret) { if (ret) {
...@@ -606,6 +831,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) ...@@ -606,6 +831,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
for (i = 0; i < variant->num_dai; i++) { for (i = 0; i < variant->num_dai; i++) {
dai_id = variant->dai_driver[i].id; dai_id = variant->dai_driver[i].id;
if (dai_id == LPASS_DP_RX)
continue;
drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(dev, drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(dev,
variant->dai_osr_clk_names[i]); variant->dai_osr_clk_names[i]);
if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) { if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
...@@ -641,6 +869,13 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) ...@@ -641,6 +869,13 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
return ret; return ret;
} }
if (drvdata->hdmi_port_enable) {
ret = lpass_hdmi_init_bitfields(dev, drvdata->hdmiif_map);
if (ret) {
dev_err(dev, "%s error hdmi init failed\n", __func__);
return ret;
}
}
ret = devm_snd_soc_register_component(dev, ret = devm_snd_soc_register_component(dev,
&lpass_cpu_comp_driver, &lpass_cpu_comp_driver,
variant->dai_driver, variant->dai_driver,
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
*
* lpass-hdmi.c -- ALSA SoC HDMI-CPU DAI driver for QTi LPASS HDMI
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <sound/pcm_params.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <dt-bindings/sound/sc7180-lpass.h>
#include "lpass-lpaif-reg.h"
#include "lpass.h"
static int lpass_hdmi_daiops_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
snd_pcm_format_t format = params_format(params);
unsigned int rate = params_rate(params);
unsigned int channels = params_channels(params);
unsigned int ret;
unsigned int bitwidth;
unsigned int word_length;
unsigned int ch_sts_buf0;
unsigned int ch_sts_buf1;
unsigned int data_format;
unsigned int sampling_freq;
unsigned int ch = 0;
struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl;
struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl;
bitwidth = snd_pcm_format_width(format);
if (bitwidth < 0) {
dev_err(dai->dev, "%s invalid bit width given : %d\n",
__func__, bitwidth);
return bitwidth;
}
switch (bitwidth) {
case 16:
word_length = LPASS_DP_AUDIO_BITWIDTH16;
break;
case 24:
word_length = LPASS_DP_AUDIO_BITWIDTH24;
break;
default:
dev_err(dai->dev, "%s invalid bit width given : %d\n",
__func__, bitwidth);
return -EINVAL;
}
switch (rate) {
case 32000:
sampling_freq = LPASS_SAMPLING_FREQ32;
break;
case 44100:
sampling_freq = LPASS_SAMPLING_FREQ44;
break;
case 48000:
sampling_freq = LPASS_SAMPLING_FREQ48;
break;
default:
dev_err(dai->dev, "%s invalid bit width given : %d\n",
__func__, bitwidth);
return -EINVAL;
}
data_format = LPASS_DATA_FORMAT_LINEAR;
ch_sts_buf0 = (((data_format << LPASS_DATA_FORMAT_SHIFT) & LPASS_DATA_FORMAT_MASK)
| ((sampling_freq << LPASS_FREQ_BIT_SHIFT) & LPASS_FREQ_BIT_MASK));
ch_sts_buf1 = (word_length) & LPASS_WORDLENGTH_MASK;
ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_RESET);
if (ret)
return ret;
ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_CLEAR);
if (ret)
return ret;
ret = regmap_field_write(drvdata->hdmitx_legacy_en, LPASS_HDMITX_LEGACY_DISABLE);
if (ret)
return ret;
ret = regmap_field_write(drvdata->hdmitx_parity_calc_en, HDMITX_PARITY_CALC_EN);
if (ret)
return ret;
ret = regmap_field_write(drvdata->vbit_ctl->replace_vbit, REPLACE_VBIT);
if (ret)
return ret;
ret = regmap_field_write(drvdata->vbit_ctl->vbit_stream, LINEAR_PCM_DATA);
if (ret)
return ret;
ret = regmap_field_write(drvdata->hdmitx_ch_msb[0], ch_sts_buf1);
if (ret)
return ret;
ret = regmap_field_write(drvdata->hdmitx_ch_lsb[0], ch_sts_buf0);
if (ret)
return ret;
ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_chs, HW_MODE);
if (ret)
return ret;
ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_chs_sel, SW_MODE);
if (ret)
return ret;
ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_usr, HW_MODE);
if (ret)
return ret;
ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_usr_sel, SW_MODE);
if (ret)
return ret;
ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_ENABLE);
if (ret)
return ret;
ret = regmap_field_write(meta_ctl->as_sdp_cc, channels - 1);
if (ret)
return ret;
ret = regmap_field_write(meta_ctl->as_sdp_ct, LPASS_META_DEFAULT_VAL);
if (ret)
return ret;
ret = regmap_field_write(meta_ctl->aif_db4, LPASS_META_DEFAULT_VAL);
if (ret)
return ret;
ret = regmap_field_write(meta_ctl->frequency, sampling_freq);
if (ret)
return ret;
ret = regmap_field_write(meta_ctl->mst_index, LPASS_META_DEFAULT_VAL);
if (ret)
return ret;
ret = regmap_field_write(meta_ctl->dptx_index, LPASS_META_DEFAULT_VAL);
if (ret)
return ret;
ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_DISABLE);
if (ret)
return ret;
ret = regmap_field_write(sstream_ctl->dma_sel, ch);
if (ret)
return ret;
ret = regmap_field_write(sstream_ctl->auto_bbit_en, LPASS_SSTREAM_DEFAULT_ENABLE);
if (ret)
return ret;
ret = regmap_field_write(sstream_ctl->layout, LPASS_SSTREAM_DEFAULT_DISABLE);
if (ret)
return ret;
ret = regmap_field_write(sstream_ctl->layout_sp, LPASS_LAYOUT_SP_DEFAULT);
if (ret)
return ret;
ret = regmap_field_write(sstream_ctl->dp_audio, LPASS_SSTREAM_DEFAULT_ENABLE);
if (ret)
return ret;
ret = regmap_field_write(sstream_ctl->set_sp_on_en, LPASS_SSTREAM_DEFAULT_ENABLE);
if (ret)
return ret;
ret = regmap_field_write(sstream_ctl->dp_sp_b_hw_en, LPASS_SSTREAM_DEFAULT_ENABLE);
if (ret)
return ret;
ret = regmap_field_write(sstream_ctl->dp_staffing_en, LPASS_SSTREAM_DEFAULT_ENABLE);
if (ret)
return ret;
return ret;
}
static int lpass_hdmi_daiops_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret;
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
ret = regmap_field_write(drvdata->sstream_ctl->sstream_en, LPASS_SSTREAM_ENABLE);
if (ret)
return ret;
ret = regmap_field_write(drvdata->meta_ctl->mute, LPASS_MUTE_DISABLE);
if (ret)
return ret;
return ret;
}
static int lpass_hdmi_daiops_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl;
struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl;
int ret = -EINVAL;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_ENABLE);
if (ret)
return ret;
ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_DISABLE);
if (ret)
return ret;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_DISABLE);
if (ret)
return ret;
ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_ENABLE);
if (ret)
return ret;
ret = regmap_field_write(sstream_ctl->dp_audio, 0);
if (ret)
return ret;
break;
}
return ret;
}
const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops = {
.hw_params = lpass_hdmi_daiops_hw_params,
.prepare = lpass_hdmi_daiops_prepare,
.trigger = lpass_hdmi_daiops_trigger,
};
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_hdmi_dai_ops);
MODULE_DESCRIPTION("QTi LPASS HDMI Driver");
MODULE_LICENSE("GPL v2");
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
*
* lpass_hdmi.h - Definitions for the QTi LPASS HDMI
*/
#ifndef __LPASS_HDMI_H__
#define __LPASS_HDMI_H__
#include <linux/regmap.h>
#define LPASS_HDMITX_LEGACY_DISABLE 0x0
#define LPASS_HDMITX_LEGACY_ENABLE 0x1
#define LPASS_DP_AUDIO_BITWIDTH16 0x0
#define LPASS_DP_AUDIO_BITWIDTH24 0xb
#define LPASS_DATA_FORMAT_SHIFT 0x1
#define LPASS_FREQ_BIT_SHIFT 24
#define LPASS_DATA_FORMAT_LINEAR 0x0
#define LPASS_DATA_FORMAT_NON_LINEAR 0x1
#define LPASS_SAMPLING_FREQ32 0x3
#define LPASS_SAMPLING_FREQ44 0x0
#define LPASS_SAMPLING_FREQ48 0x2
#define LPASS_TX_CTL_RESET 0x1
#define LPASS_TX_CTL_CLEAR 0x0
#define LPASS_SSTREAM_ENABLE 1
#define LPASS_SSTREAM_DISABLE 0
#define LPASS_LAYOUT_SP_DEFAULT 0xf
#define LPASS_SSTREAM_DEFAULT_ENABLE 1
#define LPASS_SSTREAM_DEFAULT_DISABLE 0
#define LPASS_MUTE_ENABLE 1
#define LPASS_MUTE_DISABLE 0
#define LPASS_META_DEFAULT_VAL 0
#define HW_MODE 1
#define SW_MODE 0
#define LEGACY_LPASS_LPAIF 1
#define LEGACY_LPASS_HDMI 0
#define REPLACE_VBIT 0x1
#define LINEAR_PCM_DATA 0x0
#define NON_LINEAR_PCM_DATA 0x1
#define HDMITX_PARITY_CALC_EN 0x1
#define HDMITX_PARITY_CALC_DIS 0x0
#define LPASS_DATA_FORMAT_MASK GENMASK(1, 1)
#define LPASS_WORDLENGTH_MASK GENMASK(3, 0)
#define LPASS_FREQ_BIT_MASK GENMASK(27, 24)
#define LPASS_HDMI_TX_CTL_ADDR(v) (v->hdmi_tx_ctl_addr)
#define LPASS_HDMI_TX_LEGACY_ADDR(v) (v->hdmi_legacy_addr)
#define LPASS_HDMI_TX_VBIT_CTL_ADDR(v) (v->hdmi_vbit_addr)
#define LPASS_HDMI_TX_PARITY_ADDR(v) (v->hdmi_parity_addr)
#define LPASS_HDMI_TX_DP_ADDR(v) (v->hdmi_DP_addr)
#define LPASS_HDMI_TX_SSTREAM_ADDR(v) (v->hdmi_sstream_addr)
#define LPASS_HDMI_TX_CH_LSB_ADDR(v, port) \
(v->hdmi_ch_lsb_addr + v->ch_stride * (port))
#define LPASS_HDMI_TX_CH_MSB_ADDR(v, port) \
(v->hdmi_ch_msb_addr + v->ch_stride * (port))
#define LPASS_HDMI_TX_DMA_ADDR(v, port) \
(v->hdmi_dmactl_addr + v->hdmi_dma_stride * (port))
struct lpass_sstream_ctl {
struct regmap_field *sstream_en;
struct regmap_field *dma_sel;
struct regmap_field *auto_bbit_en;
struct regmap_field *layout;
struct regmap_field *layout_sp;
struct regmap_field *set_sp_on_en;
struct regmap_field *dp_audio;
struct regmap_field *dp_staffing_en;
struct regmap_field *dp_sp_b_hw_en;
};
struct lpass_dp_metadata_ctl {
struct regmap_field *mute;
struct regmap_field *as_sdp_cc;
struct regmap_field *as_sdp_ct;
struct regmap_field *aif_db4;
struct regmap_field *frequency;
struct regmap_field *mst_index;
struct regmap_field *dptx_index;
};
struct lpass_hdmi_tx_ctl {
struct regmap_field *soft_reset;
struct regmap_field *force_reset;
};
struct lpass_hdmitx_dmactl {
struct regmap_field *use_hw_chs;
struct regmap_field *use_hw_usr;
struct regmap_field *hw_chs_sel;
struct regmap_field *hw_usr_sel;
};
struct lpass_vbit_ctrl {
struct regmap_field *replace_vbit;
struct regmap_field *vbit_stream;
};
extern const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops;
#endif /* __LPASS_HDMI_H__ */
...@@ -96,7 +96,7 @@ static int ipq806x_lpass_exit(struct platform_device *pdev) ...@@ -96,7 +96,7 @@ static int ipq806x_lpass_exit(struct platform_device *pdev)
return 0; return 0;
} }
static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir, unsigned int dai_id)
{ {
if (dir == SNDRV_PCM_STREAM_PLAYBACK) if (dir == SNDRV_PCM_STREAM_PLAYBACK)
return IPQ806X_LPAIF_RDMA_CHAN_MI2S; return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
...@@ -104,7 +104,7 @@ static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) ...@@ -104,7 +104,7 @@ static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir)
return -EINVAL; return -EINVAL;
} }
static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
{ {
return 0; return 0;
} }
......
...@@ -70,6 +70,14 @@ ...@@ -70,6 +70,14 @@
#define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port)) #define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port))
#define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port)) #define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port))
#define LPASS_HDMITX_APP_IRQ_REG_ADDR(v, addr) \
((v->hdmi_irq_reg_base) + (addr))
#define LPASS_HDMITX_APP_IRQEN_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0x4)
#define LPASS_HDMITX_APP_IRQSTAT_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0x8)
#define LPASS_HDMITX_APP_IRQCLEAR_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0xC)
#define LPAIF_IRQ_BITSTRIDE 3 #define LPAIF_IRQ_BITSTRIDE 3
#define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan))) #define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
...@@ -77,8 +85,22 @@ ...@@ -77,8 +85,22 @@
#define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan))) #define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
#define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan))) #define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
#define LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) (1 << (14 + chan))
#define LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan) (1 << (24 + chan))
#define LPAIF_IRQ_HDMI_METADONE BIT(23)
/* LPAIF DMA */ /* LPAIF DMA */
#define LPAIF_HDMI_RDMA_REG_ADDR(v, addr, chan) \
(v->hdmi_rdma_reg_base + (addr) + v->hdmi_rdma_reg_stride * (chan))
#define LPAIF_HDMI_RDMACTL_AUDINTF(id) (id << LPAIF_RDMACTL_AUDINTF_SHIFT)
#define LPAIF_HDMI_RDMACTL_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x00, (chan))
#define LPAIF_HDMI_RDMABASE_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x04, (chan))
#define LPAIF_HDMI_RDMABUFF_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x08, (chan))
#define LPAIF_HDMI_RDMACURR_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x0C, (chan))
#define LPAIF_HDMI_RDMAPER_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x10, (chan))
#define LPAIF_HDMI_RDMAPERCNT_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x14, (chan))
#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \ #define LPAIF_RDMA_REG_ADDR(v, addr, chan) \
(v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan)) (v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
...@@ -103,17 +125,22 @@ ...@@ -103,17 +125,22 @@
#define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan)) #define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan))
#define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan)) #define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan))
#define __LPAIF_DMA_REG(v, chan, dir, reg) \ #define LPAIF_INTFDMA_REG(v, chan, reg, dai_id) \
(dir == SNDRV_PCM_STREAM_PLAYBACK) ? \ ((v->dai_driver[dai_id].id == LPASS_DP_RX) ? \
LPAIF_RDMA##reg##_REG(v, chan) : \ LPAIF_HDMI_RDMA##reg##_REG(v, chan) : \
LPAIF_WRDMA##reg##_REG(v, chan) LPAIF_RDMA##reg##_REG(v, chan))
#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL) #define __LPAIF_DMA_REG(v, chan, dir, reg, dai_id) \
#define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE) ((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \
#define LPAIF_DMABUFF_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BUFF) (LPAIF_INTFDMA_REG(v, chan, reg, dai_id)) : \
#define LPAIF_DMACURR_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CURR) LPAIF_WRDMA##reg##_REG(v, chan))
#define LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER)
#define LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT) #define LPAIF_DMACTL_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, CTL, dai_id)
#define LPAIF_DMABASE_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, BASE, dai_id)
#define LPAIF_DMABUFF_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, BUFF, dai_id)
#define LPAIF_DMACURR_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, CURR, dai_id)
#define LPAIF_DMAPER_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, PER, dai_id)
#define LPAIF_DMAPERCNT_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, PERCNT, dai_id)
#define LPAIF_DMACTL_BURSTEN_SINGLE 0 #define LPAIF_DMACTL_BURSTEN_SINGLE 0
#define LPAIF_DMACTL_BURSTEN_INCR4 1 #define LPAIF_DMACTL_BURSTEN_INCR4 1
......
This diff is collapsed.
...@@ -60,38 +60,65 @@ static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = { ...@@ -60,38 +60,65 @@ static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = {
.probe = &asoc_qcom_lpass_cpu_dai_probe, .probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops, .ops = &asoc_qcom_lpass_cpu_dai_ops,
}, },
[LPASS_DP_RX] = {
.id = LPASS_DP_RX,
.name = "Hdmi",
.playback = {
.stream_name = "Hdmi Playback",
.formats = SNDRV_PCM_FMTBIT_S24,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &asoc_qcom_lpass_hdmi_dai_ops,
},
}; };
static int sc7180_lpass_alloc_dma_channel(struct lpass_data *drvdata, static int sc7180_lpass_alloc_dma_channel(struct lpass_data *drvdata,
int direction) int direction, unsigned int dai_id)
{ {
struct lpass_variant *v = drvdata->variant; struct lpass_variant *v = drvdata->variant;
int chan = 0; int chan = 0;
if (direction == SNDRV_PCM_STREAM_PLAYBACK) { if (dai_id == LPASS_DP_RX) {
chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
v->rdma_channels); chan = find_first_zero_bit(&drvdata->hdmi_dma_ch_bit_map,
v->hdmi_rdma_channels);
if (chan >= v->hdmi_rdma_channels)
return -EBUSY;
}
set_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
} else {
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
v->rdma_channels);
if (chan >= v->rdma_channels) if (chan >= v->rdma_channels)
return -EBUSY; return -EBUSY;
} else { } else {
chan = find_next_zero_bit(&drvdata->dma_ch_bit_map, chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
v->wrdma_channel_start + v->wrdma_channel_start +
v->wrdma_channels, v->wrdma_channels,
v->wrdma_channel_start); v->wrdma_channel_start);
if (chan >= v->wrdma_channel_start + v->wrdma_channels) if (chan >= v->wrdma_channel_start + v->wrdma_channels)
return -EBUSY; return -EBUSY;
} }
set_bit(chan, &drvdata->dma_ch_bit_map);
set_bit(chan, &drvdata->dma_ch_bit_map);
}
return chan; return chan;
} }
static int sc7180_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) static int sc7180_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
{ {
clear_bit(chan, &drvdata->dma_ch_bit_map); if (dai_id == LPASS_DP_RX)
clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
else
clear_bit(chan, &drvdata->dma_ch_bit_map);
return 0; return 0;
} }
...@@ -144,6 +171,9 @@ static struct lpass_variant sc7180_data = { ...@@ -144,6 +171,9 @@ static struct lpass_variant sc7180_data = {
.rdma_reg_base = 0xC000, .rdma_reg_base = 0xC000,
.rdma_reg_stride = 0x1000, .rdma_reg_stride = 0x1000,
.rdma_channels = 5, .rdma_channels = 5,
.hdmi_rdma_reg_base = 0x64000,
.hdmi_rdma_reg_stride = 0x1000,
.hdmi_rdma_channels = 4,
.dmactl_audif_start = 1, .dmactl_audif_start = 1,
.wrdma_reg_base = 0x18000, .wrdma_reg_base = 0x18000,
.wrdma_reg_stride = 0x1000, .wrdma_reg_stride = 0x1000,
...@@ -163,7 +193,7 @@ static struct lpass_variant sc7180_data = { ...@@ -163,7 +193,7 @@ static struct lpass_variant sc7180_data = {
.rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000), .rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000),
.rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000), .rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000),
.rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000), .rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000),
.rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000), .rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000),
.rdma_fifowm = REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000), .rdma_fifowm = REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000),
.rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000), .rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000),
...@@ -174,6 +204,64 @@ static struct lpass_variant sc7180_data = { ...@@ -174,6 +204,64 @@ static struct lpass_variant sc7180_data = {
.wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 5, 4, 0x1000), .wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 5, 4, 0x1000),
.wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 4, 0x1000), .wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 4, 0x1000),
.hdmi_tx_ctl_addr = 0x1000,
.hdmi_legacy_addr = 0x1008,
.hdmi_vbit_addr = 0x610c0,
.hdmi_ch_lsb_addr = 0x61048,
.hdmi_ch_msb_addr = 0x6104c,
.ch_stride = 0x8,
.hdmi_parity_addr = 0x61034,
.hdmi_dmactl_addr = 0x61038,
.hdmi_dma_stride = 0x4,
.hdmi_DP_addr = 0x610c8,
.hdmi_sstream_addr = 0x6101c,
.hdmi_irq_reg_base = 0x63000,
.hdmi_irq_ports = 1,
.hdmi_rdma_dyncclk = REG_FIELD_ID(0x64000, 14, 14, 4, 0x1000),
.hdmi_rdma_bursten = REG_FIELD_ID(0x64000, 13, 13, 4, 0x1000),
.hdmi_rdma_burst8 = REG_FIELD_ID(0x64000, 15, 15, 4, 0x1000),
.hdmi_rdma_burst16 = REG_FIELD_ID(0x64000, 16, 16, 4, 0x1000),
.hdmi_rdma_dynburst = REG_FIELD_ID(0x64000, 18, 18, 4, 0x1000),
.hdmi_rdma_wpscnt = REG_FIELD_ID(0x64000, 10, 12, 4, 0x1000),
.hdmi_rdma_fifowm = REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000),
.hdmi_rdma_enable = REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000),
.sstream_en = REG_FIELD(0x6101c, 0, 0),
.dma_sel = REG_FIELD(0x6101c, 1, 2),
.auto_bbit_en = REG_FIELD(0x6101c, 3, 3),
.layout = REG_FIELD(0x6101c, 4, 4),
.layout_sp = REG_FIELD(0x6101c, 5, 8),
.set_sp_on_en = REG_FIELD(0x6101c, 10, 10),
.dp_audio = REG_FIELD(0x6101c, 11, 11),
.dp_staffing_en = REG_FIELD(0x6101c, 12, 12),
.dp_sp_b_hw_en = REG_FIELD(0x6101c, 13, 13),
.mute = REG_FIELD(0x610c8, 0, 0),
.as_sdp_cc = REG_FIELD(0x610c8, 1, 3),
.as_sdp_ct = REG_FIELD(0x610c8, 4, 7),
.aif_db4 = REG_FIELD(0x610c8, 8, 15),
.frequency = REG_FIELD(0x610c8, 16, 21),
.mst_index = REG_FIELD(0x610c8, 28, 29),
.dptx_index = REG_FIELD(0x610c8, 30, 31),
.soft_reset = REG_FIELD(0x1000, 31, 31),
.force_reset = REG_FIELD(0x1000, 30, 30),
.use_hw_chs = REG_FIELD(0x61038, 0, 0),
.use_hw_usr = REG_FIELD(0x61038, 1, 1),
.hw_chs_sel = REG_FIELD(0x61038, 2, 4),
.hw_usr_sel = REG_FIELD(0x61038, 5, 6),
.replace_vbit = REG_FIELD(0x610c0, 0, 0),
.vbit_stream = REG_FIELD(0x610c0, 1, 1),
.legacy_en = REG_FIELD(0x1008, 0, 0),
.calc_en = REG_FIELD(0x61034, 0, 0),
.lsb_bits = REG_FIELD(0x61048, 0, 31),
.msb_bits = REG_FIELD(0x6104c, 0, 31),
.clk_name = (const char*[]) { .clk_name = (const char*[]) {
"pcnoc-sway-clk", "pcnoc-sway-clk",
"audio-core", "audio-core",
......
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. * Copyright (c) 2010-2011,2013-2015,2020 The Linux Foundation. All rights reserved.
* *
* lpass.h - Definitions for the QTi LPASS * lpass.h - Definitions for the QTi LPASS
*/ */
...@@ -12,10 +12,20 @@ ...@@ -12,10 +12,20 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <dt-bindings/sound/sc7180-lpass.h>
#include "lpass-hdmi.h"
#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 #define LPASS_AHBIX_CLOCK_FREQUENCY 131072000
#define LPASS_MAX_MI2S_PORTS (8) #define LPASS_MAX_MI2S_PORTS (8)
#define LPASS_MAX_DMA_CHANNELS (8) #define LPASS_MAX_DMA_CHANNELS (8)
#define LPASS_MAX_HDMI_DMA_CHANNELS (4)
#define QCOM_REGMAP_FIELD_ALLOC(d, m, f, mf) \
do { \
mf = devm_regmap_field_alloc(d, m, f); \
if (IS_ERR(mf)) \
return -EINVAL; \
} while (0)
struct lpaif_i2sctl { struct lpaif_i2sctl {
struct regmap_field *loopback; struct regmap_field *loopback;
...@@ -31,12 +41,15 @@ struct lpaif_i2sctl { ...@@ -31,12 +41,15 @@ struct lpaif_i2sctl {
struct lpaif_dmactl { struct lpaif_dmactl {
struct regmap_field *intf;
struct regmap_field *bursten; struct regmap_field *bursten;
struct regmap_field *wpscnt; struct regmap_field *wpscnt;
struct regmap_field *intf;
struct regmap_field *fifowm; struct regmap_field *fifowm;
struct regmap_field *enable; struct regmap_field *enable;
struct regmap_field *dyncclk; struct regmap_field *dyncclk;
struct regmap_field *burst8;
struct regmap_field *burst16;
struct regmap_field *dynburst;
}; };
/* Both the CPU DAI and platform drivers will access this data */ /* Both the CPU DAI and platform drivers will access this data */
...@@ -54,24 +67,29 @@ struct lpass_data { ...@@ -54,24 +67,29 @@ struct lpass_data {
/* MI2S SD lines to use for playback/capture */ /* MI2S SD lines to use for playback/capture */
unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS]; unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS];
unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS]; unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS];
int hdmi_port_enable;
/* low-power audio interface (LPAIF) registers */ /* low-power audio interface (LPAIF) registers */
void __iomem *lpaif; void __iomem *lpaif;
void __iomem *hdmiif;
/* regmap backed by the low-power audio interface (LPAIF) registers */ /* regmap backed by the low-power audio interface (LPAIF) registers */
struct regmap *lpaif_map; struct regmap *lpaif_map;
struct regmap *hdmiif_map;
/* interrupts from the low-power audio interface (LPAIF) */ /* interrupts from the low-power audio interface (LPAIF) */
int lpaif_irq; int lpaif_irq;
int hdmiif_irq;
/* SOC specific variations in the LPASS IP integration */ /* SOC specific variations in the LPASS IP integration */
struct lpass_variant *variant; struct lpass_variant *variant;
/* bit map to keep track of static channel allocations */ /* bit map to keep track of static channel allocations */
unsigned long dma_ch_bit_map; unsigned long dma_ch_bit_map;
unsigned long hdmi_dma_ch_bit_map;
/* used it for handling interrupt per dma channel */ /* used it for handling interrupt per dma channel */
struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS]; struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
struct snd_pcm_substream *hdmi_substream[LPASS_MAX_HDMI_DMA_CHANNELS];
/* SOC specific clock list */ /* SOC specific clock list */
struct clk_bulk_data *clks; struct clk_bulk_data *clks;
...@@ -81,22 +99,36 @@ struct lpass_data { ...@@ -81,22 +99,36 @@ struct lpass_data {
struct lpaif_i2sctl *i2sctl; struct lpaif_i2sctl *i2sctl;
struct lpaif_dmactl *rd_dmactl; struct lpaif_dmactl *rd_dmactl;
struct lpaif_dmactl *wr_dmactl; struct lpaif_dmactl *wr_dmactl;
struct lpaif_dmactl *hdmi_rd_dmactl;
/* Regmap fields of HDMI_CTRL registers*/
struct regmap_field *hdmitx_legacy_en;
struct regmap_field *hdmitx_parity_calc_en;
struct regmap_field *hdmitx_ch_msb[LPASS_MAX_HDMI_DMA_CHANNELS];
struct regmap_field *hdmitx_ch_lsb[LPASS_MAX_HDMI_DMA_CHANNELS];
struct lpass_hdmi_tx_ctl *tx_ctl;
struct lpass_vbit_ctrl *vbit_ctl;
struct lpass_hdmitx_dmactl *hdmi_tx_dmactl[LPASS_MAX_HDMI_DMA_CHANNELS];
struct lpass_dp_metadata_ctl *meta_ctl;
struct lpass_sstream_ctl *sstream_ctl;
}; };
/* Vairant data per each SOC */ /* Vairant data per each SOC */
struct lpass_variant { struct lpass_variant {
u32 i2sctrl_reg_base;
u32 i2sctrl_reg_stride;
u32 i2s_ports;
u32 irq_reg_base; u32 irq_reg_base;
u32 irq_reg_stride; u32 irq_reg_stride;
u32 irq_ports; u32 irq_ports;
u32 rdma_reg_base; u32 rdma_reg_base;
u32 rdma_reg_stride; u32 rdma_reg_stride;
u32 rdma_channels; u32 rdma_channels;
u32 hdmi_rdma_reg_base;
u32 hdmi_rdma_reg_stride;
u32 hdmi_rdma_channels;
u32 wrdma_reg_base; u32 wrdma_reg_base;
u32 wrdma_reg_stride; u32 wrdma_reg_stride;
u32 wrdma_channels; u32 wrdma_channels;
u32 i2sctrl_reg_base;
u32 i2sctrl_reg_stride;
u32 i2s_ports;
/* I2SCTL Register fields */ /* I2SCTL Register fields */
struct reg_field loopback; struct reg_field loopback;
...@@ -109,18 +141,90 @@ struct lpass_variant { ...@@ -109,18 +141,90 @@ struct lpass_variant {
struct reg_field wssrc; struct reg_field wssrc;
struct reg_field bitwidth; struct reg_field bitwidth;
u32 hdmi_irq_reg_base;
u32 hdmi_irq_reg_stride;
u32 hdmi_irq_ports;
/* HDMI specific controls */
u32 hdmi_tx_ctl_addr;
u32 hdmi_legacy_addr;
u32 hdmi_vbit_addr;
u32 hdmi_ch_lsb_addr;
u32 hdmi_ch_msb_addr;
u32 ch_stride;
u32 hdmi_parity_addr;
u32 hdmi_dmactl_addr;
u32 hdmi_dma_stride;
u32 hdmi_DP_addr;
u32 hdmi_sstream_addr;
/* HDMI SSTREAM CTRL fields */
struct reg_field sstream_en;
struct reg_field dma_sel;
struct reg_field auto_bbit_en;
struct reg_field layout;
struct reg_field layout_sp;
struct reg_field set_sp_on_en;
struct reg_field dp_audio;
struct reg_field dp_staffing_en;
struct reg_field dp_sp_b_hw_en;
/* HDMI DP METADATA CTL fields */
struct reg_field mute;
struct reg_field as_sdp_cc;
struct reg_field as_sdp_ct;
struct reg_field aif_db4;
struct reg_field frequency;
struct reg_field mst_index;
struct reg_field dptx_index;
/* HDMI TX CTRL fields */
struct reg_field soft_reset;
struct reg_field force_reset;
/* HDMI TX DMA CTRL */
struct reg_field use_hw_chs;
struct reg_field use_hw_usr;
struct reg_field hw_chs_sel;
struct reg_field hw_usr_sel;
/* HDMI VBIT CTRL */
struct reg_field replace_vbit;
struct reg_field vbit_stream;
/* HDMI TX LEGACY */
struct reg_field legacy_en;
/* HDMI TX PARITY */
struct reg_field calc_en;
/* HDMI CH LSB */
struct reg_field lsb_bits;
/* HDMI CH MSB */
struct reg_field msb_bits;
struct reg_field hdmi_rdma_bursten;
struct reg_field hdmi_rdma_wpscnt;
struct reg_field hdmi_rdma_fifowm;
struct reg_field hdmi_rdma_enable;
struct reg_field hdmi_rdma_dyncclk;
struct reg_field hdmi_rdma_burst8;
struct reg_field hdmi_rdma_burst16;
struct reg_field hdmi_rdma_dynburst;
/* RD_DMA Register fields */ /* RD_DMA Register fields */
struct reg_field rdma_intf;
struct reg_field rdma_bursten; struct reg_field rdma_bursten;
struct reg_field rdma_wpscnt; struct reg_field rdma_wpscnt;
struct reg_field rdma_intf;
struct reg_field rdma_fifowm; struct reg_field rdma_fifowm;
struct reg_field rdma_enable; struct reg_field rdma_enable;
struct reg_field rdma_dyncclk; struct reg_field rdma_dyncclk;
/* WR_DMA Register fields */ /* WR_DMA Register fields */
struct reg_field wrdma_intf;
struct reg_field wrdma_bursten; struct reg_field wrdma_bursten;
struct reg_field wrdma_wpscnt; struct reg_field wrdma_wpscnt;
struct reg_field wrdma_intf;
struct reg_field wrdma_fifowm; struct reg_field wrdma_fifowm;
struct reg_field wrdma_enable; struct reg_field wrdma_enable;
struct reg_field wrdma_dyncclk; struct reg_field wrdma_dyncclk;
...@@ -134,8 +238,8 @@ struct lpass_variant { ...@@ -134,8 +238,8 @@ struct lpass_variant {
/* SOC specific initialization like clocks */ /* SOC specific initialization like clocks */
int (*init)(struct platform_device *pdev); int (*init)(struct platform_device *pdev);
int (*exit)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev);
int (*alloc_dma_channel)(struct lpass_data *data, int direction); int (*alloc_dma_channel)(struct lpass_data *data, int direction, unsigned int dai_id);
int (*free_dma_channel)(struct lpass_data *data, int ch); int (*free_dma_channel)(struct lpass_data *data, int ch, unsigned int dai_id);
/* SOC specific dais */ /* SOC specific dais */
struct snd_soc_dai_driver *dai_driver; struct snd_soc_dai_driver *dai_driver;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment