I2S driver support for Phytium CPUs
I2S driver support for Phytium desktop and embedded CPUs, such as E2000.
(cherry picked from commit 68c0c72315
)
Signed-off-by: Alex Shi <alexsshi@tencent.com>
This commit is contained in:
parent
c40f60149f
commit
1335f94f34
|
@ -958,6 +958,19 @@ config UCB1400_CORE
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ucb1400_core.
|
||||
|
||||
config MFD_PHYTIUM_I2S_LSD
|
||||
tristate "PHYTIUM px210 I2S LSD MFD driver"
|
||||
depends on (PCI && ARCH_PHYTIUM)
|
||||
help
|
||||
This enables support for the Phytium px210 LSD I2S controller.
|
||||
|
||||
config MFD_PHYTIUM_I2S_MMD
|
||||
tristate "PHYTIUM px210 I2S MMD MFD driver"
|
||||
depends on (PCI && ARCH_PHYTIUM)
|
||||
help
|
||||
This enables support for the Phytium px210 MMD I2S controllers
|
||||
for Display Port.
|
||||
|
||||
config MFD_PM8XXX
|
||||
tristate "Qualcomm PM8xxx PMIC chips driver"
|
||||
depends on (ARM || HEXAGON || COMPILE_TEST)
|
||||
|
|
|
@ -256,3 +256,5 @@ obj-$(CONFIG_MFD_ROHM_BD70528) += rohm-bd70528.o
|
|||
obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
|
||||
obj-$(CONFIG_MFD_STMFX) += stmfx.o
|
||||
|
||||
obj-$(CONFIG_MFD_PHYTIUM_I2S_LSD) += phytium_px210_i2s_lsd.o
|
||||
obj-$(CONFIG_MFD_PHYTIUM_I2S_MMD) += phytium_px210_i2s_mmd.o
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Phytium I2S LSD MFD driver over PCI bus
|
||||
*
|
||||
* Copyright (C) 2020-2023, Phytium Technology Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mfd/core.h>
|
||||
|
||||
struct phytium_px210_mfd {
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
struct pdata_px210_mfd {
|
||||
struct device *dev;
|
||||
char *name;
|
||||
int clk_base;
|
||||
};
|
||||
|
||||
static struct resource phytium_px210_i2s_res0[] = {
|
||||
[0] = {
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell phytium_px210_mfd_cells[] = {
|
||||
{
|
||||
.id = 0,
|
||||
.name = "phytium-i2s",
|
||||
.of_compatible = "phytium,i2s",
|
||||
.resources = phytium_px210_i2s_res0,
|
||||
.num_resources = ARRAY_SIZE(phytium_px210_i2s_res0),
|
||||
.ignore_resource_conflicts = true,
|
||||
},
|
||||
};
|
||||
|
||||
static void phytium_px210_i2s_setup(struct pci_dev *pdev)
|
||||
{
|
||||
struct mfd_cell *cell = &phytium_px210_mfd_cells[0];
|
||||
struct resource *res = (struct resource *)cell->resources;
|
||||
struct pdata_px210_mfd *pdata;
|
||||
|
||||
res[0].start = pci_resource_start(pdev, 0);
|
||||
res[0].end = pci_resource_start(pdev, 0) + 0x0fff;
|
||||
|
||||
res[1].start = pci_resource_start(pdev, 0) + 0x1000;
|
||||
res[1].end = pci_resource_start(pdev, 0) + 0x1fff;
|
||||
|
||||
res[2].start = pdev->irq;
|
||||
res[2].end = pdev->irq;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
|
||||
pdata->dev = &pdev->dev;
|
||||
pdata->name = "phytium-i2s-lsd";
|
||||
pdata->clk_base = 480000000;
|
||||
|
||||
cell->platform_data = pdata;
|
||||
cell->pdata_size = sizeof(*pdata);
|
||||
}
|
||||
|
||||
static int phytium_px210_mfd_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct phytium_px210_mfd *phytium_mfd;
|
||||
int ret;
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
phytium_mfd = devm_kzalloc(&pdev->dev, sizeof(*phytium_mfd), GFP_KERNEL);
|
||||
if (!phytium_mfd)
|
||||
return -ENOMEM;
|
||||
|
||||
phytium_mfd->dev = &pdev->dev;
|
||||
dev_set_drvdata(&pdev->dev, phytium_mfd);
|
||||
|
||||
phytium_px210_i2s_setup(pdev);
|
||||
|
||||
ret = mfd_add_devices(&pdev->dev, 0, phytium_px210_mfd_cells,
|
||||
ARRAY_SIZE(phytium_px210_mfd_cells), NULL, 0,
|
||||
NULL);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void phytium_px210_mfd_remove(struct pci_dev *pdev)
|
||||
{
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id phytium_px210_mfd_ids[] = {
|
||||
{
|
||||
.vendor = 0x1DB7,
|
||||
.device = 0xDC2B,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.class = 0x3,
|
||||
.class_mask = 0,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, phytium_px210_mfd_ids);
|
||||
|
||||
static struct pci_driver phytium_i2s_lsd_mfd_driver = {
|
||||
.name = "phytium_px210_mfd_i2s",
|
||||
.id_table = phytium_px210_mfd_ids,
|
||||
.probe = phytium_px210_mfd_probe,
|
||||
.remove = phytium_px210_mfd_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(phytium_i2s_lsd_mfd_driver);
|
||||
|
||||
MODULE_AUTHOR("Yiqun Zhang <zhangyiqun@phytium.com.cn>");
|
||||
MODULE_DESCRIPTION("Phytium Px210 MFD PCI driver for I2S-LSD");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,185 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Phytium I2S MMD MFD driver over PCI bus
|
||||
*
|
||||
* Copyright (C) 2020-2023, Phytium Technology Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mfd/core.h>
|
||||
|
||||
struct phytium_px210_mfd {
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
struct pdata_px210_mfd {
|
||||
struct device *dev;
|
||||
char *name;
|
||||
int clk_base;
|
||||
};
|
||||
|
||||
static struct resource phytium_px210_i2s_res0[] = {
|
||||
[0] = {
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource phytium_px210_i2s_res1[] = {
|
||||
[0] = {
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource phytium_px210_i2s_res2[] = {
|
||||
[0] = {
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell phytium_px210_mfd_cells[] = {
|
||||
{
|
||||
.id = 1,
|
||||
.name = "phytium-i2s",
|
||||
.of_compatible = "phytium,i2s",
|
||||
.resources = phytium_px210_i2s_res0,
|
||||
.num_resources = ARRAY_SIZE(phytium_px210_i2s_res0),
|
||||
.ignore_resource_conflicts = true,
|
||||
},
|
||||
{
|
||||
.id = 2,
|
||||
.name = "phytium-i2s",
|
||||
.of_compatible = "phytium,i2s",
|
||||
.resources = phytium_px210_i2s_res1,
|
||||
.num_resources = ARRAY_SIZE(phytium_px210_i2s_res1),
|
||||
.ignore_resource_conflicts = true,
|
||||
},
|
||||
{
|
||||
.id = 3,
|
||||
.name = "phytium-i2s",
|
||||
.of_compatible = "phytium,i2s",
|
||||
.resources = phytium_px210_i2s_res2,
|
||||
.num_resources = ARRAY_SIZE(phytium_px210_i2s_res2),
|
||||
.ignore_resource_conflicts = true,
|
||||
},
|
||||
};
|
||||
|
||||
static void phytium_px210_i2s_setup(struct pci_dev *pdev, int i)
|
||||
{
|
||||
struct mfd_cell *cell = &phytium_px210_mfd_cells[i];
|
||||
struct resource *res = (struct resource *)cell->resources;
|
||||
struct pdata_px210_mfd *pdata;
|
||||
|
||||
res[0].start = pci_resource_start(pdev, 0) + 0x2000 * i + 0x1000;
|
||||
res[0].end = pci_resource_start(pdev, 0) + 0x2000 * i + 0x1fff;
|
||||
|
||||
res[1].start = pci_resource_start(pdev, 0) + 0x2000 * i;
|
||||
res[1].end = pci_resource_start(pdev, 0) + 0x2000 * i + 0x0fff;
|
||||
|
||||
res[2].start = pdev->irq;
|
||||
res[2].end = pdev->irq;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
|
||||
pdata->dev = &pdev->dev;
|
||||
pdata->clk_base = 600000000;
|
||||
switch (i) {
|
||||
case 0:
|
||||
pdata->name = "phytium-i2s-dp0";
|
||||
break;
|
||||
case 1:
|
||||
pdata->name = "phytium-i2s-dp1";
|
||||
break;
|
||||
case 2:
|
||||
pdata->name = "phytium-i2s-dp2";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
cell->platform_data = pdata;
|
||||
cell->pdata_size = sizeof(*pdata);
|
||||
}
|
||||
|
||||
static int phytium_px210_mfd_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct phytium_px210_mfd *phytium_mfd;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
phytium_mfd = devm_kzalloc(&pdev->dev, sizeof(*phytium_mfd), GFP_KERNEL);
|
||||
if (!phytium_mfd)
|
||||
return -ENOMEM;
|
||||
|
||||
phytium_mfd->dev = &pdev->dev;
|
||||
dev_set_drvdata(&pdev->dev, phytium_mfd);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
phytium_px210_i2s_setup(pdev, i);
|
||||
|
||||
ret = mfd_add_devices(&pdev->dev, 0, phytium_px210_mfd_cells,
|
||||
ARRAY_SIZE(phytium_px210_mfd_cells), NULL, 0,
|
||||
NULL);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void phytium_px210_mfd_remove(struct pci_dev *pdev)
|
||||
{
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id phytium_px210_mfd_ids[] = {
|
||||
{
|
||||
.vendor = 0x1DB7,
|
||||
.device = 0xDC23,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.class = 0x3,
|
||||
.class_mask = 0,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, phytium_px210_mfd_ids);
|
||||
|
||||
static struct pci_driver phytium_i2s_mmd_mfd_driver = {
|
||||
.name = "phytium_px210_mfd_mmd",
|
||||
.id_table = phytium_px210_mfd_ids,
|
||||
.probe = phytium_px210_mfd_probe,
|
||||
.remove = phytium_px210_mfd_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(phytium_i2s_mmd_mfd_driver);
|
||||
|
||||
MODULE_AUTHOR("Yiqun Zhang <zhangyiqun@phytium.com.cn>");
|
||||
MODULE_DESCRIPTION("Phytium Px210 MFD PCI driver for I2S-DP");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -57,6 +57,7 @@ source "sound/soc/intel/Kconfig"
|
|||
source "sound/soc/mediatek/Kconfig"
|
||||
source "sound/soc/meson/Kconfig"
|
||||
source "sound/soc/mxs/Kconfig"
|
||||
source "sound/soc/phytium/Kconfig"
|
||||
source "sound/soc/pxa/Kconfig"
|
||||
source "sound/soc/qcom/Kconfig"
|
||||
source "sound/soc/rockchip/Kconfig"
|
||||
|
|
|
@ -40,6 +40,7 @@ obj-$(CONFIG_SND_SOC) += mediatek/
|
|||
obj-$(CONFIG_SND_SOC) += meson/
|
||||
obj-$(CONFIG_SND_SOC) += mxs/
|
||||
obj-$(CONFIG_SND_SOC) += kirkwood/
|
||||
obj-$(CONFIG_SND_SOC) += phytium/
|
||||
obj-$(CONFIG_SND_SOC) += pxa/
|
||||
obj-$(CONFIG_SND_SOC) += qcom/
|
||||
obj-$(CONFIG_SND_SOC) += rockchip/
|
||||
|
|
|
@ -696,6 +696,15 @@ config SND_SOC_ES8328_SPI
|
|||
depends on SPI_MASTER
|
||||
select SND_SOC_ES8328
|
||||
|
||||
config SND_SOC_ES8336
|
||||
tristate "Everest Semi ES8336 CODEC"
|
||||
depends on I2C
|
||||
select GPIO_PHYTIUM_PCI
|
||||
|
||||
config SND_SOC_ES8388
|
||||
tristate "Everest Semi ES8388 CODEC"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_GTM601
|
||||
tristate 'GTM601 UMTS modem audio codec'
|
||||
|
||||
|
|
|
@ -86,6 +86,8 @@ snd-soc-es8316-objs := es8316.o
|
|||
snd-soc-es8328-objs := es8328.o
|
||||
snd-soc-es8328-i2c-objs := es8328-i2c.o
|
||||
snd-soc-es8328-spi-objs := es8328-spi.o
|
||||
snd-soc-es8336-objs := es8336.o
|
||||
snd-soc-es8388-objs := es8388.o
|
||||
snd-soc-gtm601-objs := gtm601.o
|
||||
snd-soc-hdac-hdmi-objs := hdac_hdmi.o
|
||||
snd-soc-hdac-hda-objs := hdac_hda.o
|
||||
|
@ -370,6 +372,8 @@ obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o
|
|||
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
|
||||
obj-$(CONFIG_SND_SOC_ES8336) += snd-soc-es8336.o
|
||||
obj-$(CONFIG_SND_SOC_ES8388) += snd-soc-es8388.o
|
||||
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
|
||||
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,153 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) Everest Semiconductor Co.,Ltd
|
||||
* Copyright (C) 2022-2023, Phytium Technology Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _ES8336_H
|
||||
#define _ES8336_H
|
||||
|
||||
/* ES8336 register space */
|
||||
/*
|
||||
* RESET Control
|
||||
*/
|
||||
#define ES8336_RESET_REG00 0x00
|
||||
/*
|
||||
* Clock Managerment
|
||||
*/
|
||||
#define ES8336_CLKMGR_CLKSW_REG01 0x01
|
||||
#define ES8336_CLKMGR_CLKSEL_REG02 0x02
|
||||
#define ES8336_CLKMGR_ADCOSR_REG03 0x03
|
||||
#define ES8336_CLKMGR_ADCDIV1_REG04 0x04
|
||||
#define ES8336_CLKMGR_ADCDIV2_REG05 0x05
|
||||
#define ES8336_CLKMGR_DACDIV1_REG06 0x06
|
||||
#define ES8336_CLKMGR_DACDIV2_REG07 0x07
|
||||
#define ES8336_CLKMGR_CPDIV_REG08 0x08
|
||||
/*
|
||||
* SDP Control
|
||||
*/
|
||||
#define ES8336_SDP_MS_BCKDIV_REG09 0x09
|
||||
#define ES8336_SDP_ADCFMT_REG0A 0x0a
|
||||
#define ES8336_SDP_DACFMT_REG0B 0x0b
|
||||
/*
|
||||
* System Control
|
||||
*/
|
||||
#define ES8336_SYS_VMIDSEL_REG0C 0x0c
|
||||
#define ES8336_SYS_PDN_REG0D 0x0d
|
||||
#define ES8336_SYS_LP1_REG0E 0x0e
|
||||
#define ES8336_SYS_LP2_REG0F 0x0f
|
||||
#define ES8336_SYS_VMIDLOW_REG10 0x10
|
||||
#define ES8336_SYS_VSEL_REG11 0x11
|
||||
#define ES8336_SYS_REF_REG12 0x12
|
||||
/*
|
||||
* HP Mixer
|
||||
*/
|
||||
#define ES8336_HPMIX_SEL_REG13 0x13
|
||||
#define ES8336_HPMIX_SWITCH_REG14 0x14
|
||||
#define ES8336_HPMIX_PDN_REG15 0x15
|
||||
#define ES8336_HPMIX_VOL_REG16 0x16
|
||||
/*
|
||||
* Charge Pump Headphone driver
|
||||
*/
|
||||
#define ES8336_CPHP_OUTEN_REG17 0x17
|
||||
#define ES8336_CPHP_ICAL_VOL_REG18 0x18
|
||||
#define ES8336_CPHP_PDN1_REG19 0x19
|
||||
#define ES8336_CPHP_PDN2_REG1A 0x1a
|
||||
#define ES8336_CPHP_LDOCTL_REG1B 0x1b
|
||||
/*
|
||||
* Calibration
|
||||
*/
|
||||
#define ES8336_CAL_TYPE_REG1C 0x1c
|
||||
#define ES8336_CAL_SET_REG1D 0x1d
|
||||
#define ES8336_CAL_HPLIV_REG1E 0x1e
|
||||
#define ES8336_CAL_HPRIV_REG1F 0x1f
|
||||
#define ES8336_CAL_HPLMV_REG20 0x20
|
||||
#define ES8336_CAL_HPRMV_REG21 0x21
|
||||
/*
|
||||
* ADC Control
|
||||
*/
|
||||
#define ES8336_ADC_PDN_LINSEL_REG22 0x22
|
||||
#define ES8336_ADC_PGAGAIN_REG23 0x23
|
||||
#define ES8336_ADC_D2SEPGA_REG24 0x24
|
||||
#define ES8336_ADC_DMIC_REG25 0x25
|
||||
#define ES8336_ADC_MUTE_REG26 0x26
|
||||
#define ES8336_ADC_VOLUME_REG27 0x27
|
||||
#define ES8336_ADC_ALC1_REG29 0x29
|
||||
#define ES8336_ADC_ALC2_REG2A 0x2a
|
||||
#define ES8336_ADC_ALC3_REG2B 0x2b
|
||||
#define ES8336_ADC_ALC4_REG2C 0x2c
|
||||
#define ES8336_ADC_ALC5_REG2D 0x2d
|
||||
#define ES8336_ADC_ALC6_REG2E 0x2e
|
||||
/*
|
||||
* DAC Control
|
||||
*/
|
||||
#define ES8336_DAC_PDN_REG2F 0x2f
|
||||
#define ES8336_DAC_SET1_REG30 0x30
|
||||
#define ES8336_DAC_SET2_REG31 0x31
|
||||
#define ES8336_DAC_SET3_REG32 0x32
|
||||
#define ES8336_DAC_VOLL_REG33 0x33
|
||||
#define ES8336_DAC_VOLR_REG34 0x34
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
#define ES8336_GPIO_SEL_REG4D 0x4D
|
||||
#define ES8336_GPIO_DEBUNCE_INT_REG4E 0x4E
|
||||
#define ES8336_GPIO_FLAG 0x4F
|
||||
/*
|
||||
* TEST MODE
|
||||
*/
|
||||
#define ES8336_TESTMODE_REG50 0x50
|
||||
#define ES8336_TEST1_REG51 0x51
|
||||
#define ES8336_TEST2_REG52 0x52
|
||||
#define ES8336_TEST3_REG53 0x53
|
||||
|
||||
#define ES8336_IFACE ES8336_SDP_MS_BCKDIV_REG09
|
||||
#define ES8336_ADC_IFACE ES8336_SDP_ADCFMT_REG0A
|
||||
#define ES8336_DAC_IFACE ES8336_SDP_DACFMT_REG0B
|
||||
|
||||
#define ES8336_REGNUM 84
|
||||
|
||||
/* REGISTER 0X01 CLOCK MANAGER */
|
||||
#define ES8336_CLKMGR_MCLK_DIV_MASK (0X1<<7)
|
||||
#define ES8336_CLKMGR_MCLK_DIV_NML (0X0<<7)
|
||||
#define ES8336_CLKMGR_MCLK_DIV_1 (0X1<<7)
|
||||
#define ES8336_CLKMGR_ADC_MCLK_MASK (0X1<<3)
|
||||
#define ES8336_CLKMGR_ADC_MCLK_EN (0X1<<3)
|
||||
#define ES8336_CLKMGR_ADC_MCLK_DIS (0X0<<3)
|
||||
#define ES8336_CLKMGR_DAC_MCLK_MASK (0X1<<2)
|
||||
#define ES8336_CLKMGR_DAC_MCLK_EN (0X1<<2)
|
||||
#define ES8336_CLKMGR_DAC_MCLK_DIS (0X0<<2)
|
||||
#define ES8336_CLKMGR_ADC_ANALOG_MASK (0X1<<1)
|
||||
#define ES8336_CLKMGR_ADC_ANALOG_EN (0X1<<1)
|
||||
#define ES8336_CLKMGR_ADC_ANALOG_DIS (0X0<<1)
|
||||
#define ES8336_CLKMGR_DAC_ANALOG_MASK (0X1<<0)
|
||||
#define ES8336_CLKMGR_DAC_ANALOG_EN (0X1<<0)
|
||||
#define ES8336_CLKMGR_DAC_ANALOG_DIS (0X0<<0)
|
||||
|
||||
/* REGISTER 0X0A */
|
||||
#define ES8336_ADCWL_MASK (0x7 << 2)
|
||||
#define ES8336_ADCWL_32 (0x4 << 2)
|
||||
#define ES8336_ADCWL_24 (0x0 << 2)
|
||||
#define ES8336_ADCWL_20 (0x1 << 2)
|
||||
#define ES8336_ADCWL_18 (0x2 << 2)
|
||||
#define ES8336_ADCWL_16 (0x3 << 2)
|
||||
#define ES8336_ADCFMT_MASK (0x3 << 0)
|
||||
#define ES8336_ADCFMT_I2S (0x0 << 0)
|
||||
#define ES8336_ADCWL_LEFT (0x1 << 0)
|
||||
#define ES8336_ADCWL_RIGHT (0x2 << 0)
|
||||
#define ES8336_ADCWL_PCM (0x3 << 0)
|
||||
|
||||
/* REGISTER 0X0B */
|
||||
#define ES8336_DACWL_MASK (0x7 << 2)
|
||||
#define ES8336_DACWL_32 (0x4 << 2)
|
||||
#define ES8336_DACWL_24 (0x0 << 2)
|
||||
#define ES8336_DACWL_20 (0x1 << 2)
|
||||
#define ES8336_DACWL_18 (0x2 << 2)
|
||||
#define ES8336_DACWL_16 (0x3 << 2)
|
||||
#define ES8336_DACFMT_MASK (0x3 << 0)
|
||||
#define ES8336_DACFMT_I2S (0x0 << 0)
|
||||
#define ES8336_DACWL_LEFT (0x1 << 0)
|
||||
#define ES8336_DACWL_RIGHT (0x2 << 0)
|
||||
#define ES8336_DACWL_PCM (0x3 << 0)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,820 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* es8388.c -- ES8388 ALSA SoC Audio driver
|
||||
*
|
||||
* Copyright 2021 Phytium Technology
|
||||
* Author: Yiqun Zhang <zhangyiqun@phytium.com.cn>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "es8388.h"
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
static const unsigned int rates_12288[] = {
|
||||
8000, 12000, 16000, 24000, 32000, 48000, 96000,
|
||||
};
|
||||
|
||||
static const int ratios_12288[] = {
|
||||
10, 7, 6, 4, 3, 2, 0,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list constraints_12288 = {
|
||||
.count = ARRAY_SIZE(rates_12288),
|
||||
.list = rates_12288,
|
||||
};
|
||||
|
||||
static const unsigned int rates_11289[] = {
|
||||
8018, 11025, 22050, 44100, 88200,
|
||||
};
|
||||
|
||||
static const int ratios_11289[] = {
|
||||
9, 7, 4, 2, 0,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list constraints_11289 = {
|
||||
.count = ARRAY_SIZE(rates_11289),
|
||||
.list = rates_11289,
|
||||
};
|
||||
|
||||
#define ES8388_RATES (SNDRV_PCM_RATE_192000 | \
|
||||
SNDRV_PCM_RATE_96000 | \
|
||||
SNDRV_PCM_RATE_88200 | \
|
||||
SNDRV_PCM_RATE_8000_48000)
|
||||
#define ES8388_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S18_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
struct es8388_priv {
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
int playback_fs;
|
||||
bool deemph;
|
||||
int mclkdiv2;
|
||||
const struct snd_pcm_hw_constraint_list *sysclk_constraints;
|
||||
const int *mclk_ratios;
|
||||
bool master;
|
||||
};
|
||||
|
||||
/*
|
||||
* ES8388 Controls
|
||||
*/
|
||||
static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert",
|
||||
"L + R Invert"};
|
||||
static SOC_ENUM_SINGLE_DECL(adcpol,
|
||||
ES8388_ADCCONTROL6, 6, adcpol_txt);
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
|
||||
|
||||
static const struct {
|
||||
int rate;
|
||||
unsigned int val;
|
||||
} deemph_settings[] = {
|
||||
{ 0, ES8388_DACCONTROL6_DEEMPH_OFF },
|
||||
{ 32000, ES8388_DACCONTROL6_DEEMPH_32k },
|
||||
{ 44100, ES8388_DACCONTROL6_DEEMPH_44_1k },
|
||||
{ 48000, ES8388_DACCONTROL6_DEEMPH_48k },
|
||||
};
|
||||
|
||||
static int es8388_set_deemph(struct snd_soc_component *component)
|
||||
{
|
||||
struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component);
|
||||
int val, i, best;
|
||||
|
||||
/*
|
||||
* If we're using deemphasis select the nearest available sample
|
||||
* rate.
|
||||
*/
|
||||
if (es8388->deemph) {
|
||||
best = 0;
|
||||
for (i = 1; i < ARRAY_SIZE(deemph_settings); i++) {
|
||||
if (abs(deemph_settings[i].rate - es8388->playback_fs) <
|
||||
abs(deemph_settings[best].rate - es8388->playback_fs))
|
||||
best = i;
|
||||
}
|
||||
|
||||
val = deemph_settings[best].val;
|
||||
} else {
|
||||
val = ES8388_DACCONTROL6_DEEMPH_OFF;
|
||||
}
|
||||
|
||||
dev_dbg(component->dev, "Set deemphasis %d\n", val);
|
||||
|
||||
return snd_soc_component_update_bits(component, ES8388_DACCONTROL6,
|
||||
ES8388_DACCONTROL6_DEEMPH_MASK, val);
|
||||
}
|
||||
|
||||
static int es8388_get_deemph(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
||||
struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
ucontrol->value.integer.value[0] = es8388->deemph;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8388_put_deemph(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
|
||||
struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int deemph = ucontrol->value.integer.value[0];
|
||||
int ret;
|
||||
|
||||
if (deemph > 1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = es8388_set_deemph(component);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
es8388->deemph = deemph;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new es8388_snd_controls[] = {
|
||||
SOC_DOUBLE_R_TLV("Capture Digital Volume",
|
||||
ES8388_ADCCONTROL8, ES8388_ADCCONTROL9,
|
||||
0, 0xc0, 1, dac_adc_tlv),
|
||||
|
||||
SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
|
||||
es8388_get_deemph, es8388_put_deemph),
|
||||
|
||||
SOC_ENUM("Capture Polarity", adcpol),
|
||||
|
||||
SOC_SINGLE_TLV("Left Mixer Left Bypass Volume",
|
||||
ES8388_DACCONTROL17, 3, 7, 1, bypass_tlv),
|
||||
SOC_SINGLE_TLV("Left Mixer Right Bypass Volume",
|
||||
ES8388_DACCONTROL19, 3, 7, 1, bypass_tlv),
|
||||
SOC_SINGLE_TLV("Right Mixer Left Bypass Volume",
|
||||
ES8388_DACCONTROL18, 3, 7, 1, bypass_tlv),
|
||||
SOC_SINGLE_TLV("Right Mixer Right Bypass Volume",
|
||||
ES8388_DACCONTROL20, 3, 7, 1, bypass_tlv),
|
||||
|
||||
SOC_DOUBLE_R_TLV("PCM Volume",
|
||||
ES8388_LDACVOL, ES8388_RDACVOL,
|
||||
0, ES8388_DACVOL_MAX, 1, dac_adc_tlv),
|
||||
|
||||
SOC_DOUBLE_R_TLV("Output 1 Playback Volume",
|
||||
ES8388_LOUT1VOL, ES8388_ROUT1VOL,
|
||||
0, ES8388_OUT1VOL_MAX, 0, play_tlv),
|
||||
|
||||
SOC_DOUBLE_R_TLV("Output 2 Playback Volume",
|
||||
ES8388_LOUT2VOL, ES8388_ROUT2VOL,
|
||||
0, ES8388_OUT2VOL_MAX, 0, play_tlv),
|
||||
|
||||
SOC_DOUBLE_TLV("Mic PGA Volume", ES8388_ADCCONTROL1,
|
||||
4, 0, 8, 0, mic_tlv),
|
||||
};
|
||||
|
||||
/*
|
||||
* DAPM Controls
|
||||
*/
|
||||
static const char * const es8388_line_texts[] = {
|
||||
"Line 1", "Line 2", "PGA", "Differential"};
|
||||
|
||||
static const struct soc_enum es8388_lline_enum =
|
||||
SOC_ENUM_SINGLE(ES8388_DACCONTROL16, 3,
|
||||
ARRAY_SIZE(es8388_line_texts),
|
||||
es8388_line_texts);
|
||||
static const struct snd_kcontrol_new es8388_left_line_controls =
|
||||
SOC_DAPM_ENUM("Route", es8388_lline_enum);
|
||||
|
||||
static const struct soc_enum es8388_rline_enum =
|
||||
SOC_ENUM_SINGLE(ES8388_DACCONTROL16, 0,
|
||||
ARRAY_SIZE(es8388_line_texts),
|
||||
es8388_line_texts);
|
||||
static const struct snd_kcontrol_new es8388_right_line_controls =
|
||||
SOC_DAPM_ENUM("Route", es8388_lline_enum);
|
||||
|
||||
/* Left Mixer */
|
||||
static const struct snd_kcontrol_new es8388_left_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Playback Switch", ES8388_DACCONTROL17, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Left Bypass Switch", ES8388_DACCONTROL17, 6, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right Playback Switch", ES8388_DACCONTROL18, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right Bypass Switch", ES8388_DACCONTROL18, 6, 1, 0),
|
||||
};
|
||||
|
||||
/* Right Mixer */
|
||||
static const struct snd_kcontrol_new es8388_right_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Left Playback Switch", ES8388_DACCONTROL19, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Left Bypass Switch", ES8388_DACCONTROL19, 6, 1, 0),
|
||||
SOC_DAPM_SINGLE("Playback Switch", ES8388_DACCONTROL20, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right Bypass Switch", ES8388_DACCONTROL20, 6, 1, 0),
|
||||
};
|
||||
|
||||
static const char * const es8388_pga_sel[] = {
|
||||
"Line 1", "Line 2", "Line 3", "Differential"};
|
||||
|
||||
/* Left PGA Mux */
|
||||
static const struct soc_enum es8388_lpga_enum =
|
||||
SOC_ENUM_SINGLE(ES8388_ADCCONTROL2, 6,
|
||||
ARRAY_SIZE(es8388_pga_sel),
|
||||
es8388_pga_sel);
|
||||
static const struct snd_kcontrol_new es8388_left_pga_controls =
|
||||
SOC_DAPM_ENUM("Route", es8388_lpga_enum);
|
||||
|
||||
/* Right PGA Mux */
|
||||
static const struct soc_enum es8388_rpga_enum =
|
||||
SOC_ENUM_SINGLE(ES8388_ADCCONTROL2, 4,
|
||||
ARRAY_SIZE(es8388_pga_sel),
|
||||
es8388_pga_sel);
|
||||
static const struct snd_kcontrol_new es8388_right_pga_controls =
|
||||
SOC_DAPM_ENUM("Route", es8388_rpga_enum);
|
||||
|
||||
/* Differential Mux */
|
||||
static const char * const es8388_diff_sel[] = {"Line 1", "Line 2"};
|
||||
static SOC_ENUM_SINGLE_DECL(diffmux,
|
||||
ES8388_ADCCONTROL3, 7, es8388_diff_sel);
|
||||
static const struct snd_kcontrol_new es8388_diffmux_controls =
|
||||
SOC_DAPM_ENUM("Route", diffmux);
|
||||
|
||||
/* Mono ADC Mux */
|
||||
static const char * const es8388_mono_mux[] = {"Stereo", "Mono (Left)",
|
||||
"Mono (Right)", "Digital Mono"};
|
||||
static SOC_ENUM_SINGLE_DECL(monomux,
|
||||
ES8388_ADCCONTROL3, 3, es8388_mono_mux);
|
||||
static const struct snd_kcontrol_new es8388_monomux_controls =
|
||||
SOC_DAPM_ENUM("Route", monomux);
|
||||
|
||||
static const struct snd_soc_dapm_widget es8388_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
|
||||
&es8388_diffmux_controls),
|
||||
SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
|
||||
&es8388_monomux_controls),
|
||||
SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
|
||||
&es8388_monomux_controls),
|
||||
|
||||
SND_SOC_DAPM_MUX("Left PGA Mux", ES8388_ADCPOWER,
|
||||
ES8388_ADCPOWER_AINL_OFF, 1,
|
||||
&es8388_left_pga_controls),
|
||||
SND_SOC_DAPM_MUX("Right PGA Mux", ES8388_ADCPOWER,
|
||||
ES8388_ADCPOWER_AINR_OFF, 1,
|
||||
&es8388_right_pga_controls),
|
||||
|
||||
SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
|
||||
&es8388_left_line_controls),
|
||||
SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
|
||||
&es8388_right_line_controls),
|
||||
|
||||
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8388_ADCPOWER,
|
||||
ES8388_ADCPOWER_ADCR_OFF, 1),
|
||||
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8388_ADCPOWER,
|
||||
ES8388_ADCPOWER_ADCL_OFF, 1),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("DAC STM", ES8388_CHIPPOWER,
|
||||
ES8388_CHIPPOWER_DACSTM_RESET, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADC STM", ES8388_CHIPPOWER,
|
||||
ES8388_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("DAC DIG", ES8388_CHIPPOWER,
|
||||
ES8388_CHIPPOWER_DACDIG_OFF, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADC DIG", ES8388_CHIPPOWER,
|
||||
ES8388_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("DAC DLL", ES8388_CHIPPOWER,
|
||||
ES8388_CHIPPOWER_DACDLL_OFF, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADC DLL", ES8388_CHIPPOWER,
|
||||
ES8388_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("ADC Vref", ES8388_CHIPPOWER,
|
||||
ES8388_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DAC Vref", ES8388_CHIPPOWER,
|
||||
ES8388_CHIPPOWER_DACVREF_OFF, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8388_DACPOWER,
|
||||
ES8388_DACPOWER_RDAC_OFF, 1),
|
||||
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8388_DACPOWER,
|
||||
ES8388_DACPOWER_LDAC_OFF, 1),
|
||||
|
||||
SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
|
||||
&es8388_left_mixer_controls[0],
|
||||
ARRAY_SIZE(es8388_left_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
|
||||
&es8388_right_mixer_controls[0],
|
||||
ARRAY_SIZE(es8388_right_mixer_controls)),
|
||||
|
||||
SND_SOC_DAPM_PGA("Right Out 2", ES8388_DACPOWER,
|
||||
ES8388_DACPOWER_ROUT2_ON, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("Left Out 2", ES8388_DACPOWER,
|
||||
ES8388_DACPOWER_LOUT2_ON, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("Right Out 1", ES8388_DACPOWER,
|
||||
ES8388_DACPOWER_ROUT1_ON, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("Left Out 1", ES8388_DACPOWER,
|
||||
ES8388_DACPOWER_LOUT1_ON, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("LOUT1"),
|
||||
SND_SOC_DAPM_OUTPUT("ROUT1"),
|
||||
SND_SOC_DAPM_OUTPUT("LOUT2"),
|
||||
SND_SOC_DAPM_OUTPUT("ROUT2"),
|
||||
|
||||
SND_SOC_DAPM_INPUT("LINPUT1"),
|
||||
SND_SOC_DAPM_INPUT("LINPUT2"),
|
||||
SND_SOC_DAPM_INPUT("RINPUT1"),
|
||||
SND_SOC_DAPM_INPUT("RINPUT2"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route es8388_dapm_routes[] = {
|
||||
{ "Left Line Mux", "Line 1", "LINPUT1" },
|
||||
{ "Left Line Mux", "Line 2", "LINPUT2" },
|
||||
{ "Left Line Mux", "PGA", "Left PGA Mux" },
|
||||
{ "Left Line Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Right Line Mux", "Line 1", "RINPUT1" },
|
||||
{ "Right Line Mux", "Line 2", "RINPUT2" },
|
||||
{ "Right Line Mux", "PGA", "Right PGA Mux" },
|
||||
{ "Right Line Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Left PGA Mux", "Line 1", "LINPUT1" },
|
||||
{ "Left PGA Mux", "Line 2", "LINPUT2" },
|
||||
{ "Left PGA Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Right PGA Mux", "Line 1", "RINPUT1" },
|
||||
{ "Right PGA Mux", "Line 2", "RINPUT2" },
|
||||
{ "Right PGA Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Differential Mux", "Line 1", "LINPUT1" },
|
||||
{ "Differential Mux", "Line 1", "RINPUT1" },
|
||||
{ "Differential Mux", "Line 2", "LINPUT2" },
|
||||
{ "Differential Mux", "Line 2", "RINPUT2" },
|
||||
|
||||
{ "Left ADC Mux", "Stereo", "Left PGA Mux" },
|
||||
{ "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
|
||||
{ "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
|
||||
|
||||
{ "Right ADC Mux", "Stereo", "Right PGA Mux" },
|
||||
{ "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
|
||||
{ "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
|
||||
|
||||
{ "Left ADC", NULL, "Left ADC Mux" },
|
||||
{ "Right ADC", NULL, "Right ADC Mux" },
|
||||
|
||||
{ "ADC DIG", NULL, "ADC STM" },
|
||||
{ "ADC DIG", NULL, "ADC Vref" },
|
||||
{ "ADC DIG", NULL, "ADC DLL" },
|
||||
|
||||
{ "Left ADC", NULL, "ADC DIG" },
|
||||
{ "Right ADC", NULL, "ADC DIG" },
|
||||
|
||||
{ "Left Line Mux", "Line 1", "LINPUT1" },
|
||||
{ "Left Line Mux", "Line 2", "LINPUT2" },
|
||||
{ "Left Line Mux", "PGA", "Left PGA Mux" },
|
||||
{ "Left Line Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Right Line Mux", "Line 1", "RINPUT1" },
|
||||
{ "Right Line Mux", "Line 2", "RINPUT2" },
|
||||
{ "Right Line Mux", "PGA", "Right PGA Mux" },
|
||||
{ "Right Line Mux", "Differential", "Differential Mux" },
|
||||
|
||||
{ "Left Out 1", NULL, "Left DAC" },
|
||||
{ "Right Out 1", NULL, "Right DAC" },
|
||||
{ "Left Out 2", NULL, "Left DAC" },
|
||||
{ "Right Out 2", NULL, "Right DAC" },
|
||||
|
||||
{ "Left Mixer", "Playback Switch", "Left DAC" },
|
||||
{ "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
|
||||
{ "Left Mixer", "Right Playback Switch", "Right DAC" },
|
||||
{ "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
|
||||
|
||||
{ "Right Mixer", "Left Playback Switch", "Left DAC" },
|
||||
{ "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
|
||||
{ "Right Mixer", "Playback Switch", "Right DAC" },
|
||||
{ "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
|
||||
|
||||
{ "DAC DIG", NULL, "DAC STM" },
|
||||
{ "DAC DIG", NULL, "DAC Vref" },
|
||||
{ "DAC DIG", NULL, "DAC DLL" },
|
||||
|
||||
{ "Left DAC", NULL, "DAC DIG" },
|
||||
{ "Right DAC", NULL, "DAC DIG" },
|
||||
|
||||
{ "Left Out 1", NULL, "Left Mixer" },
|
||||
{ "LOUT1", NULL, "Left Out 1" },
|
||||
{ "Right Out 1", NULL, "Right Mixer" },
|
||||
{ "ROUT1", NULL, "Right Out 1" },
|
||||
|
||||
{ "Left Out 2", NULL, "Left Mixer" },
|
||||
{ "LOUT2", NULL, "Left Out 2" },
|
||||
{ "Right Out 2", NULL, "Right Mixer" },
|
||||
{ "ROUT2", NULL, "Right Out 2" },
|
||||
};
|
||||
|
||||
static int es8388_mute(struct snd_soc_dai *dai, int mute, int direction)
|
||||
{
|
||||
return snd_soc_component_update_bits(dai->component, ES8388_DACCONTROL3,
|
||||
ES8388_DACCONTROL3_DACMUTE,
|
||||
mute ? ES8388_DACCONTROL3_DACMUTE : 0);
|
||||
}
|
||||
|
||||
static int es8388_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (es8388->master && es8388->sysclk_constraints)
|
||||
snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
es8388->sysclk_constraints);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8388_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 es8388_priv *es8388 = snd_soc_component_get_drvdata(component);
|
||||
int i;
|
||||
int reg;
|
||||
int wl;
|
||||
int ratio;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
reg = ES8388_DACCONTROL2;
|
||||
else
|
||||
reg = ES8388_ADCCONTROL5;
|
||||
|
||||
if (es8388->master) {
|
||||
if (!es8388->sysclk_constraints) {
|
||||
dev_err(component->dev, "No MCLK configured\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < es8388->sysclk_constraints->count; i++)
|
||||
if (es8388->sysclk_constraints->list[i] ==
|
||||
params_rate(params))
|
||||
break;
|
||||
|
||||
if (i == es8388->sysclk_constraints->count) {
|
||||
dev_err(component->dev,
|
||||
"LRCLK %d unsupported with current clock\n",
|
||||
params_rate(params));
|
||||
return -EINVAL;
|
||||
}
|
||||
ratio = es8388->mclk_ratios[i];
|
||||
} else {
|
||||
ratio = 0;
|
||||
es8388->mclkdiv2 = 0;
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, ES8388_MASTERMODE,
|
||||
ES8388_MASTERMODE_MCLKDIV2,
|
||||
es8388->mclkdiv2 ? ES8388_MASTERMODE_MCLKDIV2 : 0);
|
||||
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
wl = 3;
|
||||
break;
|
||||
case 18:
|
||||
wl = 2;
|
||||
break;
|
||||
case 20:
|
||||
wl = 1;
|
||||
break;
|
||||
case 24:
|
||||
wl = 0;
|
||||
break;
|
||||
case 32:
|
||||
wl = 4;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
snd_soc_component_update_bits(component, ES8388_DACCONTROL1,
|
||||
ES8388_DACCONTROL1_DACWL_MASK,
|
||||
wl << ES8388_DACCONTROL1_DACWL_SHIFT);
|
||||
|
||||
es8388->playback_fs = params_rate(params);
|
||||
es8388_set_deemph(component);
|
||||
} else
|
||||
snd_soc_component_update_bits(component, ES8388_ADCCONTROL4,
|
||||
ES8388_ADCCONTROL4_ADCWL_MASK,
|
||||
wl << ES8388_ADCCONTROL4_ADCWL_SHIFT);
|
||||
|
||||
return snd_soc_component_update_bits(component, reg, ES8388_RATEMASK, ratio);
|
||||
}
|
||||
|
||||
static int es8388_set_sysclk(struct snd_soc_dai *codec_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct snd_soc_component *component = codec_dai->component;
|
||||
struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component);
|
||||
int mclkdiv2 = 0;
|
||||
|
||||
switch (freq) {
|
||||
case 0:
|
||||
es8388->sysclk_constraints = NULL;
|
||||
es8388->mclk_ratios = NULL;
|
||||
break;
|
||||
case 22579200:
|
||||
mclkdiv2 = 1;
|
||||
/* fallthru */
|
||||
case 11289600:
|
||||
es8388->sysclk_constraints = &constraints_11289;
|
||||
es8388->mclk_ratios = ratios_11289;
|
||||
break;
|
||||
case 24576000:
|
||||
mclkdiv2 = 1;
|
||||
/* fallthru */
|
||||
case 12288000:
|
||||
es8388->sysclk_constraints = &constraints_12288;
|
||||
es8388->mclk_ratios = ratios_12288;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
es8388->mclkdiv2 = mclkdiv2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8388_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_component *component = codec_dai->component;
|
||||
struct es8388_priv *es8388 = snd_soc_component_get_drvdata(component);
|
||||
u8 dac_mode = 0;
|
||||
u8 adc_mode = 0;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
/* Master serial port mode, with BCLK generated automatically */
|
||||
snd_soc_component_update_bits(component, ES8388_MASTERMODE,
|
||||
ES8388_MASTERMODE_MSC,
|
||||
ES8388_MASTERMODE_MSC);
|
||||
es8388->master = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
/* Slave serial port mode */
|
||||
snd_soc_component_update_bits(component, ES8388_MASTERMODE,
|
||||
ES8388_MASTERMODE_MSC, 0);
|
||||
es8388->master = false;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* interface format */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
dac_mode |= ES8388_DACCONTROL1_DACFORMAT_I2S;
|
||||
adc_mode |= ES8388_ADCCONTROL4_ADCFORMAT_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
dac_mode |= ES8388_DACCONTROL1_DACFORMAT_RJUST;
|
||||
adc_mode |= ES8388_ADCCONTROL4_ADCFORMAT_RJUST;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
dac_mode |= ES8388_DACCONTROL1_DACFORMAT_LJUST;
|
||||
adc_mode |= ES8388_ADCCONTROL4_ADCFORMAT_LJUST;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* clock inversion */
|
||||
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
|
||||
return -EINVAL;
|
||||
|
||||
snd_soc_component_update_bits(component, ES8388_DACCONTROL1,
|
||||
ES8388_DACCONTROL1_DACFORMAT_MASK, dac_mode);
|
||||
snd_soc_component_update_bits(component, ES8388_ADCCONTROL4,
|
||||
ES8388_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8388_set_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
/* VREF, VMID=2x50k, digital enabled */
|
||||
snd_soc_component_write(component, ES8388_CHIPPOWER, 0);
|
||||
snd_soc_component_update_bits(component, ES8388_CONTROL1,
|
||||
ES8388_CONTROL1_VMIDSEL_MASK |
|
||||
ES8388_CONTROL1_ENREF,
|
||||
ES8388_CONTROL1_VMIDSEL_50k |
|
||||
ES8388_CONTROL1_ENREF);
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
|
||||
snd_soc_component_update_bits(component, ES8388_CONTROL1,
|
||||
ES8388_CONTROL1_VMIDSEL_MASK |
|
||||
ES8388_CONTROL1_ENREF,
|
||||
ES8388_CONTROL1_VMIDSEL_5k |
|
||||
ES8388_CONTROL1_ENREF);
|
||||
|
||||
/* Charge caps */
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
snd_soc_component_write(component, ES8388_CONTROL2,
|
||||
ES8388_CONTROL2_OVERCURRENT_ON |
|
||||
ES8388_CONTROL2_THERMAL_SHUTDOWN_ON);
|
||||
|
||||
/* VREF, VMID=2*500k, digital stopped */
|
||||
snd_soc_component_update_bits(component, ES8388_CONTROL1,
|
||||
ES8388_CONTROL1_VMIDSEL_MASK |
|
||||
ES8388_CONTROL1_ENREF,
|
||||
ES8388_CONTROL1_VMIDSEL_500k |
|
||||
ES8388_CONTROL1_ENREF);
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
snd_soc_component_update_bits(component, ES8388_CONTROL1,
|
||||
ES8388_CONTROL1_VMIDSEL_MASK |
|
||||
ES8388_CONTROL1_ENREF,
|
||||
0);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops es8388_dai_ops = {
|
||||
.startup = es8388_startup,
|
||||
.hw_params = es8388_hw_params,
|
||||
.mute_stream = es8388_mute,
|
||||
.set_sysclk = es8388_set_sysclk,
|
||||
.set_fmt = es8388_set_dai_fmt,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver es8388_dai = {
|
||||
.name = "es8388-hifi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = ES8388_RATES,
|
||||
.formats = ES8388_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = ES8388_RATES,
|
||||
.formats = ES8388_FORMATS,
|
||||
},
|
||||
.ops = &es8388_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
|
||||
static int es8388_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8388_resume(struct snd_soc_component *component)
|
||||
{
|
||||
struct regmap *regmap = dev_get_regmap(component->dev, NULL);
|
||||
struct es8388_priv *es8388;
|
||||
int ret;
|
||||
|
||||
es8388 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
regcache_mark_dirty(regmap);
|
||||
ret = regcache_sync(regmap);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "unable to sync regcache\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8388_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
snd_soc_component_write(component, ES8388_ADCPOWER, 0xf0);
|
||||
snd_soc_component_write(component, ES8388_CONTROL1, 0x30);
|
||||
snd_soc_component_write(component, ES8388_DACCONTROL21, 0x80);
|
||||
snd_soc_component_write(component, ES8388_ADCCONTROL10, 0xda);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void es8388_remove(struct snd_soc_component *component)
|
||||
{
|
||||
}
|
||||
|
||||
const struct regmap_config es8388_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = ES8388_REG_MAX,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(es8388_regmap_config);
|
||||
|
||||
static const struct snd_soc_component_driver es8388_component_driver = {
|
||||
.probe = es8388_component_probe,
|
||||
.remove = es8388_remove,
|
||||
.suspend = es8388_suspend,
|
||||
.resume = es8388_resume,
|
||||
.set_bias_level = es8388_set_bias_level,
|
||||
.controls = es8388_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(es8388_snd_controls),
|
||||
.dapm_widgets = es8388_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(es8388_dapm_widgets),
|
||||
.dapm_routes = es8388_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(es8388_dapm_routes),
|
||||
.suspend_bias_off = 1,
|
||||
.idle_bias_on = 1,
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
int es8388_probe(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
struct es8388_priv *es8388;
|
||||
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
es8388 = devm_kzalloc(dev, sizeof(*es8388), GFP_KERNEL);
|
||||
if (es8388 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
es8388->regmap = regmap;
|
||||
|
||||
dev_set_drvdata(dev, es8388);
|
||||
|
||||
return devm_snd_soc_register_component(dev,
|
||||
&es8388_component_driver, &es8388_dai, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(es8388_probe);
|
||||
|
||||
static const struct i2c_device_id es8388_id[] = {
|
||||
{ "es8388", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, es8388_id);
|
||||
|
||||
static const struct of_device_id es8388_of_match[] = {
|
||||
{ .compatible = "everest,es8388", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, es8388_of_match);
|
||||
|
||||
static struct acpi_device_id es8388_acpi_match[] = {
|
||||
{"ESSX8388", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, es8388_acpi_match);
|
||||
|
||||
static int es8388_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return es8388_probe(&i2c->dev,
|
||||
devm_regmap_init_i2c(i2c, &es8388_regmap_config));
|
||||
}
|
||||
|
||||
static struct i2c_driver es8388_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "es8388",
|
||||
.of_match_table = es8388_of_match,
|
||||
.acpi_match_table = es8388_acpi_match,
|
||||
},
|
||||
.probe = es8388_i2c_probe,
|
||||
.id_table = es8388_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(es8388_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC ES8388 driver");
|
||||
MODULE_AUTHOR("Yiqun Zhang <zhangyiqun@phytium.com.cn>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,290 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* es8388.h -- ES8388 ALSA SoC Audio driver
|
||||
*/
|
||||
|
||||
#ifndef _ES8388_H
|
||||
#define _ES8388_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
|
||||
struct device;
|
||||
|
||||
extern const struct regmap_config es8388_regmap_config;
|
||||
int es8388_probe(struct device *dev, struct regmap *regmap);
|
||||
|
||||
#define ES8388_DACLVOL 46
|
||||
#define ES8388_DACRVOL 47
|
||||
#define ES8388_DACCTL 28
|
||||
#define ES8388_RATEMASK (0x1f << 0)
|
||||
|
||||
#define ES8388_CONTROL1 0x00
|
||||
#define ES8388_CONTROL1_VMIDSEL_OFF (0 << 0)
|
||||
#define ES8388_CONTROL1_VMIDSEL_50k (1 << 0)
|
||||
#define ES8388_CONTROL1_VMIDSEL_500k (2 << 0)
|
||||
#define ES8388_CONTROL1_VMIDSEL_5k (3 << 0)
|
||||
#define ES8388_CONTROL1_VMIDSEL_MASK (3 << 0)
|
||||
#define ES8388_CONTROL1_ENREF (1 << 2)
|
||||
#define ES8388_CONTROL1_SEQEN (1 << 3)
|
||||
#define ES8388_CONTROL1_SAMEFS (1 << 4)
|
||||
#define ES8388_CONTROL1_DACMCLK_ADC (0 << 5)
|
||||
#define ES8388_CONTROL1_DACMCLK_DAC (1 << 5)
|
||||
#define ES8388_CONTROL1_LRCM (1 << 6)
|
||||
#define ES8388_CONTROL1_SCP_RESET (1 << 7)
|
||||
|
||||
#define ES8388_CONTROL2 0x01
|
||||
#define ES8388_CONTROL2_VREF_BUF_OFF (1 << 0)
|
||||
#define ES8388_CONTROL2_VREF_LOWPOWER (1 << 1)
|
||||
#define ES8388_CONTROL2_IBIASGEN_OFF (1 << 2)
|
||||
#define ES8388_CONTROL2_ANALOG_OFF (1 << 3)
|
||||
#define ES8388_CONTROL2_VREF_BUF_LOWPOWER (1 << 4)
|
||||
#define ES8388_CONTROL2_VCM_MOD_LOWPOWER (1 << 5)
|
||||
#define ES8388_CONTROL2_OVERCURRENT_ON (1 << 6)
|
||||
#define ES8388_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7)
|
||||
|
||||
#define ES8388_CHIPPOWER 0x02
|
||||
#define ES8388_CHIPPOWER_DACVREF_OFF 0
|
||||
#define ES8388_CHIPPOWER_ADCVREF_OFF 1
|
||||
#define ES8388_CHIPPOWER_DACDLL_OFF 2
|
||||
#define ES8388_CHIPPOWER_ADCDLL_OFF 3
|
||||
#define ES8388_CHIPPOWER_DACSTM_RESET 4
|
||||
#define ES8388_CHIPPOWER_ADCSTM_RESET 5
|
||||
#define ES8388_CHIPPOWER_DACDIG_OFF 6
|
||||
#define ES8388_CHIPPOWER_ADCDIG_OFF 7
|
||||
|
||||
#define ES8388_ADCPOWER 0x03
|
||||
#define ES8388_ADCPOWER_INT1_LOWPOWER 0
|
||||
#define ES8388_ADCPOWER_FLASH_ADC_LOWPOWER 1
|
||||
#define ES8388_ADCPOWER_ADC_BIAS_GEN_OFF 2
|
||||
#define ES8388_ADCPOWER_MIC_BIAS_OFF 3
|
||||
#define ES8388_ADCPOWER_ADCR_OFF 4
|
||||
#define ES8388_ADCPOWER_ADCL_OFF 5
|
||||
#define ES8388_ADCPOWER_AINR_OFF 6
|
||||
#define ES8388_ADCPOWER_AINL_OFF 7
|
||||
|
||||
#define ES8388_DACPOWER 0x04
|
||||
#define ES8388_DACPOWER_OUT3_ON 0
|
||||
#define ES8388_DACPOWER_MONO_ON 1
|
||||
#define ES8388_DACPOWER_ROUT2_ON 2
|
||||
#define ES8388_DACPOWER_LOUT2_ON 3
|
||||
#define ES8388_DACPOWER_ROUT1_ON 4
|
||||
#define ES8388_DACPOWER_LOUT1_ON 5
|
||||
#define ES8388_DACPOWER_RDAC_OFF 6
|
||||
#define ES8388_DACPOWER_LDAC_OFF 7
|
||||
|
||||
#define ES8388_CHIPLOPOW1 0x05
|
||||
#define ES8388_CHIPLOPOW2 0x06
|
||||
#define ES8388_ANAVOLMANAG 0x07
|
||||
|
||||
#define ES8388_MASTERMODE 0x08
|
||||
#define ES8388_MASTERMODE_BCLKDIV (0 << 0)
|
||||
#define ES8388_MASTERMODE_BCLK_INV (1 << 5)
|
||||
#define ES8388_MASTERMODE_MCLKDIV2 (1 << 6)
|
||||
#define ES8388_MASTERMODE_MSC (1 << 7)
|
||||
|
||||
#define ES8388_ADCCONTROL1 0x09
|
||||
#define ES8388_ADCCONTROL2 0x0a
|
||||
#define ES8388_ADCCONTROL3 0x0b
|
||||
|
||||
#define ES8388_ADCCONTROL4 0x0c
|
||||
#define ES8388_ADCCONTROL4_ADCFORMAT_MASK (3 << 0)
|
||||
#define ES8388_ADCCONTROL4_ADCFORMAT_I2S (0 << 0)
|
||||
#define ES8388_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0)
|
||||
#define ES8388_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0)
|
||||
#define ES8388_ADCCONTROL4_ADCFORMAT_PCM (3 << 0)
|
||||
#define ES8388_ADCCONTROL4_ADCWL_SHIFT 2
|
||||
#define ES8388_ADCCONTROL4_ADCWL_MASK (7 << 2)
|
||||
#define ES8388_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5)
|
||||
#define ES8388_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5)
|
||||
#define ES8388_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5)
|
||||
#define ES8388_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5)
|
||||
|
||||
#define ES8388_ADCCONTROL5 0x0d
|
||||
#define ES8388_ADCCONTROL5_RATEMASK (0x1f << 0)
|
||||
|
||||
#define ES8388_ADCCONTROL6 0x0e
|
||||
|
||||
#define ES8388_ADCCONTROL7 0x0f
|
||||
#define ES8388_ADCCONTROL7_ADC_MUTE (1 << 2)
|
||||
#define ES8388_ADCCONTROL7_ADC_LER (1 << 3)
|
||||
#define ES8388_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4)
|
||||
#define ES8388_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5)
|
||||
#define ES8388_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6)
|
||||
#define ES8388_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6)
|
||||
#define ES8388_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6)
|
||||
#define ES8388_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6)
|
||||
|
||||
#define ES8388_ADCCONTROL8 0x10
|
||||
#define ES8388_ADCCONTROL9 0x11
|
||||
#define ES8388_ADCCONTROL10 0x12
|
||||
#define ES8388_ADCCONTROL11 0x13
|
||||
#define ES8388_ADCCONTROL12 0x14
|
||||
#define ES8388_ADCCONTROL13 0x15
|
||||
#define ES8388_ADCCONTROL14 0x16
|
||||
|
||||
#define ES8388_DACCONTROL1 0x17
|
||||
#define ES8388_DACCONTROL1_DACFORMAT_MASK (3 << 1)
|
||||
#define ES8388_DACCONTROL1_DACFORMAT_I2S (0 << 1)
|
||||
#define ES8388_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
|
||||
#define ES8388_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
|
||||
#define ES8388_DACCONTROL1_DACFORMAT_PCM (3 << 1)
|
||||
#define ES8388_DACCONTROL1_DACWL_SHIFT 3
|
||||
#define ES8388_DACCONTROL1_DACWL_MASK (7 << 3)
|
||||
#define ES8388_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
|
||||
#define ES8388_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
|
||||
#define ES8388_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
|
||||
#define ES8388_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6)
|
||||
#define ES8388_DACCONTROL1_LRSWAP (1 << 7)
|
||||
|
||||
#define ES8388_DACCONTROL2 0x18
|
||||
#define ES8388_DACCONTROL2_RATEMASK (0x1f << 0)
|
||||
#define ES8388_DACCONTROL2_DOUBLESPEED (1 << 5)
|
||||
|
||||
#define ES8388_DACCONTROL3 0x19
|
||||
#define ES8388_DACCONTROL3_AUTOMUTE (1 << 2)
|
||||
#define ES8388_DACCONTROL3_DACMUTE (1 << 2)
|
||||
#define ES8388_DACCONTROL3_LEFTGAINVOL (1 << 3)
|
||||
#define ES8388_DACCONTROL3_DACZEROCROSS (1 << 4)
|
||||
#define ES8388_DACCONTROL3_DACSOFTRAMP (1 << 5)
|
||||
#define ES8388_DACCONTROL3_DACRAMPRATE (3 << 6)
|
||||
|
||||
#define ES8388_LDACVOL 0x1a
|
||||
#define ES8388_LDACVOL_MASK (0 << 0)
|
||||
#define ES8388_LDACVOL_MAX (0xc0)
|
||||
|
||||
#define ES8388_RDACVOL 0x1b
|
||||
#define ES8388_RDACVOL_MASK (0 << 0)
|
||||
#define ES8388_RDACVOL_MAX (0xc0)
|
||||
|
||||
#define ES8388_DACVOL_MAX (0xc0)
|
||||
|
||||
#define ES8388_DACCONTROL4 0x1a
|
||||
#define ES8388_DACCONTROL5 0x1b
|
||||
|
||||
#define ES8388_DACCONTROL6 0x1c
|
||||
#define ES8388_DACCONTROL6_CLICKFREE (1 << 3)
|
||||
#define ES8388_DACCONTROL6_DAC_INVR (1 << 4)
|
||||
#define ES8388_DACCONTROL6_DAC_INVL (1 << 5)
|
||||
#define ES8388_DACCONTROL6_DEEMPH_MASK (3 << 6)
|
||||
#define ES8388_DACCONTROL6_DEEMPH_OFF (0 << 6)
|
||||
#define ES8388_DACCONTROL6_DEEMPH_32k (1 << 6)
|
||||
#define ES8388_DACCONTROL6_DEEMPH_44_1k (2 << 6)
|
||||
#define ES8388_DACCONTROL6_DEEMPH_48k (3 << 6)
|
||||
|
||||
#define ES8388_DACCONTROL7 0x1d
|
||||
#define ES8388_DACCONTROL7_VPP_SCALE_3p5 (0 << 0)
|
||||
#define ES8388_DACCONTROL7_VPP_SCALE_4p0 (1 << 0)
|
||||
#define ES8388_DACCONTROL7_VPP_SCALE_3p0 (2 << 0)
|
||||
#define ES8388_DACCONTROL7_VPP_SCALE_2p5 (3 << 0)
|
||||
#define ES8388_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */
|
||||
#define ES8388_DACCONTROL7_MONO (1 << 5)
|
||||
#define ES8388_DACCONTROL7_ZEROR (1 << 6)
|
||||
#define ES8388_DACCONTROL7_ZEROL (1 << 7)
|
||||
|
||||
/* Shelving filter */
|
||||
#define ES8388_DACCONTROL8 0x1e
|
||||
#define ES8388_DACCONTROL9 0x1f
|
||||
#define ES8388_DACCONTROL10 0x20
|
||||
#define ES8388_DACCONTROL11 0x21
|
||||
#define ES8388_DACCONTROL12 0x22
|
||||
#define ES8388_DACCONTROL13 0x23
|
||||
#define ES8388_DACCONTROL14 0x24
|
||||
#define ES8388_DACCONTROL15 0x25
|
||||
|
||||
#define ES8388_DACCONTROL16 0x26
|
||||
#define ES8388_DACCONTROL16_RMIXSEL_RIN1 (0 << 0)
|
||||
#define ES8388_DACCONTROL16_RMIXSEL_RIN2 (1 << 0)
|
||||
#define ES8388_DACCONTROL16_RMIXSEL_RIN3 (2 << 0)
|
||||
#define ES8388_DACCONTROL16_RMIXSEL_RADC (3 << 0)
|
||||
#define ES8388_DACCONTROL16_LMIXSEL_LIN1 (0 << 3)
|
||||
#define ES8388_DACCONTROL16_LMIXSEL_LIN2 (1 << 3)
|
||||
#define ES8388_DACCONTROL16_LMIXSEL_LIN3 (2 << 3)
|
||||
#define ES8388_DACCONTROL16_LMIXSEL_LADC (3 << 3)
|
||||
|
||||
#define ES8388_DACCONTROL17 0x27
|
||||
#define ES8388_DACCONTROL17_LI2LOVOL (7 << 3)
|
||||
#define ES8388_DACCONTROL17_LI2LO (1 << 6)
|
||||
#define ES8388_DACCONTROL17_LD2LO (1 << 7)
|
||||
|
||||
#define ES8388_DACCONTROL18 0x28
|
||||
#define ES8388_DACCONTROL18_RI2LOVOL (7 << 3)
|
||||
#define ES8388_DACCONTROL18_RI2LO (1 << 6)
|
||||
#define ES8388_DACCONTROL18_RD2LO (1 << 7)
|
||||
|
||||
#define ES8388_DACCONTROL19 0x29
|
||||
#define ES8388_DACCONTROL19_LI2ROVOL (7 << 3)
|
||||
#define ES8388_DACCONTROL19_LI2RO (1 << 6)
|
||||
#define ES8388_DACCONTROL19_LD2RO (1 << 7)
|
||||
|
||||
#define ES8388_DACCONTROL20 0x2a
|
||||
#define ES8388_DACCONTROL20_RI2ROVOL (7 << 3)
|
||||
#define ES8388_DACCONTROL20_RI2RO (1 << 6)
|
||||
#define ES8388_DACCONTROL20_RD2RO (1 << 7)
|
||||
|
||||
#define ES8388_DACCONTROL21 0x2b
|
||||
#define ES8388_DACCONTROL21_LI2MOVOL (7 << 3)
|
||||
#define ES8388_DACCONTROL21_LI2MO (1 << 6)
|
||||
#define ES8388_DACCONTROL21_LD2MO (1 << 7)
|
||||
|
||||
#define ES8388_DACCONTROL22 0x2c
|
||||
#define ES8388_DACCONTROL22_RI2MOVOL (7 << 3)
|
||||
#define ES8388_DACCONTROL22_RI2MO (1 << 6)
|
||||
#define ES8388_DACCONTROL22_RD2MO (1 << 7)
|
||||
|
||||
#define ES8388_DACCONTROL23 0x2d
|
||||
#define ES8388_DACCONTROL23_MOUTINV (1 << 1)
|
||||
#define ES8388_DACCONTROL23_HPSWPOL (1 << 2)
|
||||
#define ES8388_DACCONTROL23_HPSWEN (1 << 3)
|
||||
#define ES8388_DACCONTROL23_VROI_1p5k (0 << 4)
|
||||
#define ES8388_DACCONTROL23_VROI_40k (1 << 4)
|
||||
#define ES8388_DACCONTROL23_OUT3_VREF (0 << 5)
|
||||
#define ES8388_DACCONTROL23_OUT3_ROUT1 (1 << 5)
|
||||
#define ES8388_DACCONTROL23_OUT3_MONOOUT (2 << 5)
|
||||
#define ES8388_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5)
|
||||
#define ES8388_DACCONTROL23_ROUT2INV (1 << 7)
|
||||
|
||||
/* LOUT1 Amplifier */
|
||||
#define ES8388_LOUT1VOL 0x2e
|
||||
#define ES8388_LOUT1VOL_MASK (0 << 5)
|
||||
#define ES8388_LOUT1VOL_MAX (0x24)
|
||||
|
||||
/* ROUT1 Amplifier */
|
||||
#define ES8388_ROUT1VOL 0x2f
|
||||
#define ES8388_ROUT1VOL_MASK (0 << 5)
|
||||
#define ES8388_ROUT1VOL_MAX (0x24)
|
||||
|
||||
#define ES8388_OUT1VOL_MAX (0x24)
|
||||
|
||||
/* LOUT2 Amplifier */
|
||||
#define ES8388_LOUT2VOL 0x30
|
||||
#define ES8388_LOUT2VOL_MASK (0 << 5)
|
||||
#define ES8388_LOUT2VOL_MAX (0x24)
|
||||
|
||||
/* ROUT2 Amplifier */
|
||||
#define ES8388_ROUT2VOL 0x31
|
||||
#define ES8388_ROUT2VOL_MASK (0 << 5)
|
||||
#define ES8388_ROUT2VOL_MAX (0x24)
|
||||
|
||||
#define ES8388_OUT2VOL_MAX (0x24)
|
||||
|
||||
/* Mono Out Amplifier */
|
||||
#define ES8388_MONOOUTVOL 0x32
|
||||
#define ES8388_MONOOUTVOL_MASK (0 << 5)
|
||||
#define ES8388_MONOOUTVOL_MAX (0x24)
|
||||
|
||||
#define ES8388_DACCONTROL29 0x33
|
||||
#define ES8388_DACCONTROL30 0x34
|
||||
|
||||
#define ES8388_SYSCLK 0
|
||||
|
||||
#define ES8388_REG_MAX 0x35
|
||||
|
||||
#define ES8388_1536FS 1536
|
||||
#define ES8388_1024FS 1024
|
||||
#define ES8388_768FS 768
|
||||
#define ES8388_512FS 512
|
||||
#define ES8388_384FS 384
|
||||
#define ES8388_256FS 256
|
||||
#define ES8388_128FS 128
|
||||
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
config SND_SOC_PHYTIUM_I2S
|
||||
bool "Phytium I2S Device Driver"
|
||||
help
|
||||
Say Y or M if you want to add support for I2S driver for
|
||||
Phytium I2S device . The device supports 2 channels each
|
||||
for play and record.
|
||||
|
||||
config SND_PMDK_ES8388
|
||||
tristate "Phytium machine support with ES8388"
|
||||
depends on I2C && SND_SOC_PHYTIUM_I2S
|
||||
select SND_SOC_ES8388
|
||||
help
|
||||
Say Y if you want to add Phytium machine support for
|
||||
ES8388 codecs.
|
||||
|
||||
config SND_PMDK_ES8336
|
||||
tristate "Phytium machine support with ES8336"
|
||||
depends on I2C && SND_SOC_PHYTIUM_I2S
|
||||
select SND_SOC_ES8336
|
||||
help
|
||||
Say Y if you want to add Phytium machine support for
|
||||
ES8336 codecs.
|
||||
|
||||
config SND_PMDK_DP
|
||||
tristate "Phytium machine support with DP"
|
||||
depends on I2C && SND_SOC_PHYTIUM_I2S
|
||||
select SND_SOC_HDMI_CODEC
|
||||
help
|
||||
Say Y if you want to add Phytium machine support for
|
||||
Displayport.
|
|
@ -0,0 +1,13 @@
|
|||
# PHYTIUM Platform Support
|
||||
|
||||
snd-soc-phytium-i2s-objs :=phytium_i2s.o
|
||||
obj-$(CONFIG_SND_SOC_PHYTIUM_I2S) += snd-soc-phytium-i2s.o
|
||||
|
||||
snd-soc-pmdk-es8388-objs :=pmdk_es8388.o
|
||||
obj-$(CONFIG_SND_PMDK_ES8388) += snd-soc-pmdk-es8388.o
|
||||
|
||||
snd-soc-pmdk-es8336-objs :=pmdk_es8336.o
|
||||
obj-$(CONFIG_SND_PMDK_ES8336) += snd-soc-pmdk-es8336.o
|
||||
|
||||
snd-soc-pmdk-dp-objs :=pmdk_dp.o
|
||||
obj-$(CONFIG_SND_PMDK_DP) += snd-soc-pmdk-dp.o
|
|
@ -0,0 +1,328 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2020-2023, Phytium Technology Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __PHYTIUM_I2S_LOCAL_H
|
||||
#define __PHYTIUM_I2S_LOCAL_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/types.h>
|
||||
#include <sound/pcm.h>
|
||||
|
||||
/* I2S clk setting*/
|
||||
#define CLK_CFG0 0xc00
|
||||
#define CLK_CFG1 0xc04
|
||||
|
||||
/* common register for all channel */
|
||||
#define I2S_IER 0x000
|
||||
#define IRER 0x004
|
||||
#define ITER 0x008
|
||||
#define CER 0x00C
|
||||
|
||||
#define RXFFR 0x014
|
||||
#define TXFFR 0x018
|
||||
|
||||
/* Interrupt status register fields */
|
||||
#define ISR_TXFO BIT(5)
|
||||
#define ISR_TXFE BIT(4)
|
||||
#define ISR_RXFO BIT(1)
|
||||
#define ISR_RXDA BIT(0)
|
||||
|
||||
/* I2STxRxRegisters for all channels */
|
||||
#define LRBR_LTHR(x) (0x40 * x + 0x020)
|
||||
#define RRBR_RTHR(x) (0x40 * x + 0x024)
|
||||
#define RER(x) (0x40 * x + 0x028)
|
||||
|
||||
#define RCR(x) (0x40 * x + 0x030)
|
||||
|
||||
#define ISR(x) (0x40 * x + 0x038)
|
||||
#define IMR(x) (0x40 * x + 0x03C)
|
||||
#define ROR(x) (0x40 * x + 0x040)
|
||||
#define TOR(x) (0x40 * x + 0x044)
|
||||
#define RFCR(x) (0x40 * x + 0x048)
|
||||
#define TFCR(x) (0x40 * x + 0x04C)
|
||||
#define RFF(x) (0x40 * x + 0x050)
|
||||
#define TFF(x) (0x40 * x + 0x054)
|
||||
|
||||
/*enable txd and rxd block channel0~3 */
|
||||
#define TER(x) (0x40 * x + 0x02C)
|
||||
#define CCR 0x010
|
||||
#define TCR(x) (0x40 * x + 0x034)
|
||||
|
||||
|
||||
/* I2SCOMPRegisters */
|
||||
#define I2S_COMP_PARAM_2 0x01F0
|
||||
#define I2S_COMP_PARAM_1 0x01F4
|
||||
#define I2S_COMP_VERSION 0x01F8
|
||||
#define I2S_COMP_TYPE 0x01FC
|
||||
|
||||
/***I2S AND DMA***/
|
||||
|
||||
#define DMA_GCAP 0x0024
|
||||
|
||||
#define DMA_CHAL_CONFG1 0x0028
|
||||
|
||||
#define DMA_CHAL_CONFG0 0x0004
|
||||
#define DMA_MASK_INT 0x000c
|
||||
#define DMA_BDLPU(x) (0x40 * x + 0x0040)
|
||||
#define DMA_BDLPL(x) (0x40 * x + 0x0044)
|
||||
#define DMA_CHALX_DEV_ADDR(x) (0x40 * x + 0x0048)
|
||||
#define DMA_CHALX_CBL(x) (0x40 * x + 0x0054)
|
||||
#define DMA_CHALX_LVI(x) (0x40 * x + 0x004c)
|
||||
|
||||
#define DMA_CHALX_DSIZE(x) (0x40 * x + 0x0064)
|
||||
#define DMA_CHALX_DLENTH(x) (0x40 * x + 0x0068)
|
||||
#define DMA_CHALX_CTL(x) (0x40 * x + 0x0058)
|
||||
|
||||
|
||||
#define DMA_CTL 0x0000
|
||||
|
||||
#define DMA_LPIB(x) (0x40 * x + 0x0050)
|
||||
|
||||
#define DMA_STS 0x0008
|
||||
|
||||
/****************/
|
||||
|
||||
|
||||
/* max number of fragments - we may use more if allocating more pages for BDL */
|
||||
#define BDL_SIZE 4096
|
||||
#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
|
||||
|
||||
/*
|
||||
* Component parameter register fields - define the I2S block's
|
||||
* configuration.
|
||||
*/
|
||||
#define COMP1_TX_WORDSIZE_3(r) (((r) & GENMASK(27, 25)) >> 25)
|
||||
#define COMP1_TX_WORDSIZE_2(r) (((r) & GENMASK(24, 22)) >> 22)
|
||||
#define COMP1_TX_WORDSIZE_1(r) (((r) & GENMASK(21, 19)) >> 19)
|
||||
#define COMP1_TX_WORDSIZE_0(r) (((r) & GENMASK(18, 16)) >> 16)
|
||||
#define COMP1_TX_CHANNELS(r) (((r) & GENMASK(10, 9)) >> 9)
|
||||
#define COMP1_RX_CHANNELS(r) (((r) & GENMASK(8, 7)) >> 7)
|
||||
#define COMP1_RX_ENABLED(r) (((r) & BIT(6)) >> 6)
|
||||
#define COMP1_TX_ENABLED(r) (((r) & BIT(5)) >> 5)
|
||||
#define COMP1_MODE_EN(r) (((r) & BIT(4)) >> 4)
|
||||
#define COMP1_FIFO_DEPTH_GLOBAL(r) (((r) & GENMASK(3, 2)) >> 2)
|
||||
#define COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0)
|
||||
|
||||
#define COMP2_RX_WORDSIZE_3(r) (((r) & GENMASK(12, 10)) >> 10)
|
||||
#define COMP2_RX_WORDSIZE_2(r) (((r) & GENMASK(9, 7)) >> 7)
|
||||
#define COMP2_RX_WORDSIZE_1(r) (((r) & GENMASK(5, 3)) >> 3)
|
||||
#define COMP2_RX_WORDSIZE_0(r) (((r) & GENMASK(2, 0)) >> 0)
|
||||
|
||||
/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
|
||||
#define COMP_MAX_WORDSIZE (1 << 3)
|
||||
#define COMP_MAX_DATA_WIDTH (1 << 2)
|
||||
|
||||
#define MAX_CHANNEL_NUM 8
|
||||
#define MIN_CHANNEL_NUM 2
|
||||
|
||||
#define azx_bus(chip) (&(chip)->bus.core)
|
||||
#define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core)
|
||||
|
||||
#define I2S_UNSOL_QUEUE_SIZE 64
|
||||
#define I2S_MAX_CODECS 8 /* limit by controller side */
|
||||
|
||||
|
||||
#define azx_stream(dev) (&(dev)->core)
|
||||
|
||||
struct i2s_clk_config_data {
|
||||
int chan_nr;
|
||||
u32 data_width;
|
||||
u32 sample_rate;
|
||||
};
|
||||
|
||||
struct i2sc_bus {
|
||||
struct device *dev;
|
||||
const struct i2s_bus_ops *ops;
|
||||
const struct i2s_io_ops *io_ops;
|
||||
const struct i2s_ext_bus_ops *ext_ops;
|
||||
|
||||
/* h/w resources */
|
||||
unsigned long addr;
|
||||
void __iomem *remap_addr;
|
||||
int irq;
|
||||
|
||||
/* codec linked list */
|
||||
struct list_head codec_list;
|
||||
unsigned int num_codecs;
|
||||
|
||||
unsigned int unsol_rp, unsol_wp;
|
||||
struct work_struct unsol_work;
|
||||
|
||||
struct snd_dma_buffer bdl0;
|
||||
struct snd_dma_buffer bdl1;
|
||||
|
||||
/* i2s_stream linked list */
|
||||
struct list_head stream_list;
|
||||
|
||||
bool reverse_assign; /* assign devices in reverse order */
|
||||
|
||||
int bdl_pos_adj; /* BDL position adjustment */
|
||||
|
||||
/* locks */
|
||||
spinlock_t reg_lock;
|
||||
};
|
||||
|
||||
struct i2s_bus {
|
||||
struct i2sc_bus core;
|
||||
|
||||
struct snd_card *card;
|
||||
|
||||
struct pci_dev *pci;
|
||||
|
||||
struct mutex prepare_mutex;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* i2s stream
|
||||
*/
|
||||
struct i2s_stream {
|
||||
struct i2sc_bus *bus;
|
||||
struct snd_dma_buffer bdl; /* BDL buffer */
|
||||
__le32 *posbuf; /* position buffer pointer */
|
||||
int direction; /* playback / capture (SNDRV_PCM_STREAM_*) */
|
||||
|
||||
unsigned int bufsize; /* size of the play buffer in bytes */
|
||||
unsigned int period_bytes; /* size of the period in bytes */
|
||||
unsigned int frags; /* number for period in the play buffer */
|
||||
unsigned int fifo_size; /* FIFO size */
|
||||
|
||||
void __iomem *sd_addr; /* stream descriptor pointer */
|
||||
|
||||
u32 sd_int_sta_mask; /* stream int status mask */
|
||||
|
||||
/* pcm support */
|
||||
struct snd_pcm_substream *substream; /* assigned substream,
|
||||
* set in PCM open
|
||||
*/
|
||||
unsigned int format_val; /* format value to be set in the
|
||||
* controller and the codec
|
||||
*/
|
||||
unsigned char stream_tag; /* assigned stream */
|
||||
unsigned char index; /* stream index */
|
||||
int assigned_key; /* last device# key assigned to */
|
||||
|
||||
bool opened;
|
||||
bool running;
|
||||
bool prepared;
|
||||
bool no_period_wakeup;
|
||||
|
||||
int delay_negative_threshold;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct azx_dev {
|
||||
struct i2s_stream core;
|
||||
unsigned int irq_pending:1;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* PCM setup */
|
||||
static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return substream->runtime->private_data;
|
||||
}
|
||||
|
||||
|
||||
#define AZX_MAX_CODECS HDA_MAX_CODECS
|
||||
#define AZX_DEFAULT_CODECS 4
|
||||
|
||||
#define stream_to_azx_dev(s) container_of(s, struct azx_dev, core)
|
||||
|
||||
struct azx;
|
||||
|
||||
struct i2s_controller_ops {
|
||||
int (*substream_alloc_pages)(struct azx *chip,
|
||||
struct snd_pcm_substream *substream,
|
||||
size_t size);
|
||||
int (*substream_free_pages)(struct azx *chip,
|
||||
struct snd_pcm_substream *substream);
|
||||
int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
|
||||
};
|
||||
|
||||
struct i2s_io_ops {
|
||||
int (*dma_alloc_pages)(struct i2sc_bus *bus, int type, size_t size,
|
||||
struct snd_dma_buffer *buf);
|
||||
void (*dma_free_pages)(struct i2sc_bus *bus,
|
||||
struct snd_dma_buffer *buf);
|
||||
};
|
||||
|
||||
struct azx {
|
||||
struct i2s_bus bus;
|
||||
|
||||
struct snd_card *card;
|
||||
struct pci_dev *pci;
|
||||
int dev_index;
|
||||
|
||||
int playback_streams;
|
||||
int playback_index_offset;
|
||||
int capture_streams;
|
||||
int capture_index_offset;
|
||||
int num_streams;
|
||||
|
||||
/* Register interaction. */
|
||||
const struct i2s_controller_ops *ops;
|
||||
|
||||
/* locks */
|
||||
struct mutex open_mutex; /* Prevents concurrent open/close operations */
|
||||
|
||||
/* PCM */
|
||||
struct list_head pcm_list; /* azx_pcm list */
|
||||
|
||||
/* flags */
|
||||
int bdl_pos_adj;
|
||||
unsigned int running:1;
|
||||
unsigned int region_requested:1;
|
||||
unsigned int disabled:1;
|
||||
};
|
||||
struct i2s_phytium {
|
||||
struct azx chip;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct device *dev;
|
||||
struct device *pdev;
|
||||
u32 paddr;
|
||||
void __iomem *regs;
|
||||
void __iomem *regs_db;
|
||||
int irq_id;
|
||||
|
||||
/* for pending irqs */
|
||||
struct work_struct irq_pending_work;
|
||||
|
||||
/* sync probing */
|
||||
struct completion probe_wait;
|
||||
struct work_struct probe_work;
|
||||
|
||||
/* extra flags */
|
||||
unsigned int pcie:1;
|
||||
unsigned int irq_pending_warned:1;
|
||||
unsigned int probe_continued:1;
|
||||
unsigned int i2s_dp:1;
|
||||
|
||||
unsigned int i2s_reg_comp1;
|
||||
unsigned int i2s_reg_comp2;
|
||||
struct clk *clk;
|
||||
unsigned int capability;
|
||||
unsigned int quirks;
|
||||
u32 fifo_th;
|
||||
int active;
|
||||
u32 xfer_resolution;
|
||||
u32 ccr;
|
||||
u32 clk_base;
|
||||
|
||||
struct i2s_clk_config_data config;
|
||||
|
||||
/*azx_dev*/
|
||||
struct i2s_stream core;
|
||||
};
|
||||
|
||||
#define azx_alloc_stream_pages(chip) \
|
||||
snd_i2s_bus_alloc_stream_pages(azx_bus(chip))
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,234 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2021-2023, Phytium Technology Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/jack.h>
|
||||
|
||||
struct pmdk_dp_private {
|
||||
struct snd_soc_jack jack0;
|
||||
struct snd_soc_jack jack1;
|
||||
struct snd_soc_jack jack2;
|
||||
};
|
||||
|
||||
/* PMDK widgets */
|
||||
static const struct snd_soc_dapm_widget pmdk_dp_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("DP", NULL),
|
||||
};
|
||||
|
||||
/* PMDK control */
|
||||
static const struct snd_kcontrol_new pmdk_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("DP"),
|
||||
};
|
||||
|
||||
/* PMDK connections */
|
||||
static const struct snd_soc_dapm_route pmdk_dp_audio_map[] = {
|
||||
{"DP", NULL, "TX"},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_pin dp0_pins[] = {
|
||||
{
|
||||
.pin = "HDMI/DP,pcm=0",
|
||||
.mask = SND_JACK_LINEOUT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_pin dp1_pins[] = {
|
||||
{
|
||||
.pin = "HDMI/DP,pcm=1",
|
||||
.mask = SND_JACK_LINEOUT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_pin dp2_pins[] = {
|
||||
{
|
||||
.pin = "HDMI/DP,pcm=2",
|
||||
.mask = SND_JACK_LINEOUT,
|
||||
},
|
||||
};
|
||||
|
||||
#define SMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
|
||||
SND_SOC_DAIFMT_CBS_CFS)
|
||||
|
||||
static int pmdk_dp0_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
struct snd_soc_card *card = runtime->card;
|
||||
struct pmdk_dp_private *priv = snd_soc_card_get_drvdata(card);
|
||||
struct snd_soc_component *component = runtime->codec_dai->component;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_card_jack_new(card, "HDMI/DP,pcm=0",
|
||||
SND_JACK_LINEOUT,
|
||||
&priv->jack0, dp0_pins,
|
||||
ARRAY_SIZE(dp0_pins));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "Jack creation failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
snd_soc_component_set_jack(component, &priv->jack0, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pmdk_dp1_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
struct snd_soc_card *card = runtime->card;
|
||||
struct pmdk_dp_private *priv = snd_soc_card_get_drvdata(card);
|
||||
struct snd_soc_component *component = runtime->codec_dai->component;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_card_jack_new(card, "HDMI/DP,pcm=1",
|
||||
SND_JACK_LINEOUT,
|
||||
&priv->jack1, dp1_pins,
|
||||
ARRAY_SIZE(dp1_pins));
|
||||
|
||||
if (ret) {
|
||||
dev_err(card->dev, "Jack creation failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
snd_soc_component_set_jack(component, &priv->jack1, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pmdk_dp2_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
struct snd_soc_card *card = runtime->card;
|
||||
struct pmdk_dp_private *priv = snd_soc_card_get_drvdata(card);
|
||||
struct snd_soc_component *component = runtime->codec_dai->component;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_card_jack_new(card, "HDMI/DP,pcm=2",
|
||||
SND_JACK_LINEOUT,
|
||||
&priv->jack2, dp2_pins,
|
||||
ARRAY_SIZE(dp2_pins));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "Jack creation failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
snd_soc_component_set_jack(component, &priv->jack2, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SND_SOC_DAILINK_DEFS(pmdk_dp0_dai,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-dp0")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("hdmi-audio-codec.1346918656", "i2s-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
|
||||
|
||||
SND_SOC_DAILINK_DEFS(pmdk_dp1_dai,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-dp1")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("hdmi-audio-codec.1346918657", "i2s-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
|
||||
|
||||
SND_SOC_DAILINK_DEFS(pmdk_dp2_dai,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-dp2")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("hdmi-audio-codec.1346918658", "i2s-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
|
||||
|
||||
static struct snd_soc_dai_link pmdk_dai0 = {
|
||||
.name = "Phytium dp0-audio",
|
||||
.stream_name = "Playback",
|
||||
.dai_fmt = SMDK_DAI_FMT,
|
||||
.init = pmdk_dp0_init,
|
||||
SND_SOC_DAILINK_REG(pmdk_dp0_dai),
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link pmdk_dai1 = {
|
||||
.name = "Phytium dp1-audio",
|
||||
.stream_name = "Playback",
|
||||
.dai_fmt = SMDK_DAI_FMT,
|
||||
.init = pmdk_dp1_init,
|
||||
SND_SOC_DAILINK_REG(pmdk_dp1_dai),
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link pmdk_dai2 = {
|
||||
.name = "Phytium dp2-audio",
|
||||
.stream_name = "Playback",
|
||||
.dai_fmt = SMDK_DAI_FMT,
|
||||
.init = pmdk_dp2_init,
|
||||
SND_SOC_DAILINK_REG(pmdk_dp2_dai),
|
||||
};
|
||||
|
||||
static struct snd_soc_card pmdk = {
|
||||
.name = "PMDK-I2S",
|
||||
.owner = THIS_MODULE,
|
||||
|
||||
.dapm_widgets = pmdk_dp_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(pmdk_dp_dapm_widgets),
|
||||
.controls = pmdk_controls,
|
||||
.num_controls = ARRAY_SIZE(pmdk_controls),
|
||||
.dapm_routes = pmdk_dp_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(pmdk_dp_audio_map),
|
||||
};
|
||||
|
||||
static int pmdk_sound_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &pmdk;
|
||||
struct pmdk_dp_private *priv;
|
||||
struct snd_soc_dai_link *pmdk_dai;
|
||||
int num_dp = 2;
|
||||
card->dev = &pdev->dev;
|
||||
device_property_read_u32(&pdev->dev, "num-dp", &num_dp);
|
||||
pmdk_dai = devm_kzalloc(&pdev->dev, num_dp * sizeof(*pmdk_dai), GFP_KERNEL);
|
||||
if (!pmdk_dai)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (num_dp) {
|
||||
case 1:
|
||||
pmdk_dai[0] = pmdk_dai0;
|
||||
break;
|
||||
case 2:
|
||||
pmdk_dai[0] = pmdk_dai0;
|
||||
pmdk_dai[1] = pmdk_dai1;
|
||||
break;
|
||||
case 3:
|
||||
pmdk_dai[0] = pmdk_dai0;
|
||||
pmdk_dai[1] = pmdk_dai1;
|
||||
pmdk_dai[2] = pmdk_dai2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
card->dai_link = pmdk_dai;
|
||||
card->num_links = num_dp;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_soc_card_set_drvdata(card, priv);
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, card);
|
||||
}
|
||||
|
||||
static const struct of_device_id pmdk_sound_of_match[] = {
|
||||
{ .compatible = "phytium,pmdk-dp",},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pmdk_sound_of_match);
|
||||
|
||||
static const struct acpi_device_id pmdk_sound_acpi_match[] = {
|
||||
{ "PHYT8006", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pmdk_sound_acpi_match);
|
||||
|
||||
static struct platform_driver pmdk_sound_driver = {
|
||||
.probe = pmdk_sound_probe,
|
||||
.driver = {
|
||||
.name = "pmdk_dp",
|
||||
.acpi_match_table = pmdk_sound_acpi_match,
|
||||
.of_match_table = pmdk_sound_of_match,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &snd_soc_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(pmdk_sound_driver);
|
||||
|
||||
MODULE_AUTHOR("Zhang Yiqun<zhangyiqun@phytium.com.cn>");
|
||||
MODULE_DESCRIPTION("ALSA SoC PMDK DP");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,96 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2021-2023, Phytium Techonology Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
/* PMDK widgets */
|
||||
static const struct snd_soc_dapm_widget pmdk_es8336_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("HP", NULL),
|
||||
SND_SOC_DAPM_MIC("Int Mic", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic In", NULL),
|
||||
};
|
||||
|
||||
/* PMDK control */
|
||||
static const struct snd_kcontrol_new pmdk_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("HP"),
|
||||
SOC_DAPM_PIN_SWITCH("Int Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Mic In"),
|
||||
};
|
||||
|
||||
/* PMDK connections */
|
||||
static const struct snd_soc_dapm_route pmdk_es8336_audio_map[] = {
|
||||
{"DMIC", NULL, "Int Mic"},
|
||||
{"MIC1", NULL, "Mic In"},
|
||||
{"MIC2", NULL, "Mic In"},
|
||||
|
||||
{"HP", NULL, "HPOL"},
|
||||
{"HP", NULL, "HPOR"},
|
||||
};
|
||||
|
||||
#define PMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
|
||||
SND_SOC_DAIFMT_CBS_CFS)
|
||||
|
||||
SND_SOC_DAILINK_DEFS(pmdk_es8366,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-lsd")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "es8336-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
|
||||
|
||||
static struct snd_soc_dai_link pmdk_dai[] = {
|
||||
{
|
||||
.name = "ES8336 HIFI",
|
||||
.stream_name = "ES8336 HIFI",
|
||||
.dai_fmt = PMDK_DAI_FMT,
|
||||
SND_SOC_DAILINK_REG(pmdk_es8366),
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card pmdk = {
|
||||
.name = "PMDK-I2S",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = pmdk_dai,
|
||||
.num_links = ARRAY_SIZE(pmdk_dai),
|
||||
|
||||
.dapm_widgets = pmdk_es8336_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(pmdk_es8336_dapm_widgets),
|
||||
.controls = pmdk_controls,
|
||||
.num_controls = ARRAY_SIZE(pmdk_controls),
|
||||
.dapm_routes = pmdk_es8336_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(pmdk_es8336_audio_map),
|
||||
};
|
||||
|
||||
static int pmdk_sound_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &pmdk;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
card->dev = dev;
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, card);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id pmdk_sound_acpi_match[] = {
|
||||
{ "PHYT8005", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pmdk_sound_acpi_match);
|
||||
|
||||
static struct platform_driver pmdk_sound_driver = {
|
||||
.probe = pmdk_sound_probe,
|
||||
.driver = {
|
||||
.name = "pmdk_es8336",
|
||||
.acpi_match_table = pmdk_sound_acpi_match,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &snd_soc_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(pmdk_sound_driver);
|
||||
MODULE_AUTHOR("Zhang Yiqun <zhangyiqun@phytium.com.cn>");
|
||||
MODULE_DESCRIPTION("ALSA SoC PMDK ES8336");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,169 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2021-2023, Phytium Techonology Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/jack.h>
|
||||
|
||||
static struct snd_soc_jack hs_jack;
|
||||
|
||||
/* Headset jack detection DAPM pins */
|
||||
static struct snd_soc_jack_pin hs_jack_pins[] = {
|
||||
{
|
||||
.pin = "FrontIn",
|
||||
.mask = SND_JACK_MICROPHONE,
|
||||
},
|
||||
{
|
||||
.pin = "RearIn",
|
||||
.mask = SND_JACK_MICROPHONE,
|
||||
.invert = 1
|
||||
},
|
||||
{
|
||||
.pin = "Front",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
{
|
||||
.pin = "Rear",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
.invert = 1
|
||||
},
|
||||
};
|
||||
|
||||
/* Headset jack detection gpios */
|
||||
static struct snd_soc_jack_gpio hs_jack_gpios[] = {
|
||||
{
|
||||
.name = "det",
|
||||
.report = SND_JACK_HEADSET,
|
||||
.debounce_time = 200,
|
||||
.invert = 1,
|
||||
},
|
||||
};
|
||||
|
||||
/* PMDK widgets */
|
||||
static const struct snd_soc_dapm_widget pmdk_es8388_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Front", NULL),
|
||||
SND_SOC_DAPM_HP("Rear", NULL),
|
||||
|
||||
SND_SOC_DAPM_MIC("FrontIn", NULL),
|
||||
SND_SOC_DAPM_MIC("RearIn", NULL),
|
||||
};
|
||||
|
||||
/* PMDK control */
|
||||
static const struct snd_kcontrol_new pmdk_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Front"),
|
||||
SOC_DAPM_PIN_SWITCH("Rear"),
|
||||
SOC_DAPM_PIN_SWITCH("FrontIn"),
|
||||
SOC_DAPM_PIN_SWITCH("RearIn"),
|
||||
};
|
||||
|
||||
/* PMDK connections */
|
||||
static const struct snd_soc_dapm_route pmdk_es8388_audio_map[] = {
|
||||
{"LINPUT1", NULL, "FrontIn"},
|
||||
{"RINPUT1", NULL, "FrontIn"},
|
||||
|
||||
{"LINPUT2", NULL, "RearIn"},
|
||||
{"RINPUT2", NULL, "RearIn"},
|
||||
|
||||
{"Front", NULL, "LOUT1"},
|
||||
{"Front", NULL, "ROUT1"},
|
||||
|
||||
{"Rear", NULL, "LOUT2"},
|
||||
{"Rear", NULL, "ROUT2"},
|
||||
};
|
||||
|
||||
static int pmdk_es8388_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Jack detection API stuff */
|
||||
ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
|
||||
&hs_jack, hs_jack_pins,
|
||||
ARRAY_SIZE(hs_jack_pins));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
|
||||
hs_jack_gpios);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define PMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
|
||||
SND_SOC_DAIFMT_CBS_CFS)
|
||||
|
||||
SND_SOC_DAILINK_DEFS(pmdk_es8388,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-lsd")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8388:00", "es8388-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
|
||||
|
||||
static struct snd_soc_dai_link pmdk_dai[] = {
|
||||
{
|
||||
.name = "ES8388 HIFI",
|
||||
.stream_name = "ES8388 HIFI",
|
||||
.dai_fmt = PMDK_DAI_FMT,
|
||||
.init = pmdk_es8388_init,
|
||||
SND_SOC_DAILINK_REG(pmdk_es8388),
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card pmdk = {
|
||||
.name = "PMDK-I2S",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = pmdk_dai,
|
||||
.num_links = ARRAY_SIZE(pmdk_dai),
|
||||
|
||||
.dapm_widgets = pmdk_es8388_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(pmdk_es8388_dapm_widgets),
|
||||
.controls = pmdk_controls,
|
||||
.num_controls = ARRAY_SIZE(pmdk_controls),
|
||||
.dapm_routes = pmdk_es8388_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(pmdk_es8388_audio_map),
|
||||
};
|
||||
|
||||
static int pmdk_sound_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &pmdk;
|
||||
struct device *dev = &pdev->dev;
|
||||
int n;
|
||||
|
||||
card->dev = dev;
|
||||
hs_jack_gpios[0].gpiod_dev = dev;
|
||||
n = gpiod_count(dev, "det");
|
||||
|
||||
if (n < 0)
|
||||
pmdk_dai[0].init = NULL;
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, card);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id pmdk_sound_acpi_match[] = {
|
||||
{ "PHYT8004", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pmdk_sound_acpi_match);
|
||||
|
||||
static struct platform_driver pmdk_sound_driver = {
|
||||
.probe = pmdk_sound_probe,
|
||||
.driver = {
|
||||
.name = "pmdk_es8388",
|
||||
.acpi_match_table = pmdk_sound_acpi_match,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &snd_soc_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(pmdk_sound_driver);
|
||||
|
||||
MODULE_AUTHOR("Zhang Yiqun<zhangyiqun@phytium.com.cn>");
|
||||
MODULE_DESCRIPTION("ALSA SoC PMDK ES8388");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue