Commit 242f66c8 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/ac97', 'asoc/topic/ac97-mfd',...

Merge remote-tracking branches 'asoc/topic/ac97', 'asoc/topic/ac97-mfd', 'asoc/topic/amd' and 'asoc/topic/arizona-mfd' into asoc-next
...@@ -65,45 +65,6 @@ Optional properties: ...@@ -65,45 +65,6 @@ Optional properties:
a value that is out of range for a 16 bit register then the chip default a value that is out of range for a 16 bit register then the chip default
will be used. If present exactly five values must be specified. will be used. If present exactly five values must be specified.
- wlf,inmode : A list of INn_MODE register values, where n is the number
of input signals. Valid values are 0 (Differential), 1 (Single-ended) and
2 (Digital Microphone). If absent, INn_MODE registers set to 0 by default.
If present, values must be specified less than or equal to the number of
input signals. If values less than the number of input signals, elements
that have not been specified are set to 0 by default. Entries are:
<IN1, IN2, IN3, IN4> (wm5102, wm5110, wm8280, wm8997)
<IN1A, IN2A, IN1B, IN2B> (wm8998, wm1814)
- wlf,out-mono : A list of boolean values indicating whether each output is
mono or stereo. Position within the list indicates the output affected
(eg. First entry in the list corresponds to output 1). A non-zero value
indicates a mono output. If present, the number of values should be less
than or equal to the number of outputs, if less values are supplied the
additional outputs will be treated as stereo.
- wlf,dmic-ref : DMIC reference voltage source for each input, can be
selected from either MICVDD or one of the MICBIAS's, defines
(ARIZONA_DMIC_xxxx) are provided in <dt-bindings/mfd/arizona.txt>. If
present, the number of values should be less than or equal to the
number of inputs, unspecified inputs will use the chip default.
- wlf,max-channels-clocked : The maximum number of channels to be clocked on
each AIF, useful for I2S systems with multiple data lines being mastered.
Specify one cell for each AIF to be configured, specify zero for AIFs that
should be handled normally.
If present, number of cells must be less than or equal to the number of
AIFs. If less than the number of AIFs, for cells that have not been
specified the corresponding AIFs will be treated as default setting.
- wlf,spk-fmt : PDM speaker data format, must contain 2 cells (OUT5 and OUT6).
See the datasheet for values.
The second cell is ignored for codecs that do not have OUT6 (wm5102, wm8997,
wm8998, wm1814)
- wlf,spk-mute : PDM speaker mute setting, must contain 2 cells (OUT5 and OUT6).
See the datasheet for values.
The second cell is ignored for codecs that do not have OUT6 (wm5102, wm8997,
wm8998, wm1814)
- DCVDD-supply, MICVDD-supply : Power supplies, only need to be specified if - DCVDD-supply, MICVDD-supply : Power supplies, only need to be specified if
they are being externally supplied. As covered in they are being externally supplied. As covered in
Documentation/devicetree/bindings/regulator/regulator.txt Documentation/devicetree/bindings/regulator/regulator.txt
...@@ -112,6 +73,7 @@ Optional properties: ...@@ -112,6 +73,7 @@ Optional properties:
Also see child specific device properties: Also see child specific device properties:
Regulator - ../regulator/arizona-regulator.txt Regulator - ../regulator/arizona-regulator.txt
Extcon - ../extcon/extcon-arizona.txt Extcon - ../extcon/extcon-arizona.txt
Sound - ../sound/arizona.txt
Example: Example:
......
Cirrus Logic Arizona class audio SoCs
These devices are audio SoCs with extensive digital capabilities and a range
of analogue I/O.
This document lists sound specific bindings, see the primary binding
document:
../mfd/arizona.txt
Optional properties:
- wlf,inmode : A list of INn_MODE register values, where n is the number
of input signals. Valid values are 0 (Differential), 1 (Single-ended) and
2 (Digital Microphone). If absent, INn_MODE registers set to 0 by default.
If present, values must be specified less than or equal to the number of
input signals. If values less than the number of input signals, elements
that have not been specified are set to 0 by default. Entries are:
<IN1, IN2, IN3, IN4> (wm5102, wm5110, wm8280, wm8997)
<IN1A, IN2A, IN1B, IN2B> (wm8998, wm1814)
- wlf,out-mono : A list of boolean values indicating whether each output is
mono or stereo. Position within the list indicates the output affected
(eg. First entry in the list corresponds to output 1). A non-zero value
indicates a mono output. If present, the number of values should be less
than or equal to the number of outputs, if less values are supplied the
additional outputs will be treated as stereo.
- wlf,dmic-ref : DMIC reference voltage source for each input, can be
selected from either MICVDD or one of the MICBIAS's, defines
(ARIZONA_DMIC_xxxx) are provided in <dt-bindings/mfd/arizona.txt>. If
present, the number of values should be less than or equal to the
number of inputs, unspecified inputs will use the chip default.
- wlf,max-channels-clocked : The maximum number of channels to be clocked on
each AIF, useful for I2S systems with multiple data lines being mastered.
Specify one cell for each AIF to be configured, specify zero for AIFs that
should be handled normally.
If present, number of cells must be less than or equal to the number of
AIFs. If less than the number of AIFs, for cells that have not been
specified the corresponding AIFs will be treated as default setting.
- wlf,spk-fmt : PDM speaker data format, must contain 2 cells (OUT5 and OUT6).
See the datasheet for values.
The second cell is ignored for codecs that do not have OUT6 (wm5102, wm8997,
wm8998, wm1814)
- wlf,spk-mute : PDM speaker mute setting, must contain 2 cells (OUT5 and OUT6).
See the datasheet for values.
The second cell is ignored for codecs that do not have OUT6 (wm5102, wm8997,
wm8998, wm1814)
- wlf,out-volume-limit : The volume limit value that should be applied to each
output channel. See the datasheet for exact values. Channels are specified
in the order OUT1L, OUT1R, OUT2L, OUT2R, etc.
...@@ -14597,6 +14597,7 @@ F: Documentation/devicetree/bindings/extcon/extcon-arizona.txt ...@@ -14597,6 +14597,7 @@ F: Documentation/devicetree/bindings/extcon/extcon-arizona.txt
F: Documentation/devicetree/bindings/regulator/arizona-regulator.txt F: Documentation/devicetree/bindings/regulator/arizona-regulator.txt
F: Documentation/devicetree/bindings/mfd/arizona.txt F: Documentation/devicetree/bindings/mfd/arizona.txt
F: Documentation/devicetree/bindings/mfd/wm831x.txt F: Documentation/devicetree/bindings/mfd/wm831x.txt
F: Documentation/devicetree/bindings/sound/wlf,arizona.txt
F: arch/arm/mach-s3c64xx/mach-crag6410* F: arch/arm/mach-s3c64xx/mach-crag6410*
F: drivers/clk/clk-wm83*.c F: drivers/clk/clk-wm83*.c
F: drivers/extcon/extcon-arizona.c F: drivers/extcon/extcon-arizona.c
......
...@@ -371,6 +371,8 @@ static int acp_hw_init(void *handle) ...@@ -371,6 +371,8 @@ static int acp_hw_init(void *handle)
adev->acp.acp_cell[0].name = "acp_audio_dma"; adev->acp.acp_cell[0].name = "acp_audio_dma";
adev->acp.acp_cell[0].num_resources = 4; adev->acp.acp_cell[0].num_resources = 4;
adev->acp.acp_cell[0].resources = &adev->acp.acp_res[0]; adev->acp.acp_cell[0].resources = &adev->acp.acp_res[0];
adev->acp.acp_cell[0].platform_data = &adev->asic_type;
adev->acp.acp_cell[0].pdata_size = sizeof(adev->asic_type);
adev->acp.acp_cell[1].name = "designware-i2s"; adev->acp.acp_cell[1].name = "designware-i2s";
adev->acp.acp_cell[1].num_resources = 1; adev->acp.acp_cell[1].num_resources = 1;
......
...@@ -23,34 +23,9 @@ ...@@ -23,34 +23,9 @@
#ifndef __AMD_SHARED_H__ #ifndef __AMD_SHARED_H__
#define __AMD_SHARED_H__ #define __AMD_SHARED_H__
#define AMD_MAX_USEC_TIMEOUT 200000 /* 200 ms */ #include <drm/amd_asic_type.h>
/* #define AMD_MAX_USEC_TIMEOUT 200000 /* 200 ms */
* Supported ASIC types
*/
enum amd_asic_type {
CHIP_TAHITI = 0,
CHIP_PITCAIRN,
CHIP_VERDE,
CHIP_OLAND,
CHIP_HAINAN,
CHIP_BONAIRE,
CHIP_KAVERI,
CHIP_KABINI,
CHIP_HAWAII,
CHIP_MULLINS,
CHIP_TOPAZ,
CHIP_TONGA,
CHIP_FIJI,
CHIP_CARRIZO,
CHIP_STONEY,
CHIP_POLARIS10,
CHIP_POLARIS11,
CHIP_POLARIS12,
CHIP_VEGA10,
CHIP_RAVEN,
CHIP_LAST,
};
/* /*
* Chip flags * Chip flags
......
...@@ -727,7 +727,7 @@ config TOUCHSCREEN_WM831X ...@@ -727,7 +727,7 @@ config TOUCHSCREEN_WM831X
config TOUCHSCREEN_WM97XX config TOUCHSCREEN_WM97XX
tristate "Support for WM97xx AC97 touchscreen controllers" tristate "Support for WM97xx AC97 touchscreen controllers"
depends on AC97_BUS depends on AC97_BUS || AC97_BUS_NEW
help help
Say Y here if you have a Wolfson Microelectronics WM97xx Say Y here if you have a Wolfson Microelectronics WM97xx
touchscreen connected to your system. Note that this option touchscreen connected to your system. Note that this option
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/mfd/wm97xx.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/wm97xx.h> #include <linux/wm97xx.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
...@@ -581,27 +582,85 @@ static void wm97xx_ts_input_close(struct input_dev *idev) ...@@ -581,27 +582,85 @@ static void wm97xx_ts_input_close(struct input_dev *idev)
wm->codec->acc_enable(wm, 0); wm->codec->acc_enable(wm, 0);
} }
static int wm97xx_probe(struct device *dev) static int wm97xx_register_touch(struct wm97xx *wm)
{ {
struct wm97xx *wm; struct wm97xx_pdata *pdata = dev_get_platdata(wm->dev);
struct wm97xx_pdata *pdata = dev_get_platdata(dev); int ret;
int ret = 0, id = 0;
wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL); wm->input_dev = devm_input_allocate_device(wm->dev);
if (!wm) if (wm->input_dev == NULL)
return -ENOMEM; return -ENOMEM;
mutex_init(&wm->codec_mutex);
wm->dev = dev; /* set up touch configuration */
dev_set_drvdata(dev, wm); wm->input_dev->name = "wm97xx touchscreen";
wm->ac97 = to_ac97_t(dev); wm->input_dev->phys = "wm97xx";
wm->input_dev->open = wm97xx_ts_input_open;
wm->input_dev->close = wm97xx_ts_input_close;
__set_bit(EV_ABS, wm->input_dev->evbit);
__set_bit(EV_KEY, wm->input_dev->evbit);
__set_bit(BTN_TOUCH, wm->input_dev->keybit);
input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1],
abs_x[2], 0);
input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1],
abs_y[2], 0);
input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1],
abs_p[2], 0);
input_set_drvdata(wm->input_dev, wm);
wm->input_dev->dev.parent = wm->dev;
ret = input_register_device(wm->input_dev);
if (ret)
return ret;
/*
* register our extended touch device (for machine specific
* extensions)
*/
wm->touch_dev = platform_device_alloc("wm97xx-touch", -1);
if (!wm->touch_dev) {
ret = -ENOMEM;
goto touch_err;
}
platform_set_drvdata(wm->touch_dev, wm);
wm->touch_dev->dev.parent = wm->dev;
wm->touch_dev->dev.platform_data = pdata;
ret = platform_device_add(wm->touch_dev);
if (ret < 0)
goto touch_reg_err;
return 0;
touch_reg_err:
platform_device_put(wm->touch_dev);
touch_err:
input_unregister_device(wm->input_dev);
wm->input_dev = NULL;
return ret;
}
static void wm97xx_unregister_touch(struct wm97xx *wm)
{
platform_device_unregister(wm->touch_dev);
input_unregister_device(wm->input_dev);
wm->input_dev = NULL;
}
static int _wm97xx_probe(struct wm97xx *wm)
{
int id = 0;
mutex_init(&wm->codec_mutex);
dev_set_drvdata(wm->dev, wm);
/* check that we have a supported codec */ /* check that we have a supported codec */
id = wm97xx_reg_read(wm, AC97_VENDOR_ID1); id = wm97xx_reg_read(wm, AC97_VENDOR_ID1);
if (id != WM97XX_ID1) { if (id != WM97XX_ID1) {
dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id); dev_err(wm->dev,
ret = -ENODEV; "Device with vendor %04x is not a wm97xx\n", id);
goto alloc_err; return -ENODEV;
} }
wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
...@@ -629,8 +688,7 @@ static int wm97xx_probe(struct device *dev) ...@@ -629,8 +688,7 @@ static int wm97xx_probe(struct device *dev)
default: default:
dev_err(wm->dev, "Support for wm97%02x not compiled in.\n", dev_err(wm->dev, "Support for wm97%02x not compiled in.\n",
wm->id & 0xff); wm->id & 0xff);
ret = -ENODEV; return -ENODEV;
goto alloc_err;
} }
/* set up physical characteristics */ /* set up physical characteristics */
...@@ -644,79 +702,58 @@ static int wm97xx_probe(struct device *dev) ...@@ -644,79 +702,58 @@ static int wm97xx_probe(struct device *dev)
wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
wm->input_dev = input_allocate_device(); return wm97xx_register_touch(wm);
if (wm->input_dev == NULL) { }
ret = -ENOMEM;
goto alloc_err;
}
/* set up touch configuration */
wm->input_dev->name = "wm97xx touchscreen";
wm->input_dev->phys = "wm97xx";
wm->input_dev->open = wm97xx_ts_input_open;
wm->input_dev->close = wm97xx_ts_input_close;
__set_bit(EV_ABS, wm->input_dev->evbit);
__set_bit(EV_KEY, wm->input_dev->evbit);
__set_bit(BTN_TOUCH, wm->input_dev->keybit);
input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1],
abs_x[2], 0);
input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1],
abs_y[2], 0);
input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1],
abs_p[2], 0);
input_set_drvdata(wm->input_dev, wm); static void wm97xx_remove_battery(struct wm97xx *wm)
wm->input_dev->dev.parent = dev; {
platform_device_unregister(wm->battery_dev);
}
ret = input_register_device(wm->input_dev); static int wm97xx_add_battery(struct wm97xx *wm,
if (ret < 0) struct wm97xx_batt_pdata *pdata)
goto dev_alloc_err; {
int ret;
/* register our battery device */
wm->battery_dev = platform_device_alloc("wm97xx-battery", -1); wm->battery_dev = platform_device_alloc("wm97xx-battery", -1);
if (!wm->battery_dev) { if (!wm->battery_dev)
ret = -ENOMEM; return -ENOMEM;
goto batt_err;
}
platform_set_drvdata(wm->battery_dev, wm); platform_set_drvdata(wm->battery_dev, wm);
wm->battery_dev->dev.parent = dev; wm->battery_dev->dev.parent = wm->dev;
wm->battery_dev->dev.platform_data = pdata ? pdata->batt_pdata : NULL; wm->battery_dev->dev.platform_data = pdata;
ret = platform_device_add(wm->battery_dev); ret = platform_device_add(wm->battery_dev);
if (ret < 0) if (ret)
goto batt_reg_err; platform_device_put(wm->battery_dev);
/* register our extended touch device (for machine specific return ret;
* extensions) */ }
wm->touch_dev = platform_device_alloc("wm97xx-touch", -1);
if (!wm->touch_dev) { static int wm97xx_probe(struct device *dev)
ret = -ENOMEM; {
goto touch_err; struct wm97xx *wm;
} int ret;
platform_set_drvdata(wm->touch_dev, wm); struct wm97xx_pdata *pdata = dev_get_platdata(dev);
wm->touch_dev->dev.parent = dev;
wm->touch_dev->dev.platform_data = pdata; wm = devm_kzalloc(dev, sizeof(struct wm97xx), GFP_KERNEL);
ret = platform_device_add(wm->touch_dev); if (!wm)
return -ENOMEM;
wm->dev = dev;
wm->ac97 = to_ac97_t(dev);
ret = _wm97xx_probe(wm);
if (ret)
return ret;
ret = wm97xx_add_battery(wm, pdata ? pdata->batt_pdata : NULL);
if (ret < 0) if (ret < 0)
goto touch_reg_err; goto batt_err;
return ret; return ret;
touch_reg_err: batt_err:
platform_device_put(wm->touch_dev); wm97xx_unregister_touch(wm);
touch_err:
platform_device_del(wm->battery_dev);
batt_reg_err:
platform_device_put(wm->battery_dev);
batt_err:
input_unregister_device(wm->input_dev);
wm->input_dev = NULL;
dev_alloc_err:
input_free_device(wm->input_dev);
alloc_err:
kfree(wm);
return ret; return ret;
} }
...@@ -724,14 +761,45 @@ static int wm97xx_remove(struct device *dev) ...@@ -724,14 +761,45 @@ static int wm97xx_remove(struct device *dev)
{ {
struct wm97xx *wm = dev_get_drvdata(dev); struct wm97xx *wm = dev_get_drvdata(dev);
platform_device_unregister(wm->battery_dev); wm97xx_remove_battery(wm);
platform_device_unregister(wm->touch_dev); wm97xx_unregister_touch(wm);
input_unregister_device(wm->input_dev);
kfree(wm);
return 0; return 0;
} }
static int wm97xx_mfd_probe(struct platform_device *pdev)
{
struct wm97xx *wm;
struct wm97xx_platform_data *mfd_pdata = dev_get_platdata(&pdev->dev);
int ret;
wm = devm_kzalloc(&pdev->dev, sizeof(struct wm97xx), GFP_KERNEL);
if (!wm)
return -ENOMEM;
wm->dev = &pdev->dev;
wm->ac97 = mfd_pdata->ac97;
ret = _wm97xx_probe(wm);
if (ret)
return ret;
ret = wm97xx_add_battery(wm, mfd_pdata->batt_pdata);
if (ret < 0)
goto batt_err;
return ret;
batt_err:
wm97xx_unregister_touch(wm);
return ret;
}
static int wm97xx_mfd_remove(struct platform_device *pdev)
{
return wm97xx_remove(&pdev->dev);
}
static int __maybe_unused wm97xx_suspend(struct device *dev) static int __maybe_unused wm97xx_suspend(struct device *dev)
{ {
struct wm97xx *wm = dev_get_drvdata(dev); struct wm97xx *wm = dev_get_drvdata(dev);
...@@ -828,21 +896,41 @@ EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); ...@@ -828,21 +896,41 @@ EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
static struct device_driver wm97xx_driver = { static struct device_driver wm97xx_driver = {
.name = "wm97xx-ts", .name = "wm97xx-ts",
#ifdef CONFIG_AC97_BUS
.bus = &ac97_bus_type, .bus = &ac97_bus_type,
#endif
.owner = THIS_MODULE, .owner = THIS_MODULE,
.probe = wm97xx_probe, .probe = wm97xx_probe,
.remove = wm97xx_remove, .remove = wm97xx_remove,
.pm = &wm97xx_pm_ops, .pm = &wm97xx_pm_ops,
}; };
static struct platform_driver wm97xx_mfd_driver = {
.driver = {
.name = "wm97xx-ts",
.pm = &wm97xx_pm_ops,
},
.probe = wm97xx_mfd_probe,
.remove = wm97xx_mfd_remove,
};
static int __init wm97xx_init(void) static int __init wm97xx_init(void)
{ {
return driver_register(&wm97xx_driver); int ret;
ret = platform_driver_register(&wm97xx_mfd_driver);
if (ret)
return ret;
if (IS_BUILTIN(CONFIG_AC97_BUS))
ret = driver_register(&wm97xx_driver);
return ret;
} }
static void __exit wm97xx_exit(void) static void __exit wm97xx_exit(void)
{ {
driver_unregister(&wm97xx_driver); driver_unregister(&wm97xx_driver);
platform_driver_unregister(&wm97xx_mfd_driver);
} }
module_init(wm97xx_init); module_init(wm97xx_init);
......
...@@ -1746,6 +1746,20 @@ config MFD_WM8994 ...@@ -1746,6 +1746,20 @@ config MFD_WM8994
core support for the WM8994, in order to use the actual core support for the WM8994, in order to use the actual
functionaltiy of the device other drivers must be enabled. functionaltiy of the device other drivers must be enabled.
config MFD_WM97xx
tristate "Wolfson Microelectronics WM97xx"
select MFD_CORE
select REGMAP_AC97
select AC97_BUS_COMPAT
depends on AC97_BUS_NEW
help
The WM9705, WM9712 and WM9713 is a highly integrated hi-fi CODEC
designed for smartphone applications. As well as audio functionality
it has on board GPIO and a touchscreen functionality which is
supported via the relevant subsystems. This driver provides core
support for the WM97xx, in order to use the actual functionaltiy of
the device other drivers must be enabled.
config MFD_STW481X config MFD_STW481X
tristate "Support for ST Microelectronics STw481x" tristate "Support for ST Microelectronics STw481x"
depends on I2C && (ARCH_NOMADIK || COMPILE_TEST) depends on I2C && (ARCH_NOMADIK || COMPILE_TEST)
......
...@@ -74,6 +74,7 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o ...@@ -74,6 +74,7 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
wm8994-objs := wm8994-core.o wm8994-irq.o wm8994-regmap.o wm8994-objs := wm8994-core.o wm8994-irq.o wm8994-regmap.o
obj-$(CONFIG_MFD_WM8994) += wm8994.o obj-$(CONFIG_MFD_WM8994) += wm8994.o
obj-$(CONFIG_MFD_WM97xx) += wm97xx-core.o
obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS65010) += tps65010.o
......
...@@ -797,12 +797,7 @@ EXPORT_SYMBOL_GPL(arizona_of_get_type); ...@@ -797,12 +797,7 @@ EXPORT_SYMBOL_GPL(arizona_of_get_type);
static int arizona_of_get_core_pdata(struct arizona *arizona) static int arizona_of_get_core_pdata(struct arizona *arizona)
{ {
struct arizona_pdata *pdata = &arizona->pdata; struct arizona_pdata *pdata = &arizona->pdata;
struct property *prop;
const __be32 *cur;
u32 val;
u32 pdm_val[ARIZONA_MAX_PDM_SPK];
int ret, i; int ret, i;
int count = 0;
pdata->reset = of_get_named_gpio(arizona->dev->of_node, "wlf,reset", 0); pdata->reset = of_get_named_gpio(arizona->dev->of_node, "wlf,reset", 0);
if (pdata->reset == -EPROBE_DEFER) { if (pdata->reset == -EPROBE_DEFER) {
...@@ -836,64 +831,6 @@ static int arizona_of_get_core_pdata(struct arizona *arizona) ...@@ -836,64 +831,6 @@ static int arizona_of_get_core_pdata(struct arizona *arizona)
ret); ret);
} }
of_property_for_each_u32(arizona->dev->of_node, "wlf,inmode", prop,
cur, val) {
if (count == ARRAY_SIZE(pdata->inmode))
break;
pdata->inmode[count] = val;
count++;
}
count = 0;
of_property_for_each_u32(arizona->dev->of_node, "wlf,dmic-ref", prop,
cur, val) {
if (count == ARRAY_SIZE(pdata->dmic_ref))
break;
pdata->dmic_ref[count] = val;
count++;
}
count = 0;
of_property_for_each_u32(arizona->dev->of_node, "wlf,out-mono", prop,
cur, val) {
if (count == ARRAY_SIZE(pdata->out_mono))
break;
pdata->out_mono[count] = !!val;
count++;
}
count = 0;
of_property_for_each_u32(arizona->dev->of_node,
"wlf,max-channels-clocked",
prop, cur, val) {
if (count == ARRAY_SIZE(pdata->max_channels_clocked))
break;
pdata->max_channels_clocked[count] = val;
count++;
}
ret = of_property_read_u32_array(arizona->dev->of_node,
"wlf,spk-fmt",
pdm_val,
ARRAY_SIZE(pdm_val));
if (ret >= 0)
for (count = 0; count < ARRAY_SIZE(pdata->spk_fmt); ++count)
pdata->spk_fmt[count] = pdm_val[count];
ret = of_property_read_u32_array(arizona->dev->of_node,
"wlf,spk-mute",
pdm_val,
ARRAY_SIZE(pdm_val));
if (ret >= 0)
for (count = 0; count < ARRAY_SIZE(pdata->spk_mute); ++count)
pdata->spk_mute[count] = pdm_val[count];
return 0; return 0;
} }
...@@ -1026,7 +963,7 @@ int arizona_dev_init(struct arizona *arizona) ...@@ -1026,7 +963,7 @@ int arizona_dev_init(struct arizona *arizona)
const char * const mclk_name[] = { "mclk1", "mclk2" }; const char * const mclk_name[] = { "mclk1", "mclk2" };
struct device *dev = arizona->dev; struct device *dev = arizona->dev;
const char *type_name = NULL; const char *type_name = NULL;
unsigned int reg, val, mask; unsigned int reg, val;
int (*apply_patch)(struct arizona *) = NULL; int (*apply_patch)(struct arizona *) = NULL;
const struct mfd_cell *subdevs = NULL; const struct mfd_cell *subdevs = NULL;
int n_subdevs, ret, i; int n_subdevs, ret, i;
...@@ -1429,73 +1366,6 @@ int arizona_dev_init(struct arizona *arizona) ...@@ -1429,73 +1366,6 @@ int arizona_dev_init(struct arizona *arizona)
ARIZONA_MICB1_RATE, val); ARIZONA_MICB1_RATE, val);
} }
for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
/* Default for both is 0 so noop with defaults */
val = arizona->pdata.dmic_ref[i]
<< ARIZONA_IN1_DMIC_SUP_SHIFT;
if (arizona->pdata.inmode[i] & ARIZONA_INMODE_DMIC)
val |= 1 << ARIZONA_IN1_MODE_SHIFT;
switch (arizona->type) {
case WM8998:
case WM1814:
regmap_update_bits(arizona->regmap,
ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 8),
ARIZONA_IN1L_SRC_SE_MASK,
(arizona->pdata.inmode[i] & ARIZONA_INMODE_SE)
<< ARIZONA_IN1L_SRC_SE_SHIFT);
regmap_update_bits(arizona->regmap,
ARIZONA_ADC_DIGITAL_VOLUME_1R + (i * 8),
ARIZONA_IN1R_SRC_SE_MASK,
(arizona->pdata.inmode[i] & ARIZONA_INMODE_SE)
<< ARIZONA_IN1R_SRC_SE_SHIFT);
mask = ARIZONA_IN1_DMIC_SUP_MASK |
ARIZONA_IN1_MODE_MASK;
break;
default:
if (arizona->pdata.inmode[i] & ARIZONA_INMODE_SE)
val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT;
mask = ARIZONA_IN1_DMIC_SUP_MASK |
ARIZONA_IN1_MODE_MASK |
ARIZONA_IN1_SINGLE_ENDED_MASK;
break;
}
regmap_update_bits(arizona->regmap,
ARIZONA_IN1L_CONTROL + (i * 8),
mask, val);
}
for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
/* Default is 0 so noop with defaults */
if (arizona->pdata.out_mono[i])
val = ARIZONA_OUT1_MONO;
else
val = 0;
regmap_update_bits(arizona->regmap,
ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
ARIZONA_OUT1_MONO, val);
}
for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
if (arizona->pdata.spk_mute[i])
regmap_update_bits(arizona->regmap,
ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
ARIZONA_SPK1_MUTE_ENDIAN_MASK |
ARIZONA_SPK1_MUTE_SEQ1_MASK,
arizona->pdata.spk_mute[i]);
if (arizona->pdata.spk_fmt[i])
regmap_update_bits(arizona->regmap,
ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
ARIZONA_SPK1_FMT_MASK,
arizona->pdata.spk_fmt[i]);
}
pm_runtime_set_active(arizona->dev); pm_runtime_set_active(arizona->dev);
pm_runtime_enable(arizona->dev); pm_runtime_enable(arizona->dev);
......
/*
* Wolfson WM97xx -- Core device
*
* Copyright (C) 2017 Robert Jarzmik
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Features:
* - an AC97 audio codec
* - a touchscreen driver
* - a GPIO block
*/
#include <linux/device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/wm97xx.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/wm97xx.h>
#include <sound/ac97/codec.h>
#include <sound/ac97/compat.h>
#define WM9705_VENDOR_ID 0x574d4c05
#define WM9712_VENDOR_ID 0x574d4c12
#define WM9713_VENDOR_ID 0x574d4c13
#define WM97xx_VENDOR_ID_MASK 0xffffffff
struct wm97xx_priv {
struct regmap *regmap;
struct snd_ac97 *ac97;
struct device *dev;
struct wm97xx_platform_data codec_pdata;
};
static bool wm97xx_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case AC97_RESET ... AC97_PCM_SURR_DAC_RATE:
case AC97_PCM_LR_ADC_RATE:
case AC97_CENTER_LFE_MASTER:
case AC97_SPDIF ... AC97_LINE1_LEVEL:
case AC97_GPIO_CFG ... 0x5c:
case AC97_CODEC_CLASS_REV ... AC97_PCI_SID:
case 0x74 ... AC97_VENDOR_ID2:
return true;
default:
return false;
}
}
static bool wm97xx_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
return false;
default:
return wm97xx_readable_reg(dev, reg);
}
}
static const struct reg_default wm9705_reg_defaults[] = {
{ 0x02, 0x8000 },
{ 0x04, 0x8000 },
{ 0x06, 0x8000 },
{ 0x0a, 0x8000 },
{ 0x0c, 0x8008 },
{ 0x0e, 0x8008 },
{ 0x10, 0x8808 },
{ 0x12, 0x8808 },
{ 0x14, 0x8808 },
{ 0x16, 0x8808 },
{ 0x18, 0x8808 },
{ 0x1a, 0x0000 },
{ 0x1c, 0x8000 },
{ 0x20, 0x0000 },
{ 0x22, 0x0000 },
{ 0x26, 0x000f },
{ 0x28, 0x0605 },
{ 0x2a, 0x0000 },
{ 0x2c, 0xbb80 },
{ 0x32, 0xbb80 },
{ 0x34, 0x2000 },
{ 0x5a, 0x0000 },
{ 0x5c, 0x0000 },
{ 0x72, 0x0808 },
{ 0x74, 0x0000 },
{ 0x76, 0x0006 },
{ 0x78, 0x0000 },
{ 0x7a, 0x0000 },
};
static const struct regmap_config wm9705_regmap_config = {
.reg_bits = 16,
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = wm9705_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9705_reg_defaults),
.volatile_reg = regmap_ac97_default_volatile,
.readable_reg = wm97xx_readable_reg,
.writeable_reg = wm97xx_writeable_reg,
};
static struct mfd_cell wm9705_cells[] = {
{ .name = "wm9705-codec", },
{ .name = "wm97xx-ts", },
};
static bool wm9712_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case AC97_REC_GAIN:
return true;
default:
return regmap_ac97_default_volatile(dev, reg);
}
}
static const struct reg_default wm9712_reg_defaults[] = {
{ 0x02, 0x8000 },
{ 0x04, 0x8000 },
{ 0x06, 0x8000 },
{ 0x08, 0x0f0f },
{ 0x0a, 0xaaa0 },
{ 0x0c, 0xc008 },
{ 0x0e, 0x6808 },
{ 0x10, 0xe808 },
{ 0x12, 0xaaa0 },
{ 0x14, 0xad00 },
{ 0x16, 0x8000 },
{ 0x18, 0xe808 },
{ 0x1a, 0x3000 },
{ 0x1c, 0x8000 },
{ 0x20, 0x0000 },
{ 0x22, 0x0000 },
{ 0x26, 0x000f },
{ 0x28, 0x0605 },
{ 0x2a, 0x0410 },
{ 0x2c, 0xbb80 },
{ 0x2e, 0xbb80 },
{ 0x32, 0xbb80 },
{ 0x34, 0x2000 },
{ 0x4c, 0xf83e },
{ 0x4e, 0xffff },
{ 0x50, 0x0000 },
{ 0x52, 0x0000 },
{ 0x56, 0xf83e },
{ 0x58, 0x0008 },
{ 0x5c, 0x0000 },
{ 0x60, 0xb032 },
{ 0x62, 0x3e00 },
{ 0x64, 0x0000 },
{ 0x76, 0x0006 },
{ 0x78, 0x0001 },
{ 0x7a, 0x0000 },
};
static const struct regmap_config wm9712_regmap_config = {
.reg_bits = 16,
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = wm9712_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults),
.volatile_reg = wm9712_volatile_reg,
.readable_reg = wm97xx_readable_reg,
.writeable_reg = wm97xx_writeable_reg,
};
static struct mfd_cell wm9712_cells[] = {
{ .name = "wm9712-codec", },
{ .name = "wm97xx-ts", },
};
static const struct reg_default wm9713_reg_defaults[] = {
{ 0x02, 0x8080 }, /* Speaker Output Volume */
{ 0x04, 0x8080 }, /* Headphone Output Volume */
{ 0x06, 0x8080 }, /* Out3/OUT4 Volume */
{ 0x08, 0xc880 }, /* Mono Volume */
{ 0x0a, 0xe808 }, /* LINEIN Volume */
{ 0x0c, 0xe808 }, /* DAC PGA Volume */
{ 0x0e, 0x0808 }, /* MIC PGA Volume */
{ 0x10, 0x00da }, /* MIC Routing Control */
{ 0x12, 0x8000 }, /* Record PGA Volume */
{ 0x14, 0xd600 }, /* Record Routing */
{ 0x16, 0xaaa0 }, /* PCBEEP Volume */
{ 0x18, 0xaaa0 }, /* VxDAC Volume */
{ 0x1a, 0xaaa0 }, /* AUXDAC Volume */
{ 0x1c, 0x0000 }, /* Output PGA Mux */
{ 0x1e, 0x0000 }, /* DAC 3D control */
{ 0x20, 0x0f0f }, /* DAC Tone Control*/
{ 0x22, 0x0040 }, /* MIC Input Select & Bias */
{ 0x24, 0x0000 }, /* Output Volume Mapping & Jack */
{ 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/
{ 0x28, 0x0405 }, /* Extended Audio ID */
{ 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */
{ 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */
{ 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */
{ 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */
{ 0x36, 0x4523 }, /* PCM codec control */
{ 0x3a, 0x2000 }, /* SPDIF control */
{ 0x3c, 0xfdff }, /* Powerdown 1 */
{ 0x3e, 0xffff }, /* Powerdown 2 */
{ 0x40, 0x0000 }, /* General Purpose */
{ 0x42, 0x0000 }, /* Fast Power-Up Control */
{ 0x44, 0x0080 }, /* MCLK/PLL Control */
{ 0x46, 0x0000 }, /* MCLK/PLL Control */
{ 0x4c, 0xfffe }, /* GPIO Pin Configuration */
{ 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */
{ 0x50, 0x0000 }, /* GPIO Pin Sticky */
{ 0x52, 0x0000 }, /* GPIO Pin Wake-Up */
/* GPIO Pin Status */
{ 0x56, 0xfffe }, /* GPIO Pin Sharing */
{ 0x58, 0x4000 }, /* GPIO PullUp/PullDown */
{ 0x5a, 0x0000 }, /* Additional Functions 1 */
{ 0x5c, 0x0000 }, /* Additional Functions 2 */
{ 0x60, 0xb032 }, /* ALC Control */
{ 0x62, 0x3e00 }, /* ALC / Noise Gate Control */
{ 0x64, 0x0000 }, /* AUXDAC input control */
{ 0x74, 0x0000 }, /* Digitiser Reg 1 */
{ 0x76, 0x0006 }, /* Digitiser Reg 2 */
{ 0x78, 0x0001 }, /* Digitiser Reg 3 */
{ 0x7a, 0x0000 }, /* Digitiser Read Back */
};
static const struct regmap_config wm9713_regmap_config = {
.reg_bits = 16,
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = wm9713_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults),
.volatile_reg = regmap_ac97_default_volatile,
.readable_reg = wm97xx_readable_reg,
.writeable_reg = wm97xx_writeable_reg,
};
static struct mfd_cell wm9713_cells[] = {
{ .name = "wm9713-codec", },
{ .name = "wm97xx-ts", },
};
static int wm97xx_ac97_probe(struct ac97_codec_device *adev)
{
struct wm97xx_priv *wm97xx;
const struct regmap_config *config;
struct wm97xx_platform_data *codec_pdata;
struct mfd_cell *cells;
int ret = -ENODEV, nb_cells, i;
struct wm97xx_pdata *pdata = snd_ac97_codec_get_platdata(adev);
wm97xx = devm_kzalloc(ac97_codec_dev2dev(adev),
sizeof(*wm97xx), GFP_KERNEL);
if (!wm97xx)
return -ENOMEM;
wm97xx->dev = ac97_codec_dev2dev(adev);
wm97xx->ac97 = snd_ac97_compat_alloc(adev);
if (IS_ERR(wm97xx->ac97))
return PTR_ERR(wm97xx->ac97);
ac97_set_drvdata(adev, wm97xx);
dev_info(wm97xx->dev, "wm97xx core found, id=0x%x\n",
adev->vendor_id);
codec_pdata = &wm97xx->codec_pdata;
codec_pdata->ac97 = wm97xx->ac97;
codec_pdata->batt_pdata = pdata->batt_pdata;
switch (adev->vendor_id) {
case WM9705_VENDOR_ID:
config = &wm9705_regmap_config;
cells = wm9705_cells;
nb_cells = ARRAY_SIZE(wm9705_cells);
break;
case WM9712_VENDOR_ID:
config = &wm9712_regmap_config;
cells = wm9712_cells;
nb_cells = ARRAY_SIZE(wm9712_cells);
break;
case WM9713_VENDOR_ID:
config = &wm9713_regmap_config;
cells = wm9713_cells;
nb_cells = ARRAY_SIZE(wm9713_cells);
break;
default:
goto err_free_compat;
}
for (i = 0; i < nb_cells; i++) {
cells[i].platform_data = codec_pdata;
cells[i].pdata_size = sizeof(*codec_pdata);
}
codec_pdata->regmap = devm_regmap_init_ac97(wm97xx->ac97, config);
if (IS_ERR(codec_pdata->regmap)) {
ret = PTR_ERR(codec_pdata->regmap);
goto err_free_compat;
}
ret = devm_mfd_add_devices(wm97xx->dev, PLATFORM_DEVID_NONE,
cells, nb_cells, NULL, 0, NULL);
if (ret)
goto err_free_compat;
return ret;
err_free_compat:
snd_ac97_compat_release(wm97xx->ac97);
return ret;
}
static int wm97xx_ac97_remove(struct ac97_codec_device *adev)
{
struct wm97xx_priv *wm97xx = ac97_get_drvdata(adev);
snd_ac97_compat_release(wm97xx->ac97);
return 0;
}
static const struct ac97_id wm97xx_ac97_ids[] = {
{ .id = WM9705_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK },
{ .id = WM9712_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK },
{ .id = WM9713_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK },
{ }
};
static struct ac97_codec_driver wm97xx_ac97_driver = {
.driver = {
.name = "wm97xx-core",
},
.probe = wm97xx_ac97_probe,
.remove = wm97xx_ac97_remove,
.id_table = wm97xx_ac97_ids,
};
static int __init wm97xx_module_init(void)
{
return snd_ac97_codec_driver_register(&wm97xx_ac97_driver);
}
module_init(wm97xx_module_init);
static void __exit wm97xx_module_exit(void)
{
snd_ac97_codec_driver_unregister(&wm97xx_ac97_driver);
}
module_exit(wm97xx_module_exit);
MODULE_DESCRIPTION("WM9712, WM9713 core driver");
MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
MODULE_LICENSE("GPL");
/*
* Copyright 2017 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __AMD_ASIC_TYPE_H__
#define __AMD_ASIC_TYPE_H__
/*
* Supported ASIC types
*/
enum amd_asic_type {
CHIP_TAHITI = 0,
CHIP_PITCAIRN,
CHIP_VERDE,
CHIP_OLAND,
CHIP_HAINAN,
CHIP_BONAIRE,
CHIP_KAVERI,
CHIP_KABINI,
CHIP_HAWAII,
CHIP_MULLINS,
CHIP_TOPAZ,
CHIP_TONGA,
CHIP_FIJI,
CHIP_CARRIZO,
CHIP_STONEY,
CHIP_POLARIS10,
CHIP_POLARIS11,
CHIP_POLARIS12,
CHIP_VEGA10,
CHIP_RAVEN,
CHIP_LAST,
};
#endif /*__AMD_ASIC_TYPE_H__ */
...@@ -174,6 +174,9 @@ struct arizona_pdata { ...@@ -174,6 +174,9 @@ struct arizona_pdata {
/** Mode for outputs */ /** Mode for outputs */
int out_mono[ARIZONA_MAX_OUTPUT]; int out_mono[ARIZONA_MAX_OUTPUT];
/** Limit output volumes */
unsigned int out_vol_limit[2 * ARIZONA_MAX_OUTPUT];
/** PDM speaker mute setting */ /** PDM speaker mute setting */
unsigned int spk_mute[ARIZONA_MAX_PDM_SPK]; unsigned int spk_mute[ARIZONA_MAX_PDM_SPK];
......
/*
* wm97xx client interface
*
* Copyright (C) 2017 Robert Jarzmik
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __LINUX_MFD_WM97XX_H
#define __LINUX_MFD_WM97XX_H
struct regmap;
struct wm97xx_batt_pdata;
struct snd_ac97;
struct wm97xx_platform_data {
struct snd_ac97 *ac97;
struct regmap *regmap;
struct wm97xx_batt_pdata *batt_pdata;
};
#endif
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SOUND_AC97_CODEC2_H
#define __SOUND_AC97_CODEC2_H
#include <linux/device.h>
#define AC97_ID(vendor_id1, vendor_id2) \
((((vendor_id1) & 0xffff) << 16) | ((vendor_id2) & 0xffff))
#define AC97_DRIVER_ID(vendor_id1, vendor_id2, mask_id1, mask_id2, _data) \
{ .id = (((vendor_id1) & 0xffff) << 16) | ((vendor_id2) & 0xffff), \
.mask = (((mask_id1) & 0xffff) << 16) | ((mask_id2) & 0xffff), \
.data = (_data) }
struct ac97_controller;
struct clk;
/**
* struct ac97_id - matches a codec device and driver on an ac97 bus
* @id: The significant bits if the codec vendor ID1 and ID2
* @mask: Bitmask specifying which bits of the id field are significant when
* matching. A driver binds to a device when :
* ((vendorID1 << 8 | vendorID2) & (mask_id1 << 8 | mask_id2)) == id.
* @data: Private data used by the driver.
*/
struct ac97_id {
unsigned int id;
unsigned int mask;
void *data;
};
/**
* ac97_codec_device - a ac97 codec
* @dev: the core device
* @vendor_id: the vendor_id of the codec, as sensed on the AC-link
* @num: the codec number, 0 is primary, 1 is first slave, etc ...
* @clk: the clock BIT_CLK provided by the codec
* @ac97_ctrl: ac97 digital controller on the same AC-link
*
* This is the device instantiated for each codec living on a AC-link. There are
* normally 0 to 4 codec devices per AC-link, and all of them are controlled by
* an AC97 digital controller.
*/
struct ac97_codec_device {
struct device dev;
unsigned int vendor_id;
unsigned int num;
struct clk *clk;
struct ac97_controller *ac97_ctrl;
};
/**
* ac97_codec_driver - a ac97 codec driver
* @driver: the device driver structure
* @probe: the function called when a ac97_codec_device is matched
* @remove: the function called when the device is unbound/removed
* @shutdown: shutdown function (might be NULL)
* @id_table: ac97 vendor_id match table, { } member terminated
*/
struct ac97_codec_driver {
struct device_driver driver;
int (*probe)(struct ac97_codec_device *);
int (*remove)(struct ac97_codec_device *);
void (*shutdown)(struct ac97_codec_device *);
const struct ac97_id *id_table;
};
static inline struct ac97_codec_device *to_ac97_device(struct device *d)
{
return container_of(d, struct ac97_codec_device, dev);
}
static inline struct ac97_codec_driver *to_ac97_driver(struct device_driver *d)
{
return container_of(d, struct ac97_codec_driver, driver);
}
#if IS_ENABLED(CONFIG_AC97_BUS_NEW)
int snd_ac97_codec_driver_register(struct ac97_codec_driver *drv);
void snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv);
#else
static inline int
snd_ac97_codec_driver_register(struct ac97_codec_driver *drv)
{
return 0;
}
static inline void
snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv)
{
}
#endif
static inline struct device *
ac97_codec_dev2dev(struct ac97_codec_device *adev)
{
return &adev->dev;
}
static inline void *ac97_get_drvdata(struct ac97_codec_device *adev)
{
return dev_get_drvdata(ac97_codec_dev2dev(adev));
}
static inline void ac97_set_drvdata(struct ac97_codec_device *adev,
void *data)
{
dev_set_drvdata(ac97_codec_dev2dev(adev), data);
}
void *snd_ac97_codec_get_platdata(const struct ac97_codec_device *adev);
#endif
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
*
* 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.
*
* This file is for backward compatibility with snd_ac97 structure and its
* multiple usages, such as the snd_ac97_bus and snd_ac97_build_ops.
*
*/
#ifndef AC97_COMPAT_H
#define AC97_COMPAT_H
#include <sound/ac97_codec.h>
struct snd_ac97 *snd_ac97_compat_alloc(struct ac97_codec_device *adev);
void snd_ac97_compat_release(struct snd_ac97 *ac97);
#endif
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef AC97_CONTROLLER_H
#define AC97_CONTROLLER_H
#include <linux/device.h>
#include <linux/list.h>
#define AC97_BUS_MAX_CODECS 4
#define AC97_SLOTS_AVAILABLE_ALL 0xf
struct ac97_controller_ops;
/**
* struct ac97_controller - The AC97 controller of the AC-Link
* @ops: the AC97 operations.
* @controllers: linked list of all existing controllers.
* @adap: the shell device ac97-%d, ie. ac97 adapter
* @nr: the number of the shell device
* @slots_available: the mask of accessible/scanable codecs.
* @parent: the device providing the AC97 controller.
* @codecs: the 4 possible AC97 codecs (NULL if none found).
* @codecs_pdata: platform_data for each codec (NULL if no pdata).
*
* This structure is internal to AC97 bus, and should not be used by the
* controllers themselves, excepting for using @dev.
*/
struct ac97_controller {
const struct ac97_controller_ops *ops;
struct list_head controllers;
struct device adap;
int nr;
unsigned short slots_available;
struct device *parent;
struct ac97_codec_device *codecs[AC97_BUS_MAX_CODECS];
void *codecs_pdata[AC97_BUS_MAX_CODECS];
};
/**
* struct ac97_controller_ops - The AC97 operations
* @reset: Cold reset of the AC97 AC-Link.
* @warm_reset: Warm reset of the AC97 AC-Link.
* @read: Read of a single AC97 register.
* Returns the register value or a negative error code.
* @write: Write of a single AC97 register.
*
* These are the basic operation an AC97 controller must provide for an AC97
* access functions. Amongst these, all but the last 2 are mandatory.
* The slot number is also known as the AC97 codec number, between 0 and 3.
*/
struct ac97_controller_ops {
void (*reset)(struct ac97_controller *adrv);
void (*warm_reset)(struct ac97_controller *adrv);
int (*write)(struct ac97_controller *adrv, int slot,
unsigned short reg, unsigned short val);
int (*read)(struct ac97_controller *adrv, int slot, unsigned short reg);
};
#if IS_ENABLED(CONFIG_AC97_BUS_NEW)
struct ac97_controller *snd_ac97_controller_register(
const struct ac97_controller_ops *ops, struct device *dev,
unsigned short slots_available, void **codecs_pdata);
void snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl);
#else
static inline struct ac97_controller *
snd_ac97_controller_register(const struct ac97_controller_ops *ops,
struct device *dev,
unsigned short slots_available,
void **codecs_pdata)
{
return ERR_PTR(-ENODEV);
}
static inline void
snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl)
{
}
#endif
#endif
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.1
* by Intel Corporation (http://developer.intel.com).
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*
* AC'97 codec registers
*/
#define AC97_RESET 0x00 /* Reset */
#define AC97_MASTER 0x02 /* Master Volume */
#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */
#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */
#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */
#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */
#define AC97_PHONE 0x0c /* Phone Volume (optional) */
#define AC97_MIC 0x0e /* MIC Volume */
#define AC97_LINE 0x10 /* Line In Volume */
#define AC97_CD 0x12 /* CD Volume */
#define AC97_VIDEO 0x14 /* Video Volume (optional) */
#define AC97_AUX 0x16 /* AUX Volume (optional) */
#define AC97_PCM 0x18 /* PCM Volume */
#define AC97_REC_SEL 0x1a /* Record Select */
#define AC97_REC_GAIN 0x1c /* Record Gain */
#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */
#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */
#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */
#define AC97_INT_PAGING 0x24 /* Audio Interrupt & Paging (AC'97 2.3) */
#define AC97_POWERDOWN 0x26 /* Powerdown control / status */
/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */
#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */
#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */
#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */
#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */
#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */
#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */
#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */
#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */
#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */
#define AC97_SPDIF 0x3a /* S/PDIF control */
/* range 0x3c-0x58 - MODEM */
#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */
#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */
#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */
#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */
#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */
#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */
#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */
#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */
#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */
#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */
#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */
#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */
#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */
#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */
/* range 0x5a-0x7b - Vendor Specific */
#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */
#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */
/* range 0x60-0x6f (page 1) - extended codec registers */
#define AC97_CODEC_CLASS_REV 0x60 /* Codec Class/Revision */
#define AC97_PCI_SVID 0x62 /* PCI Subsystem Vendor ID */
#define AC97_PCI_SID 0x64 /* PCI Subsystem ID */
#define AC97_FUNC_SELECT 0x66 /* Function Select */
#define AC97_FUNC_INFO 0x68 /* Function Information */
#define AC97_SENSE_INFO 0x6a /* Sense Details */
/* volume controls */
#define AC97_MUTE_MASK_MONO 0x8000
#define AC97_MUTE_MASK_STEREO 0x8080
/* slot allocation */
#define AC97_SLOT_TAG 0
#define AC97_SLOT_CMD_ADDR 1
#define AC97_SLOT_CMD_DATA 2
#define AC97_SLOT_PCM_LEFT 3
#define AC97_SLOT_PCM_RIGHT 4
#define AC97_SLOT_MODEM_LINE1 5
#define AC97_SLOT_PCM_CENTER 6
#define AC97_SLOT_MIC 6 /* input */
#define AC97_SLOT_SPDIF_LEFT1 6
#define AC97_SLOT_PCM_SLEFT 7 /* surround left */
#define AC97_SLOT_PCM_LEFT_0 7 /* double rate operation */
#define AC97_SLOT_SPDIF_LEFT 7
#define AC97_SLOT_PCM_SRIGHT 8 /* surround right */
#define AC97_SLOT_PCM_RIGHT_0 8 /* double rate operation */
#define AC97_SLOT_SPDIF_RIGHT 8
#define AC97_SLOT_LFE 9
#define AC97_SLOT_SPDIF_RIGHT1 9
#define AC97_SLOT_MODEM_LINE2 10
#define AC97_SLOT_PCM_LEFT_1 10 /* double rate operation */
#define AC97_SLOT_SPDIF_LEFT2 10
#define AC97_SLOT_HANDSET 11 /* output */
#define AC97_SLOT_PCM_RIGHT_1 11 /* double rate operation */
#define AC97_SLOT_SPDIF_RIGHT2 11
#define AC97_SLOT_MODEM_GPIO 12 /* modem GPIO */
#define AC97_SLOT_PCM_CENTER_1 12 /* double rate operation */
/* basic capabilities (reset register) */
#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */
#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */
#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */
#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */
#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */
#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */
#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */
#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */
#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */
#define AC97_BC_DAC_MASK 0x00c0
#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */
#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */
#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */
#define AC97_BC_ADC_MASK 0x0300
#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */
/* general purpose */
#define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */
#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */
#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */
/* powerdown bits */
#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */
#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */
#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */
#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */
#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */
#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */
#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */
#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */
#define AC97_PD_PR4 0x1000 /* Power down AC-Link */
#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */
#define AC97_PD_PR6 0x4000 /* Headphone amplifier */
#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */
/* extended audio ID bit defines */
#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */
#define AC97_EI_DRA 0x0002 /* Double rate supported */
#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */
#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */
#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */
#define AC97_EI_DACS_SLOT_SHIFT 4
#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */
#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */
#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */
#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */
#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */
#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */
#define AC97_EI_REV_23 0x0800 /* AC'97 revision 2.3 */
#define AC97_EI_REV_SHIFT 10
#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */
#define AC97_EI_ADDR_SHIFT 14
/* extended audio status and control bit defines */
#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */
#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */
#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */
#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */
#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */
#define AC97_EA_SPSA_SLOT_SHIFT 4
#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */
#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */
#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */
#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */
#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */
#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */
#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */
#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */
#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */
#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */
#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */
#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */
#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */
/* S/PDIF control bit defines */
#define AC97_SC_PRO 0x0001 /* Professional status */
#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */
#define AC97_SC_COPY 0x0004 /* Copyright status */
#define AC97_SC_PRE 0x0008 /* Preemphasis status */
#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */
#define AC97_SC_CC_SHIFT 4
#define AC97_SC_L 0x0800 /* Generation Level status */
#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */
#define AC97_SC_SPSR_SHIFT 12
#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */
#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */
#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */
#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */
#define AC97_SC_V 0x8000 /* Validity status */
/* Interrupt and Paging bit defines (AC'97 2.3) */
#define AC97_PAGE_MASK 0x000f /* Page Selector */
#define AC97_PAGE_VENDOR 0 /* Vendor-specific registers */
#define AC97_PAGE_1 1 /* Extended Codec Registers page 1 */
#define AC97_INT_ENABLE 0x0800 /* Interrupt Enable */
#define AC97_INT_SENSE 0x1000 /* Sense Cycle */
#define AC97_INT_CAUSE_SENSE 0x2000 /* Sense Cycle Completed (RO) */
#define AC97_INT_CAUSE_GPIO 0x4000 /* GPIO bits changed (RO) */
#define AC97_INT_STATUS 0x8000 /* Interrupt Status */
/* extended modem ID bit defines */
#define AC97_MEI_LINE1 0x0001 /* Line1 present */
#define AC97_MEI_LINE2 0x0002 /* Line2 present */
#define AC97_MEI_HANDSET 0x0004 /* Handset present */
#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */
#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */
#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */
#define AC97_MEI_ADDR_SHIFT 14
/* extended modem status and control bit defines */
#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */
#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */
#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */
#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */
#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */
#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */
#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */
#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */
#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */
#define AC97_MEA_PRB 0x0200 /* reserved */
#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */
#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */
#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */
#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */
#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */
#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */
/* modem gpio status defines */
#define AC97_GPIO_LINE1_OH 0x0001 /* Off Hook Line1 */
#define AC97_GPIO_LINE1_RI 0x0002 /* Ring Detect Line1 */
#define AC97_GPIO_LINE1_CID 0x0004 /* Caller ID path enable Line1 */
#define AC97_GPIO_LINE1_LCS 0x0008 /* Loop Current Sense Line1 */
#define AC97_GPIO_LINE1_PULSE 0x0010 /* Opt./ Pulse Dial Line1 (out) */
#define AC97_GPIO_LINE1_HL1R 0x0020 /* Opt./ Handset to Line1 relay control (out) */
#define AC97_GPIO_LINE1_HOHD 0x0040 /* Opt./ Handset off hook detect Line1 (in) */
#define AC97_GPIO_LINE12_AC 0x0080 /* Opt./ Int.bit 1 / Line1/2 AC (out) */
#define AC97_GPIO_LINE12_DC 0x0100 /* Opt./ Int.bit 2 / Line1/2 DC (out) */
#define AC97_GPIO_LINE12_RS 0x0200 /* Opt./ Int.bit 3 / Line1/2 RS (out) */
#define AC97_GPIO_LINE2_OH 0x0400 /* Off Hook Line2 */
#define AC97_GPIO_LINE2_RI 0x0800 /* Ring Detect Line2 */
#define AC97_GPIO_LINE2_CID 0x1000 /* Caller ID path enable Line2 */
#define AC97_GPIO_LINE2_LCS 0x2000 /* Loop Current Sense Line2 */
#define AC97_GPIO_LINE2_PULSE 0x4000 /* Opt./ Pulse Dial Line2 (out) */
#define AC97_GPIO_LINE2_HL1R 0x8000 /* Opt./ Handset to Line2 relay control (out) */
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <sound/ac97/regs.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/info.h> #include <sound/info.h>
...@@ -35,244 +36,6 @@ ...@@ -35,244 +36,6 @@
/* maximum number of devices on the AC97 bus */ /* maximum number of devices on the AC97 bus */
#define AC97_BUS_MAX_DEVICES 4 #define AC97_BUS_MAX_DEVICES 4
/*
* AC'97 codec registers
*/
#define AC97_RESET 0x00 /* Reset */
#define AC97_MASTER 0x02 /* Master Volume */
#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */
#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */
#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */
#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */
#define AC97_PHONE 0x0c /* Phone Volume (optional) */
#define AC97_MIC 0x0e /* MIC Volume */
#define AC97_LINE 0x10 /* Line In Volume */
#define AC97_CD 0x12 /* CD Volume */
#define AC97_VIDEO 0x14 /* Video Volume (optional) */
#define AC97_AUX 0x16 /* AUX Volume (optional) */
#define AC97_PCM 0x18 /* PCM Volume */
#define AC97_REC_SEL 0x1a /* Record Select */
#define AC97_REC_GAIN 0x1c /* Record Gain */
#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */
#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */
#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */
#define AC97_INT_PAGING 0x24 /* Audio Interrupt & Paging (AC'97 2.3) */
#define AC97_POWERDOWN 0x26 /* Powerdown control / status */
/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */
#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */
#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */
#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */
#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */
#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */
#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */
#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */
#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */
#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */
#define AC97_SPDIF 0x3a /* S/PDIF control */
/* range 0x3c-0x58 - MODEM */
#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */
#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */
#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */
#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */
#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */
#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */
#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */
#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */
#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */
#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */
#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */
#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */
#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */
#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */
/* range 0x5a-0x7b - Vendor Specific */
#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */
#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */
/* range 0x60-0x6f (page 1) - extended codec registers */
#define AC97_CODEC_CLASS_REV 0x60 /* Codec Class/Revision */
#define AC97_PCI_SVID 0x62 /* PCI Subsystem Vendor ID */
#define AC97_PCI_SID 0x64 /* PCI Subsystem ID */
#define AC97_FUNC_SELECT 0x66 /* Function Select */
#define AC97_FUNC_INFO 0x68 /* Function Information */
#define AC97_SENSE_INFO 0x6a /* Sense Details */
/* volume controls */
#define AC97_MUTE_MASK_MONO 0x8000
#define AC97_MUTE_MASK_STEREO 0x8080
/* slot allocation */
#define AC97_SLOT_TAG 0
#define AC97_SLOT_CMD_ADDR 1
#define AC97_SLOT_CMD_DATA 2
#define AC97_SLOT_PCM_LEFT 3
#define AC97_SLOT_PCM_RIGHT 4
#define AC97_SLOT_MODEM_LINE1 5
#define AC97_SLOT_PCM_CENTER 6
#define AC97_SLOT_MIC 6 /* input */
#define AC97_SLOT_SPDIF_LEFT1 6
#define AC97_SLOT_PCM_SLEFT 7 /* surround left */
#define AC97_SLOT_PCM_LEFT_0 7 /* double rate operation */
#define AC97_SLOT_SPDIF_LEFT 7
#define AC97_SLOT_PCM_SRIGHT 8 /* surround right */
#define AC97_SLOT_PCM_RIGHT_0 8 /* double rate operation */
#define AC97_SLOT_SPDIF_RIGHT 8
#define AC97_SLOT_LFE 9
#define AC97_SLOT_SPDIF_RIGHT1 9
#define AC97_SLOT_MODEM_LINE2 10
#define AC97_SLOT_PCM_LEFT_1 10 /* double rate operation */
#define AC97_SLOT_SPDIF_LEFT2 10
#define AC97_SLOT_HANDSET 11 /* output */
#define AC97_SLOT_PCM_RIGHT_1 11 /* double rate operation */
#define AC97_SLOT_SPDIF_RIGHT2 11
#define AC97_SLOT_MODEM_GPIO 12 /* modem GPIO */
#define AC97_SLOT_PCM_CENTER_1 12 /* double rate operation */
/* basic capabilities (reset register) */
#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */
#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */
#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */
#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */
#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */
#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */
#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */
#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */
#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */
#define AC97_BC_DAC_MASK 0x00c0
#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */
#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */
#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */
#define AC97_BC_ADC_MASK 0x0300
#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */
/* general purpose */
#define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */
#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */
#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */
/* powerdown bits */
#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */
#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */
#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */
#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */
#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */
#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */
#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */
#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */
#define AC97_PD_PR4 0x1000 /* Power down AC-Link */
#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */
#define AC97_PD_PR6 0x4000 /* Headphone amplifier */
#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */
/* extended audio ID bit defines */
#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */
#define AC97_EI_DRA 0x0002 /* Double rate supported */
#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */
#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */
#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */
#define AC97_EI_DACS_SLOT_SHIFT 4
#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */
#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */
#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */
#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */
#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */
#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */
#define AC97_EI_REV_23 0x0800 /* AC'97 revision 2.3 */
#define AC97_EI_REV_SHIFT 10
#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */
#define AC97_EI_ADDR_SHIFT 14
/* extended audio status and control bit defines */
#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */
#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */
#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */
#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */
#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */
#define AC97_EA_SPSA_SLOT_SHIFT 4
#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */
#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */
#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */
#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */
#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */
#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */
#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */
#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */
#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */
#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */
#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */
#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */
#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */
/* S/PDIF control bit defines */
#define AC97_SC_PRO 0x0001 /* Professional status */
#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */
#define AC97_SC_COPY 0x0004 /* Copyright status */
#define AC97_SC_PRE 0x0008 /* Preemphasis status */
#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */
#define AC97_SC_CC_SHIFT 4
#define AC97_SC_L 0x0800 /* Generation Level status */
#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */
#define AC97_SC_SPSR_SHIFT 12
#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */
#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */
#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */
#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */
#define AC97_SC_V 0x8000 /* Validity status */
/* Interrupt and Paging bit defines (AC'97 2.3) */
#define AC97_PAGE_MASK 0x000f /* Page Selector */
#define AC97_PAGE_VENDOR 0 /* Vendor-specific registers */
#define AC97_PAGE_1 1 /* Extended Codec Registers page 1 */
#define AC97_INT_ENABLE 0x0800 /* Interrupt Enable */
#define AC97_INT_SENSE 0x1000 /* Sense Cycle */
#define AC97_INT_CAUSE_SENSE 0x2000 /* Sense Cycle Completed (RO) */
#define AC97_INT_CAUSE_GPIO 0x4000 /* GPIO bits changed (RO) */
#define AC97_INT_STATUS 0x8000 /* Interrupt Status */
/* extended modem ID bit defines */
#define AC97_MEI_LINE1 0x0001 /* Line1 present */
#define AC97_MEI_LINE2 0x0002 /* Line2 present */
#define AC97_MEI_HANDSET 0x0004 /* Handset present */
#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */
#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */
#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */
#define AC97_MEI_ADDR_SHIFT 14
/* extended modem status and control bit defines */
#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */
#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */
#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */
#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */
#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */
#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */
#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */
#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */
#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */
#define AC97_MEA_PRB 0x0200 /* reserved */
#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */
#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */
#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */
#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */
#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */
#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */
/* modem gpio status defines */
#define AC97_GPIO_LINE1_OH 0x0001 /* Off Hook Line1 */
#define AC97_GPIO_LINE1_RI 0x0002 /* Ring Detect Line1 */
#define AC97_GPIO_LINE1_CID 0x0004 /* Caller ID path enable Line1 */
#define AC97_GPIO_LINE1_LCS 0x0008 /* Loop Current Sense Line1 */
#define AC97_GPIO_LINE1_PULSE 0x0010 /* Opt./ Pulse Dial Line1 (out) */
#define AC97_GPIO_LINE1_HL1R 0x0020 /* Opt./ Handset to Line1 relay control (out) */
#define AC97_GPIO_LINE1_HOHD 0x0040 /* Opt./ Handset off hook detect Line1 (in) */
#define AC97_GPIO_LINE12_AC 0x0080 /* Opt./ Int.bit 1 / Line1/2 AC (out) */
#define AC97_GPIO_LINE12_DC 0x0100 /* Opt./ Int.bit 2 / Line1/2 DC (out) */
#define AC97_GPIO_LINE12_RS 0x0200 /* Opt./ Int.bit 3 / Line1/2 RS (out) */
#define AC97_GPIO_LINE2_OH 0x0400 /* Off Hook Line2 */
#define AC97_GPIO_LINE2_RI 0x0800 /* Ring Detect Line2 */
#define AC97_GPIO_LINE2_CID 0x1000 /* Caller ID path enable Line2 */
#define AC97_GPIO_LINE2_LCS 0x2000 /* Loop Current Sense Line2 */
#define AC97_GPIO_LINE2_PULSE 0x4000 /* Opt./ Pulse Dial Line2 (out) */
#define AC97_GPIO_LINE2_HL1R 0x8000 /* Opt./ Handset to Line2 relay control (out) */
/* specific - SigmaTel */ /* specific - SigmaTel */
#define AC97_SIGMATEL_OUTSEL 0x64 /* Output Select, STAC9758 */ #define AC97_SIGMATEL_OUTSEL 0x64 /* Output Select, STAC9758 */
#define AC97_SIGMATEL_INSEL 0x66 /* Input Select, STAC9758 */ #define AC97_SIGMATEL_INSEL 0x66 /* Input Select, STAC9758 */
......
...@@ -2,10 +2,13 @@ ...@@ -2,10 +2,13 @@
#ifndef PXA2XX_LIB_H #ifndef PXA2XX_LIB_H
#define PXA2XX_LIB_H #define PXA2XX_LIB_H
#include <uapi/sound/asound.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <sound/ac97_codec.h>
/* PCM */ /* PCM */
struct snd_pcm_substream;
struct snd_pcm_hw_params;
struct snd_pcm;
extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params); struct snd_pcm_hw_params *params);
...@@ -22,12 +25,12 @@ extern void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm); ...@@ -22,12 +25,12 @@ extern void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm);
/* AC97 */ /* AC97 */
extern unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg); extern int pxa2xx_ac97_read(int slot, unsigned short reg);
extern void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val); extern int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val);
extern bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97); extern bool pxa2xx_ac97_try_warm_reset(void);
extern bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97); extern bool pxa2xx_ac97_try_cold_reset(void);
extern void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97); extern void pxa2xx_ac97_finish_reset(void);
extern int pxa2xx_ac97_hw_suspend(void); extern int pxa2xx_ac97_hw_suspend(void);
extern int pxa2xx_ac97_hw_resume(void); extern int pxa2xx_ac97_hw_resume(void);
......
...@@ -80,6 +80,8 @@ source "sound/hda/Kconfig" ...@@ -80,6 +80,8 @@ source "sound/hda/Kconfig"
source "sound/ppc/Kconfig" source "sound/ppc/Kconfig"
source "sound/ac97/Kconfig"
source "sound/aoa/Kconfig" source "sound/aoa/Kconfig"
source "sound/arm/Kconfig" source "sound/arm/Kconfig"
......
...@@ -11,6 +11,7 @@ obj-$(CONFIG_SND_AOA) += aoa/ ...@@ -11,6 +11,7 @@ obj-$(CONFIG_SND_AOA) += aoa/
# This one must be compilable even if sound is configured out # This one must be compilable even if sound is configured out
obj-$(CONFIG_AC97_BUS) += ac97_bus.o obj-$(CONFIG_AC97_BUS) += ac97_bus.o
obj-$(CONFIG_AC97_BUS_NEW) += ac97/
ifeq ($(CONFIG_SND),y) ifeq ($(CONFIG_SND),y)
obj-y += last.o obj-y += last.o
......
#
# AC97 configuration
#
config AC97_BUS_NEW
tristate
select AC97
help
This is the new AC97 bus type, successor of AC97_BUS. The ported
drivers which benefit from the AC97 automatic probing should "select"
this instead of the AC97_BUS.
Say Y here if you want to have AC97 devices, which are sound oriented
devices around an AC-Link.
config AC97_BUS_COMPAT
bool
depends on AC97_BUS_NEW
depends on !AC97_BUS
#
# make for AC97 bus drivers
#
obj-$(CONFIG_AC97_BUS_NEW) += ac97.o
ac97-y += bus.o codec.o
ac97-$(CONFIG_AC97_BUS_COMPAT) += snd_ac97_compat.o
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
*
* 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.
*/
unsigned int snd_ac97_bus_scan_one(struct ac97_controller *ac97,
unsigned int codec_num);
static inline bool ac97_ids_match(unsigned int id1, unsigned int id2,
unsigned int mask)
{
return (id1 & mask) == (id2 & mask);
}
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
*
* 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/module.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <sound/ac97/codec.h>
#include <sound/ac97/controller.h>
#include <sound/ac97/regs.h>
#include "ac97_core.h"
/*
* Protects ac97_controllers and each ac97_controller structure.
*/
static DEFINE_MUTEX(ac97_controllers_mutex);
static DEFINE_IDR(ac97_adapter_idr);
static LIST_HEAD(ac97_controllers);
static struct bus_type ac97_bus_type;
static inline struct ac97_controller*
to_ac97_controller(struct device *ac97_adapter)
{
return container_of(ac97_adapter, struct ac97_controller, adap);
}
static int ac97_unbound_ctrl_write(struct ac97_controller *adrv, int slot,
unsigned short reg, unsigned short val)
{
return -ENODEV;
}
static int ac97_unbound_ctrl_read(struct ac97_controller *adrv, int slot,
unsigned short reg)
{
return -ENODEV;
}
static const struct ac97_controller_ops ac97_unbound_ctrl_ops = {
.write = ac97_unbound_ctrl_write,
.read = ac97_unbound_ctrl_read,
};
static struct ac97_controller ac97_unbound_ctrl = {
.ops = &ac97_unbound_ctrl_ops,
};
static struct ac97_codec_device *
ac97_codec_find(struct ac97_controller *ac97_ctrl, unsigned int codec_num)
{
if (codec_num >= AC97_BUS_MAX_CODECS)
return ERR_PTR(-EINVAL);
return ac97_ctrl->codecs[codec_num];
}
static void ac97_codec_release(struct device *dev)
{
struct ac97_codec_device *adev;
struct ac97_controller *ac97_ctrl;
adev = to_ac97_device(dev);
ac97_ctrl = adev->ac97_ctrl;
ac97_ctrl->codecs[adev->num] = NULL;
kfree(adev);
}
static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx,
unsigned int vendor_id)
{
struct ac97_codec_device *codec;
int ret;
codec = kzalloc(sizeof(*codec), GFP_KERNEL);
if (!codec)
return -ENOMEM;
ac97_ctrl->codecs[idx] = codec;
codec->vendor_id = vendor_id;
codec->dev.release = ac97_codec_release;
codec->dev.bus = &ac97_bus_type;
codec->dev.parent = &ac97_ctrl->adap;
codec->num = idx;
codec->ac97_ctrl = ac97_ctrl;
device_initialize(&codec->dev);
dev_set_name(&codec->dev, "%s:%u", dev_name(ac97_ctrl->parent), idx);
ret = device_add(&codec->dev);
if (ret)
goto err_free_codec;
return 0;
err_free_codec:
put_device(&codec->dev);
kfree(codec);
ac97_ctrl->codecs[idx] = NULL;
return ret;
}
unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv,
unsigned int codec_num)
{
unsigned short vid1, vid2;
int ret;
ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID1);
vid1 = (ret & 0xffff);
if (ret < 0)
return 0;
ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID2);
vid2 = (ret & 0xffff);
if (ret < 0)
return 0;
dev_dbg(&adrv->adap, "%s(codec_num=%u): vendor_id=0x%08x\n",
__func__, codec_num, AC97_ID(vid1, vid2));
return AC97_ID(vid1, vid2);
}
static int ac97_bus_scan(struct ac97_controller *ac97_ctrl)
{
int ret, i;
unsigned int vendor_id;
for (i = 0; i < AC97_BUS_MAX_CODECS; i++) {
if (ac97_codec_find(ac97_ctrl, i))
continue;
if (!(ac97_ctrl->slots_available & BIT(i)))
continue;
vendor_id = snd_ac97_bus_scan_one(ac97_ctrl, i);
if (!vendor_id)
continue;
ret = ac97_codec_add(ac97_ctrl, i, vendor_id);
if (ret < 0)
return ret;
}
return 0;
}
static int ac97_bus_reset(struct ac97_controller *ac97_ctrl)
{
ac97_ctrl->ops->reset(ac97_ctrl);
return 0;
}
/**
* snd_ac97_codec_driver_register - register an AC97 codec driver
* @dev: AC97 driver codec to register
*
* Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital
* controller.
*
* Returns 0 on success or error code
*/
int snd_ac97_codec_driver_register(struct ac97_codec_driver *drv)
{
drv->driver.bus = &ac97_bus_type;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register);
/**
* snd_ac97_codec_driver_unregister - unregister an AC97 codec driver
* @dev: AC97 codec driver to unregister
*
* Unregister a previously registered ac97 codec driver.
*/
void snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_unregister);
/**
* snd_ac97_codec_get_platdata - get platform_data
* @adev: the ac97 codec device
*
* For legacy platforms, in order to have platform_data in codec drivers
* available, while ac97 device are auto-created upon probe, this retrieves the
* platdata which was setup on ac97 controller registration.
*
* Returns the platform data pointer
*/
void *snd_ac97_codec_get_platdata(const struct ac97_codec_device *adev)
{
struct ac97_controller *ac97_ctrl = adev->ac97_ctrl;
return ac97_ctrl->codecs_pdata[adev->num];
}
EXPORT_SYMBOL_GPL(snd_ac97_codec_get_platdata);
static void ac97_ctrl_codecs_unregister(struct ac97_controller *ac97_ctrl)
{
int i;
for (i = 0; i < AC97_BUS_MAX_CODECS; i++)
if (ac97_ctrl->codecs[i]) {
ac97_ctrl->codecs[i]->ac97_ctrl = &ac97_unbound_ctrl;
device_unregister(&ac97_ctrl->codecs[i]->dev);
}
}
static ssize_t cold_reset_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t len)
{
struct ac97_controller *ac97_ctrl;
mutex_lock(&ac97_controllers_mutex);
ac97_ctrl = to_ac97_controller(dev);
ac97_ctrl->ops->reset(ac97_ctrl);
mutex_unlock(&ac97_controllers_mutex);
return len;
}
static DEVICE_ATTR_WO(cold_reset);
static ssize_t warm_reset_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t len)
{
struct ac97_controller *ac97_ctrl;
if (!dev)
return -ENODEV;
mutex_lock(&ac97_controllers_mutex);
ac97_ctrl = to_ac97_controller(dev);
ac97_ctrl->ops->warm_reset(ac97_ctrl);
mutex_unlock(&ac97_controllers_mutex);
return len;
}
static DEVICE_ATTR_WO(warm_reset);
static struct attribute *ac97_controller_device_attrs[] = {
&dev_attr_cold_reset.attr,
&dev_attr_warm_reset.attr,
NULL
};
static struct attribute_group ac97_adapter_attr_group = {
.name = "ac97_operations",
.attrs = ac97_controller_device_attrs,
};
static const struct attribute_group *ac97_adapter_groups[] = {
&ac97_adapter_attr_group,
NULL,
};
static void ac97_del_adapter(struct ac97_controller *ac97_ctrl)
{
mutex_lock(&ac97_controllers_mutex);
ac97_ctrl_codecs_unregister(ac97_ctrl);
list_del(&ac97_ctrl->controllers);
mutex_unlock(&ac97_controllers_mutex);
device_unregister(&ac97_ctrl->adap);
}
static void ac97_adapter_release(struct device *dev)
{
struct ac97_controller *ac97_ctrl;
ac97_ctrl = to_ac97_controller(dev);
idr_remove(&ac97_adapter_idr, ac97_ctrl->nr);
dev_dbg(&ac97_ctrl->adap, "adapter unregistered by %s\n",
dev_name(ac97_ctrl->parent));
}
static const struct device_type ac97_adapter_type = {
.groups = ac97_adapter_groups,
.release = ac97_adapter_release,
};
static int ac97_add_adapter(struct ac97_controller *ac97_ctrl)
{
int ret;
mutex_lock(&ac97_controllers_mutex);
ret = idr_alloc(&ac97_adapter_idr, ac97_ctrl, 0, 0, GFP_KERNEL);
ac97_ctrl->nr = ret;
if (ret >= 0) {
dev_set_name(&ac97_ctrl->adap, "ac97-%d", ret);
ac97_ctrl->adap.type = &ac97_adapter_type;
ac97_ctrl->adap.parent = ac97_ctrl->parent;
ret = device_register(&ac97_ctrl->adap);
if (ret)
put_device(&ac97_ctrl->adap);
}
if (!ret)
list_add(&ac97_ctrl->controllers, &ac97_controllers);
mutex_unlock(&ac97_controllers_mutex);
if (!ret)
dev_dbg(&ac97_ctrl->adap, "adapter registered by %s\n",
dev_name(ac97_ctrl->parent));
return ret;
}
/**
* snd_ac97_controller_register - register an ac97 controller
* @ops: the ac97 bus operations
* @dev: the device providing the ac97 DC function
* @slots_available: mask of the ac97 codecs that can be scanned and probed
* bit0 => codec 0, bit1 => codec 1 ... bit 3 => codec 3
*
* Register a digital controller which can control up to 4 ac97 codecs. This is
* the controller side of the AC97 AC-link, while the slave side are the codecs.
*
* Returns a valid controller upon success, negative pointer value upon error
*/
struct ac97_controller *snd_ac97_controller_register(
const struct ac97_controller_ops *ops, struct device *dev,
unsigned short slots_available, void **codecs_pdata)
{
struct ac97_controller *ac97_ctrl;
int ret, i;
ac97_ctrl = kzalloc(sizeof(*ac97_ctrl), GFP_KERNEL);
if (!ac97_ctrl)
return ERR_PTR(-ENOMEM);
for (i = 0; i < AC97_BUS_MAX_CODECS && codecs_pdata; i++)
ac97_ctrl->codecs_pdata[i] = codecs_pdata[i];
ac97_ctrl->ops = ops;
ac97_ctrl->slots_available = slots_available;
ac97_ctrl->parent = dev;
ret = ac97_add_adapter(ac97_ctrl);
if (ret)
goto err;
ac97_bus_reset(ac97_ctrl);
ac97_bus_scan(ac97_ctrl);
return ac97_ctrl;
err:
kfree(ac97_ctrl);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(snd_ac97_controller_register);
/**
* snd_ac97_controller_unregister - unregister an ac97 controller
* @ac97_ctrl: the device previously provided to ac97_controller_register()
*
*/
void snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl)
{
ac97_del_adapter(ac97_ctrl);
}
EXPORT_SYMBOL_GPL(snd_ac97_controller_unregister);
#ifdef CONFIG_PM
static int ac97_pm_runtime_suspend(struct device *dev)
{
struct ac97_codec_device *codec = to_ac97_device(dev);
int ret = pm_generic_runtime_suspend(dev);
if (ret == 0 && dev->driver) {
if (pm_runtime_is_irq_safe(dev))
clk_disable(codec->clk);
else
clk_disable_unprepare(codec->clk);
}
return ret;
}
static int ac97_pm_runtime_resume(struct device *dev)
{
struct ac97_codec_device *codec = to_ac97_device(dev);
int ret;
if (dev->driver) {
if (pm_runtime_is_irq_safe(dev))
ret = clk_enable(codec->clk);
else
ret = clk_prepare_enable(codec->clk);
if (ret)
return ret;
}
return pm_generic_runtime_resume(dev);
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops ac97_pm = {
.suspend = pm_generic_suspend,
.resume = pm_generic_resume,
.freeze = pm_generic_freeze,
.thaw = pm_generic_thaw,
.poweroff = pm_generic_poweroff,
.restore = pm_generic_restore,
SET_RUNTIME_PM_OPS(
ac97_pm_runtime_suspend,
ac97_pm_runtime_resume,
NULL)
};
static int ac97_get_enable_clk(struct ac97_codec_device *adev)
{
int ret;
adev->clk = clk_get(&adev->dev, "ac97_clk");
if (IS_ERR(adev->clk))
return PTR_ERR(adev->clk);
ret = clk_prepare_enable(adev->clk);
if (ret)
clk_put(adev->clk);
return ret;
}
static void ac97_put_disable_clk(struct ac97_codec_device *adev)
{
clk_disable_unprepare(adev->clk);
clk_put(adev->clk);
}
static ssize_t vendor_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ac97_codec_device *codec = to_ac97_device(dev);
return sprintf(buf, "%08x", codec->vendor_id);
}
DEVICE_ATTR_RO(vendor_id);
static struct attribute *ac97_dev_attrs[] = {
&dev_attr_vendor_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(ac97_dev);
static int ac97_bus_match(struct device *dev, struct device_driver *drv)
{
struct ac97_codec_device *adev = to_ac97_device(dev);
struct ac97_codec_driver *adrv = to_ac97_driver(drv);
const struct ac97_id *id = adrv->id_table;
int i = 0;
if (adev->vendor_id == 0x0 || adev->vendor_id == 0xffffffff)
return false;
do {
if (ac97_ids_match(id[i].id, adev->vendor_id, id[i].mask))
return true;
} while (id[i++].id);
return false;
}
static int ac97_bus_probe(struct device *dev)
{
struct ac97_codec_device *adev = to_ac97_device(dev);
struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
int ret;
ret = ac97_get_enable_clk(adev);
if (ret)
return ret;
pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
ret = adrv->probe(adev);
if (ret == 0)
return 0;
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_put_noidle(dev);
ac97_put_disable_clk(adev);
return ret;
}
static int ac97_bus_remove(struct device *dev)
{
struct ac97_codec_device *adev = to_ac97_device(dev);
struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
int ret;
ret = pm_runtime_get_sync(dev);
if (ret)
return ret;
ret = adrv->remove(adev);
pm_runtime_put_noidle(dev);
if (ret == 0)
ac97_put_disable_clk(adev);
return ret;
}
static struct bus_type ac97_bus_type = {
.name = "ac97bus",
.dev_groups = ac97_dev_groups,
.match = ac97_bus_match,
.pm = &ac97_pm,
.probe = ac97_bus_probe,
.remove = ac97_bus_remove,
};
static int __init ac97_bus_init(void)
{
return bus_register(&ac97_bus_type);
}
subsys_initcall(ac97_bus_init);
static void __exit ac97_bus_exit(void)
{
bus_unregister(&ac97_bus_type);
}
module_exit(ac97_bus_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
*
* 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 <sound/ac97_codec.h>
#include <sound/ac97/codec.h>
#include <sound/ac97/controller.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <sound/soc.h> /* For compat_ac97_* */
/*
* Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
*
* 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/list.h>
#include <linux/slab.h>
#include <sound/ac97/codec.h>
#include <sound/ac97/compat.h>
#include <sound/ac97/controller.h>
#include <sound/soc.h>
#include "ac97_core.h"
static void compat_ac97_reset(struct snd_ac97 *ac97)
{
struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
struct ac97_controller *actrl = adev->ac97_ctrl;
if (actrl->ops->reset)
actrl->ops->reset(actrl);
}
static void compat_ac97_warm_reset(struct snd_ac97 *ac97)
{
struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
struct ac97_controller *actrl = adev->ac97_ctrl;
if (actrl->ops->warm_reset)
actrl->ops->warm_reset(actrl);
}
static void compat_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
struct ac97_controller *actrl = adev->ac97_ctrl;
actrl->ops->write(actrl, ac97->num, reg, val);
}
static unsigned short compat_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
struct ac97_controller *actrl = adev->ac97_ctrl;
return actrl->ops->read(actrl, ac97->num, reg);
}
static struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = {
.reset = compat_ac97_reset,
.warm_reset = compat_ac97_warm_reset,
.write = compat_ac97_write,
.read = compat_ac97_read,
};
static struct snd_ac97_bus compat_soc_ac97_bus = {
.ops = &compat_snd_ac97_bus_ops,
};
struct snd_ac97 *snd_ac97_compat_alloc(struct ac97_codec_device *adev)
{
struct snd_ac97 *ac97;
ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
if (ac97 == NULL)
return ERR_PTR(-ENOMEM);
ac97->dev = adev->dev;
ac97->private_data = adev;
ac97->bus = &compat_soc_ac97_bus;
return ac97;
}
EXPORT_SYMBOL_GPL(snd_ac97_compat_alloc);
void snd_ac97_compat_release(struct snd_ac97 *ac97)
{
kfree(ac97);
}
EXPORT_SYMBOL_GPL(snd_ac97_compat_release);
int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
unsigned int id_mask)
{
struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
struct ac97_controller *actrl = adev->ac97_ctrl;
unsigned int scanned;
if (try_warm) {
compat_ac97_warm_reset(ac97);
scanned = snd_ac97_bus_scan_one(actrl, adev->num);
if (ac97_ids_match(scanned, adev->vendor_id, id_mask))
return 1;
}
compat_ac97_reset(ac97);
compat_ac97_warm_reset(ac97);
scanned = snd_ac97_bus_scan_one(actrl, adev->num);
if (ac97_ids_match(scanned, adev->vendor_id, id_mask))
return 0;
return -ENODEV;
}
EXPORT_SYMBOL_GPL(snd_ac97_reset);
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <sound/ac97_codec.h>
#include <sound/pxa2xx-lib.h> #include <sound/pxa2xx-lib.h>
#include <mach/irqs.h> #include <mach/irqs.h>
...@@ -46,38 +45,41 @@ extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio); ...@@ -46,38 +45,41 @@ extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio);
* 1 jiffy timeout if interrupt never comes). * 1 jiffy timeout if interrupt never comes).
*/ */
unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) int pxa2xx_ac97_read(int slot, unsigned short reg)
{ {
unsigned short val = -1; int val = -ENODEV;
volatile u32 *reg_addr; volatile u32 *reg_addr;
if (slot > 0)
return -ENODEV;
mutex_lock(&car_mutex); mutex_lock(&car_mutex);
/* set up primary or secondary codec space */ /* set up primary or secondary codec space */
if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS) if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; reg_addr = slot ? &SMC_REG_BASE : &PMC_REG_BASE;
else else
reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; reg_addr = slot ? &SAC_REG_BASE : &PAC_REG_BASE;
reg_addr += (reg >> 1); reg_addr += (reg >> 1);
/* start read access across the ac97 link */ /* start read access across the ac97 link */
GSR = GSR_CDONE | GSR_SDONE; GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0; gsr_bits = 0;
val = *reg_addr; val = (*reg_addr & 0xffff);
if (reg == AC97_GPIO_STATUS) if (reg == AC97_GPIO_STATUS)
goto out; goto out;
if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 && if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
!((GSR | gsr_bits) & GSR_SDONE)) { !((GSR | gsr_bits) & GSR_SDONE)) {
printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
__func__, reg, GSR | gsr_bits); __func__, reg, GSR | gsr_bits);
val = -1; val = -ETIMEDOUT;
goto out; goto out;
} }
/* valid data now */ /* valid data now */
GSR = GSR_CDONE | GSR_SDONE; GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0; gsr_bits = 0;
val = *reg_addr; val = (*reg_addr & 0xffff);
/* but we've just started another cycle... */ /* but we've just started another cycle... */
wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
...@@ -86,29 +88,32 @@ out: mutex_unlock(&car_mutex); ...@@ -86,29 +88,32 @@ out: mutex_unlock(&car_mutex);
} }
EXPORT_SYMBOL_GPL(pxa2xx_ac97_read); EXPORT_SYMBOL_GPL(pxa2xx_ac97_read);
void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val)
unsigned short val)
{ {
volatile u32 *reg_addr; volatile u32 *reg_addr;
int ret = 0;
mutex_lock(&car_mutex); mutex_lock(&car_mutex);
/* set up primary or secondary codec space */ /* set up primary or secondary codec space */
if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS) if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; reg_addr = slot ? &SMC_REG_BASE : &PMC_REG_BASE;
else else
reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; reg_addr = slot ? &SAC_REG_BASE : &PAC_REG_BASE;
reg_addr += (reg >> 1); reg_addr += (reg >> 1);
GSR = GSR_CDONE | GSR_SDONE; GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0; gsr_bits = 0;
*reg_addr = val; *reg_addr = val;
if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 && if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
!((GSR | gsr_bits) & GSR_CDONE)) !((GSR | gsr_bits) & GSR_CDONE)) {
printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
__func__, reg, GSR | gsr_bits); __func__, reg, GSR | gsr_bits);
ret = -EIO;
}
mutex_unlock(&car_mutex); mutex_unlock(&car_mutex);
return ret;
} }
EXPORT_SYMBOL_GPL(pxa2xx_ac97_write); EXPORT_SYMBOL_GPL(pxa2xx_ac97_write);
...@@ -188,7 +193,7 @@ static inline void pxa_ac97_cold_pxa3xx(void) ...@@ -188,7 +193,7 @@ static inline void pxa_ac97_cold_pxa3xx(void)
} }
#endif #endif
bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) bool pxa2xx_ac97_try_warm_reset(void)
{ {
unsigned long gsr; unsigned long gsr;
unsigned int timeout = 100; unsigned int timeout = 100;
...@@ -225,7 +230,7 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) ...@@ -225,7 +230,7 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
} }
EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset); EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset);
bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) bool pxa2xx_ac97_try_cold_reset(void)
{ {
unsigned long gsr; unsigned long gsr;
unsigned int timeout = 1000; unsigned int timeout = 1000;
...@@ -263,7 +268,7 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) ...@@ -263,7 +268,7 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset); EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset);
void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97) void pxa2xx_ac97_finish_reset(void)
{ {
GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
GCR |= GCR_SDONE_IE|GCR_CDONE_IE; GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
......
...@@ -29,19 +29,38 @@ ...@@ -29,19 +29,38 @@
#include "pxa2xx-pcm.h" #include "pxa2xx-pcm.h"
static void pxa2xx_ac97_reset(struct snd_ac97 *ac97) static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97)
{ {
if (!pxa2xx_ac97_try_cold_reset(ac97)) { if (!pxa2xx_ac97_try_cold_reset())
pxa2xx_ac97_try_warm_reset(ac97); pxa2xx_ac97_try_warm_reset();
}
pxa2xx_ac97_finish_reset();
}
static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
unsigned short reg)
{
int ret;
ret = pxa2xx_ac97_read(ac97->num, reg);
if (ret < 0)
return 0;
else
return (unsigned short)(ret & 0xffff);
}
static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
int __always_unused ret;
pxa2xx_ac97_finish_reset(ac97); ret = pxa2xx_ac97_write(ac97->num, reg, val);
} }
static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.read = pxa2xx_ac97_read, .read = pxa2xx_ac97_legacy_read,
.write = pxa2xx_ac97_write, .write = pxa2xx_ac97_legacy_write,
.reset = pxa2xx_ac97_reset, .reset = pxa2xx_ac97_legacy_reset,
}; };
static struct pxad_param pxa2xx_ac97_pcm_out_req = { static struct pxad_param pxa2xx_ac97_pcm_out_req = {
......
...@@ -2,3 +2,10 @@ config SND_SOC_AMD_ACP ...@@ -2,3 +2,10 @@ config SND_SOC_AMD_ACP
tristate "AMD Audio Coprocessor support" tristate "AMD Audio Coprocessor support"
help help
This option enables ACP DMA support on AMD platform. This option enables ACP DMA support on AMD platform.
config SND_SOC_AMD_CZ_RT5645_MACH
tristate "AMD CZ support for RT5645"
select SND_SOC_RT5645
depends on SND_SOC_AMD_ACP && I2C
help
This option enables machine driver for rt5645.
snd-soc-acp-pcm-objs := acp-pcm-dma.o acp_audio_dma-objs := acp-pcm-dma.o
snd-soc-acp-rt5645-mach-objs := acp-rt5645.o
obj-$(CONFIG_SND_SOC_AMD_ACP) += snd-soc-acp-pcm.o obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o
obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <drm/amd_asic_type.h>
#include "acp.h" #include "acp.h"
#define PLAYBACK_MIN_NUM_PERIODS 2 #define PLAYBACK_MIN_NUM_PERIODS 2
...@@ -35,6 +35,13 @@ ...@@ -35,6 +35,13 @@
#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) #define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
#define MIN_BUFFER MAX_BUFFER #define MIN_BUFFER MAX_BUFFER
#define ST_PLAYBACK_MAX_PERIOD_SIZE 8192
#define ST_CAPTURE_MAX_PERIOD_SIZE ST_PLAYBACK_MAX_PERIOD_SIZE
#define ST_MAX_BUFFER (ST_PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
#define ST_MIN_BUFFER ST_MAX_BUFFER
#define DRV_NAME "acp_audio_dma"
static const struct snd_pcm_hardware acp_pcm_hardware_playback = { static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED | .info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
...@@ -73,10 +80,42 @@ static const struct snd_pcm_hardware acp_pcm_hardware_capture = { ...@@ -73,10 +80,42 @@ static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
.periods_max = CAPTURE_MAX_NUM_PERIODS, .periods_max = CAPTURE_MAX_NUM_PERIODS,
}; };
struct audio_drv_data { static const struct snd_pcm_hardware acp_st_pcm_hardware_playback = {
struct snd_pcm_substream *play_stream; .info = SNDRV_PCM_INFO_INTERLEAVED |
struct snd_pcm_substream *capture_stream; SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
void __iomem *acp_mmio; SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.rate_min = 8000,
.rate_max = 96000,
.buffer_bytes_max = ST_MAX_BUFFER,
.period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
.period_bytes_max = ST_PLAYBACK_MAX_PERIOD_SIZE,
.periods_min = PLAYBACK_MIN_NUM_PERIODS,
.periods_max = PLAYBACK_MAX_NUM_PERIODS,
};
static const struct snd_pcm_hardware acp_st_pcm_hardware_capture = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.buffer_bytes_max = ST_MAX_BUFFER,
.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
.period_bytes_max = ST_CAPTURE_MAX_PERIOD_SIZE,
.periods_min = CAPTURE_MIN_NUM_PERIODS,
.periods_max = CAPTURE_MAX_NUM_PERIODS,
}; };
static u32 acp_reg_read(void __iomem *acp_mmio, u32 reg) static u32 acp_reg_read(void __iomem *acp_mmio, u32 reg)
...@@ -143,8 +182,8 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio, ...@@ -143,8 +182,8 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
* system memory <-> ACP SRAM * system memory <-> ACP SRAM
*/ */
static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
u32 size, int direction, u32 size, int direction,
u32 pte_offset) u32 pte_offset, u32 asic_type)
{ {
u16 i; u16 i;
u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12; u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
...@@ -154,24 +193,46 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, ...@@ -154,24 +193,46 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
dmadscr[i].xfer_val = 0; dmadscr[i].xfer_val = 0;
if (direction == SNDRV_PCM_STREAM_PLAYBACK) { if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12 + i; dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12 + i;
dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS + dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS
(size / 2) - (i * (size/2)); + (i * (size/2));
dmadscr[i].src = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS dmadscr[i].src = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS
+ (pte_offset * SZ_4K) + (i * (size/2)); + (pte_offset * SZ_4K) + (i * (size/2));
dmadscr[i].xfer_val |= switch (asic_type) {
(ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) | case CHIP_STONEY:
(size / 2); dmadscr[i].xfer_val |=
(ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM << 16) |
(size / 2);
break;
default:
dmadscr[i].xfer_val |=
(ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) |
(size / 2);
}
} else { } else {
dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH14 + i; dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH14 + i;
dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS + switch (asic_type) {
(i * (size/2)); case CHIP_STONEY:
dmadscr[i].dest = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS dmadscr[i].src = ACP_SHARED_RAM_BANK_3_ADDRESS +
+ (pte_offset * SZ_4K) + (i * (size/2));
(i * (size/2)); dmadscr[i].dest =
dmadscr[i].xfer_val |= ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS +
BIT(22) | (pte_offset * SZ_4K) + (i * (size/2));
(ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) | dmadscr[i].xfer_val |=
(size / 2); BIT(22) |
(ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC << 16) |
(size / 2);
break;
default:
dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS +
(i * (size/2));
dmadscr[i].dest =
ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS +
(pte_offset * SZ_4K) + (i * (size/2));
dmadscr[i].xfer_val |=
BIT(22) |
(ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) |
(size / 2);
}
} }
config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx, config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
&dmadscr[i]); &dmadscr[i]);
...@@ -192,7 +253,8 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, ...@@ -192,7 +253,8 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
* ACP SRAM <-> I2S * ACP SRAM <-> I2S
*/ */
static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio,
u32 size, int direction) u32 size, int direction,
u32 asic_type)
{ {
u16 i; u16 i;
...@@ -213,8 +275,17 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, ...@@ -213,8 +275,17 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio,
dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH15 + i; dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH15 + i;
/* dmadscr[i].src is unused by hardware. */ /* dmadscr[i].src is unused by hardware. */
dmadscr[i].src = 0; dmadscr[i].src = 0;
dmadscr[i].dest = ACP_SHARED_RAM_BANK_5_ADDRESS + switch (asic_type) {
case CHIP_STONEY:
dmadscr[i].dest =
ACP_SHARED_RAM_BANK_3_ADDRESS +
(i * (size / 2)); (i * (size / 2));
break;
default:
dmadscr[i].dest =
ACP_SHARED_RAM_BANK_5_ADDRESS +
(i * (size / 2));
}
dmadscr[i].xfer_val |= BIT(22) | dmadscr[i].xfer_val |= BIT(22) |
(FROM_ACP_I2S_1 << 16) | (size / 2); (FROM_ACP_I2S_1 << 16) | (size / 2);
} }
...@@ -270,7 +341,8 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, ...@@ -270,7 +341,8 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg,
} }
static void config_acp_dma(void __iomem *acp_mmio, static void config_acp_dma(void __iomem *acp_mmio,
struct audio_substream_data *audio_config) struct audio_substream_data *audio_config,
u32 asic_type)
{ {
u32 pte_offset; u32 pte_offset;
...@@ -284,11 +356,11 @@ static void config_acp_dma(void __iomem *acp_mmio, ...@@ -284,11 +356,11 @@ static void config_acp_dma(void __iomem *acp_mmio,
/* Configure System memory <-> ACP SRAM DMA descriptors */ /* Configure System memory <-> ACP SRAM DMA descriptors */
set_acp_sysmem_dma_descriptors(acp_mmio, audio_config->size, set_acp_sysmem_dma_descriptors(acp_mmio, audio_config->size,
audio_config->direction, pte_offset); audio_config->direction, pte_offset, asic_type);
/* Configure ACP SRAM <-> I2S DMA descriptors */ /* Configure ACP SRAM <-> I2S DMA descriptors */
set_acp_to_i2s_dma_descriptors(acp_mmio, audio_config->size, set_acp_to_i2s_dma_descriptors(acp_mmio, audio_config->size,
audio_config->direction); audio_config->direction, asic_type);
} }
/* Start a given DMA channel transfer */ /* Start a given DMA channel transfer */
...@@ -425,7 +497,7 @@ static void acp_set_sram_bank_state(void __iomem *acp_mmio, u16 bank, ...@@ -425,7 +497,7 @@ static void acp_set_sram_bank_state(void __iomem *acp_mmio, u16 bank,
} }
/* Initialize and bring ACP hardware to default state. */ /* Initialize and bring ACP hardware to default state. */
static int acp_init(void __iomem *acp_mmio) static int acp_init(void __iomem *acp_mmio, u32 asic_type)
{ {
u16 bank; u16 bank;
u32 val, count, sram_pte_offset; u32 val, count, sram_pte_offset;
...@@ -499,10 +571,21 @@ static int acp_init(void __iomem *acp_mmio) ...@@ -499,10 +571,21 @@ static int acp_init(void __iomem *acp_mmio)
/* When ACP_TILE_P1 is turned on, all SRAM banks get turned on. /* When ACP_TILE_P1 is turned on, all SRAM banks get turned on.
* Now, turn off all of them. This can't be done in 'poweron' of * Now, turn off all of them. This can't be done in 'poweron' of
* ACP pm domain, as this requires ACP to be initialized. * ACP pm domain, as this requires ACP to be initialized.
* For Stoney, Memory gating is disabled,i.e SRAM Banks
* won't be turned off. The default state for SRAM banks is ON.
* Setting SRAM bank state code skipped for STONEY platform.
*/ */
for (bank = 1; bank < 48; bank++) if (asic_type != CHIP_STONEY) {
acp_set_sram_bank_state(acp_mmio, bank, false); for (bank = 1; bank < 48; bank++)
acp_set_sram_bank_state(acp_mmio, bank, false);
}
/* Stoney supports 16bit resolution */
if (asic_type == CHIP_STONEY) {
val = acp_reg_read(acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN);
val |= 0x03;
acp_reg_write(val, acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN);
}
return 0; return 0;
} }
...@@ -572,9 +655,9 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) ...@@ -572,9 +655,9 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
valid_irq = true; valid_irq = true;
if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_13) == if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_13) ==
PLAYBACK_START_DMA_DESCR_CH13) PLAYBACK_START_DMA_DESCR_CH13)
dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
else
dscr_idx = PLAYBACK_END_DMA_DESCR_CH12; dscr_idx = PLAYBACK_END_DMA_DESCR_CH12;
else
dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM, dscr_idx, config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM, dscr_idx,
1, 0); 1, 0);
acp_dma_start(acp_mmio, SYSRAM_TO_ACP_CH_NUM, false); acp_dma_start(acp_mmio, SYSRAM_TO_ACP_CH_NUM, false);
...@@ -626,10 +709,23 @@ static int acp_dma_open(struct snd_pcm_substream *substream) ...@@ -626,10 +709,23 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
if (adata == NULL) if (adata == NULL)
return -ENOMEM; return -ENOMEM;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
runtime->hw = acp_pcm_hardware_playback; switch (intr_data->asic_type) {
else case CHIP_STONEY:
runtime->hw = acp_pcm_hardware_capture; runtime->hw = acp_st_pcm_hardware_playback;
break;
default:
runtime->hw = acp_pcm_hardware_playback;
}
} else {
switch (intr_data->asic_type) {
case CHIP_STONEY:
runtime->hw = acp_st_pcm_hardware_capture;
break;
default:
runtime->hw = acp_pcm_hardware_capture;
}
}
ret = snd_pcm_hw_constraint_integer(runtime, ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS); SNDRV_PCM_HW_PARAM_PERIODS);
...@@ -652,14 +748,22 @@ static int acp_dma_open(struct snd_pcm_substream *substream) ...@@ -652,14 +748,22 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
intr_data->play_stream = substream; intr_data->play_stream = substream;
for (bank = 1; bank <= 4; bank++) /* For Stoney, Memory gating is disabled,i.e SRAM Banks
acp_set_sram_bank_state(intr_data->acp_mmio, bank, * won't be turned off. The default state for SRAM banks is ON.
true); * Setting SRAM bank state code skipped for STONEY platform.
*/
if (intr_data->asic_type != CHIP_STONEY) {
for (bank = 1; bank <= 4; bank++)
acp_set_sram_bank_state(intr_data->acp_mmio,
bank, true);
}
} else { } else {
intr_data->capture_stream = substream; intr_data->capture_stream = substream;
for (bank = 5; bank <= 8; bank++) if (intr_data->asic_type != CHIP_STONEY) {
acp_set_sram_bank_state(intr_data->acp_mmio, bank, for (bank = 5; bank <= 8; bank++)
true); acp_set_sram_bank_state(intr_data->acp_mmio,
bank, true);
}
} }
return 0; return 0;
...@@ -673,6 +777,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, ...@@ -673,6 +777,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
struct page *pg; struct page *pg;
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
struct audio_substream_data *rtd; struct audio_substream_data *rtd;
struct snd_soc_pcm_runtime *prtd = substream->private_data;
struct audio_drv_data *adata = dev_get_drvdata(prtd->platform->dev);
runtime = substream->runtime; runtime = substream->runtime;
rtd = runtime->private_data; rtd = runtime->private_data;
...@@ -700,7 +806,7 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, ...@@ -700,7 +806,7 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
rtd->direction = substream->stream; rtd->direction = substream->stream;
config_acp_dma(rtd->acp_mmio, rtd); config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type);
status = 0; status = 0;
} else { } else {
status = -ENOMEM; status = -ENOMEM;
...@@ -713,40 +819,48 @@ static int acp_dma_hw_free(struct snd_pcm_substream *substream) ...@@ -713,40 +819,48 @@ static int acp_dma_hw_free(struct snd_pcm_substream *substream)
return snd_pcm_lib_free_pages(substream); return snd_pcm_lib_free_pages(substream);
} }
static u64 acp_get_byte_count(void __iomem *acp_mmio, int stream)
{
union acp_dma_count playback_dma_count;
union acp_dma_count capture_dma_count;
u64 bytescount = 0;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
playback_dma_count.bcount.high = acp_reg_read(acp_mmio,
mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH);
playback_dma_count.bcount.low = acp_reg_read(acp_mmio,
mmACP_I2S_TRANSMIT_BYTE_CNT_LOW);
bytescount = playback_dma_count.bytescount;
} else {
capture_dma_count.bcount.high = acp_reg_read(acp_mmio,
mmACP_I2S_RECEIVED_BYTE_CNT_HIGH);
capture_dma_count.bcount.low = acp_reg_read(acp_mmio,
mmACP_I2S_RECEIVED_BYTE_CNT_LOW);
bytescount = capture_dma_count.bytescount;
}
return bytescount;
}
static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream)
{ {
u16 dscr; u32 buffersize;
u32 mul, dma_config, period_bytes;
u32 pos = 0; u32 pos = 0;
u64 bytescount = 0;
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data; struct audio_substream_data *rtd = runtime->private_data;
period_bytes = frames_to_bytes(runtime, runtime->period_size); buffersize = frames_to_bytes(runtime, runtime->buffer_size);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { bytescount = acp_get_byte_count(rtd->acp_mmio, substream->stream);
dscr = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CUR_DSCR_13);
if (dscr == PLAYBACK_START_DMA_DESCR_CH13) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
mul = 0; if (bytescount > rtd->renderbytescount)
else bytescount = bytescount - rtd->renderbytescount;
mul = 1;
pos = (mul * period_bytes);
} else { } else {
dma_config = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CNTL_14); if (bytescount > rtd->capturebytescount)
if (dma_config != 0) { bytescount = bytescount - rtd->capturebytescount;
dscr = acp_reg_read(rtd->acp_mmio,
mmACP_DMA_CUR_DSCR_14);
if (dscr == CAPTURE_START_DMA_DESCR_CH14)
mul = 1;
else
mul = 2;
pos = (mul * period_bytes);
}
if (pos >= (2 * period_bytes))
pos = 0;
} }
pos = do_div(bytescount, buffersize);
return bytes_to_frames(runtime, pos); return bytes_to_frames(runtime, pos);
} }
...@@ -768,23 +882,6 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) ...@@ -768,23 +882,6 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream)
config_acp_dma_channel(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM, config_acp_dma_channel(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM,
PLAYBACK_START_DMA_DESCR_CH13, PLAYBACK_START_DMA_DESCR_CH13,
NUM_DSCRS_PER_CHANNEL, 0); NUM_DSCRS_PER_CHANNEL, 0);
/* Fill ACP SRAM (2 periods) with zeros from System RAM
* which is zero-ed in hw_params
*/
acp_dma_start(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, false);
/* ACP SRAM (2 periods of buffer size) is intially filled with
* zeros. Before rendering starts, 2nd half of SRAM will be
* filled with valid audio data DMA'ed from first half of system
* RAM and 1st half of SRAM will be filled with Zeros. This is
* the initial scenario when redering starts from SRAM. Later
* on, 2nd half of system memory will be DMA'ed to 1st half of
* SRAM, 1st half of system memory will be DMA'ed to 2nd half of
* SRAM in ping-pong way till rendering stops.
*/
config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM,
PLAYBACK_START_DMA_DESCR_CH12,
1, 0);
} else { } else {
config_acp_dma_channel(rtd->acp_mmio, ACP_TO_SYSRAM_CH_NUM, config_acp_dma_channel(rtd->acp_mmio, ACP_TO_SYSRAM_CH_NUM,
CAPTURE_START_DMA_DESCR_CH14, CAPTURE_START_DMA_DESCR_CH14,
...@@ -799,7 +896,8 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) ...@@ -799,7 +896,8 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream)
static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
{ {
int ret; int ret;
u32 loops = 1000; u32 loops = 4000;
u64 bytescount = 0;
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *prtd = substream->private_data; struct snd_soc_pcm_runtime *prtd = substream->private_data;
...@@ -811,7 +909,11 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -811,7 +909,11 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
bytescount = acp_get_byte_count(rtd->acp_mmio,
substream->stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (rtd->renderbytescount == 0)
rtd->renderbytescount = bytescount;
acp_dma_start(rtd->acp_mmio, acp_dma_start(rtd->acp_mmio,
SYSRAM_TO_ACP_CH_NUM, false); SYSRAM_TO_ACP_CH_NUM, false);
while (acp_reg_read(rtd->acp_mmio, mmACP_DMA_CH_STS) & while (acp_reg_read(rtd->acp_mmio, mmACP_DMA_CH_STS) &
...@@ -828,6 +930,8 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -828,6 +930,8 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
ACP_TO_I2S_DMA_CH_NUM, true); ACP_TO_I2S_DMA_CH_NUM, true);
} else { } else {
if (rtd->capturebytescount == 0)
rtd->capturebytescount = bytescount;
acp_dma_start(rtd->acp_mmio, acp_dma_start(rtd->acp_mmio,
I2S_TO_ACP_DMA_CH_NUM, true); I2S_TO_ACP_DMA_CH_NUM, true);
} }
...@@ -841,12 +945,15 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -841,12 +945,15 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
* channels will stopped automatically after its transfer * channels will stopped automatically after its transfer
* completes : SYSRAM_TO_ACP_CH_NUM / ACP_TO_SYSRAM_CH_NUM * completes : SYSRAM_TO_ACP_CH_NUM / ACP_TO_SYSRAM_CH_NUM
*/ */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = acp_dma_stop(rtd->acp_mmio, ret = acp_dma_stop(rtd->acp_mmio,
ACP_TO_I2S_DMA_CH_NUM); ACP_TO_I2S_DMA_CH_NUM);
else rtd->renderbytescount = 0;
} else {
ret = acp_dma_stop(rtd->acp_mmio, ret = acp_dma_stop(rtd->acp_mmio,
I2S_TO_ACP_DMA_CH_NUM); I2S_TO_ACP_DMA_CH_NUM);
rtd->capturebytescount = 0;
}
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
...@@ -857,10 +964,27 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -857,10 +964,27 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
static int acp_dma_new(struct snd_soc_pcm_runtime *rtd) static int acp_dma_new(struct snd_soc_pcm_runtime *rtd)
{ {
return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, int ret;
struct audio_drv_data *adata = dev_get_drvdata(rtd->platform->dev);
switch (adata->asic_type) {
case CHIP_STONEY:
ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
SNDRV_DMA_TYPE_DEV,
NULL, ST_MIN_BUFFER,
ST_MAX_BUFFER);
break;
default:
ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
SNDRV_DMA_TYPE_DEV, SNDRV_DMA_TYPE_DEV,
NULL, MIN_BUFFER, NULL, MIN_BUFFER,
MAX_BUFFER); MAX_BUFFER);
break;
}
if (ret < 0)
dev_err(rtd->platform->dev,
"buffer preallocation failer error:%d\n", ret);
return ret;
} }
static int acp_dma_close(struct snd_pcm_substream *substream) static int acp_dma_close(struct snd_pcm_substream *substream)
...@@ -875,14 +999,23 @@ static int acp_dma_close(struct snd_pcm_substream *substream) ...@@ -875,14 +999,23 @@ static int acp_dma_close(struct snd_pcm_substream *substream)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
adata->play_stream = NULL; adata->play_stream = NULL;
for (bank = 1; bank <= 4; bank++) /* For Stoney, Memory gating is disabled,i.e SRAM Banks
acp_set_sram_bank_state(adata->acp_mmio, bank, * won't be turned off. The default state for SRAM banks is ON.
false); * Setting SRAM bank state code skipped for STONEY platform.
} else { * added condition checks for Carrizo platform only
*/
if (adata->asic_type != CHIP_STONEY) {
for (bank = 1; bank <= 4; bank++)
acp_set_sram_bank_state(adata->acp_mmio, bank,
false);
}
} else {
adata->capture_stream = NULL; adata->capture_stream = NULL;
for (bank = 5; bank <= 8; bank++) if (adata->asic_type != CHIP_STONEY) {
acp_set_sram_bank_state(adata->acp_mmio, bank, for (bank = 5; bank <= 8; bank++)
false); acp_set_sram_bank_state(adata->acp_mmio, bank,
false);
}
} }
/* Disable ACP irq, when the current stream is being closed and /* Disable ACP irq, when the current stream is being closed and
...@@ -916,6 +1049,7 @@ static int acp_audio_probe(struct platform_device *pdev) ...@@ -916,6 +1049,7 @@ static int acp_audio_probe(struct platform_device *pdev)
int status; int status;
struct audio_drv_data *audio_drv_data; struct audio_drv_data *audio_drv_data;
struct resource *res; struct resource *res;
const u32 *pdata = pdev->dev.platform_data;
audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data), audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data),
GFP_KERNEL); GFP_KERNEL);
...@@ -932,6 +1066,7 @@ static int acp_audio_probe(struct platform_device *pdev) ...@@ -932,6 +1066,7 @@ static int acp_audio_probe(struct platform_device *pdev)
audio_drv_data->play_stream = NULL; audio_drv_data->play_stream = NULL;
audio_drv_data->capture_stream = NULL; audio_drv_data->capture_stream = NULL;
audio_drv_data->asic_type = *pdata;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) { if (!res) {
...@@ -949,7 +1084,7 @@ static int acp_audio_probe(struct platform_device *pdev) ...@@ -949,7 +1084,7 @@ static int acp_audio_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, audio_drv_data); dev_set_drvdata(&pdev->dev, audio_drv_data);
/* Initialize the ACP */ /* Initialize the ACP */
acp_init(audio_drv_data->acp_mmio); acp_init(audio_drv_data->acp_mmio, audio_drv_data->asic_type);
status = snd_soc_register_platform(&pdev->dev, &acp_asoc_platform); status = snd_soc_register_platform(&pdev->dev, &acp_asoc_platform);
if (status != 0) { if (status != 0) {
...@@ -980,21 +1115,31 @@ static int acp_pcm_resume(struct device *dev) ...@@ -980,21 +1115,31 @@ static int acp_pcm_resume(struct device *dev)
u16 bank; u16 bank;
struct audio_drv_data *adata = dev_get_drvdata(dev); struct audio_drv_data *adata = dev_get_drvdata(dev);
acp_init(adata->acp_mmio); acp_init(adata->acp_mmio, adata->asic_type);
if (adata->play_stream && adata->play_stream->runtime) { if (adata->play_stream && adata->play_stream->runtime) {
for (bank = 1; bank <= 4; bank++) /* For Stoney, Memory gating is disabled,i.e SRAM Banks
acp_set_sram_bank_state(adata->acp_mmio, bank, * won't be turned off. The default state for SRAM banks is ON.
* Setting SRAM bank state code skipped for STONEY platform.
*/
if (adata->asic_type != CHIP_STONEY) {
for (bank = 1; bank <= 4; bank++)
acp_set_sram_bank_state(adata->acp_mmio, bank,
true); true);
}
config_acp_dma(adata->acp_mmio, config_acp_dma(adata->acp_mmio,
adata->play_stream->runtime->private_data); adata->play_stream->runtime->private_data,
adata->asic_type);
} }
if (adata->capture_stream && adata->capture_stream->runtime) { if (adata->capture_stream && adata->capture_stream->runtime) {
for (bank = 5; bank <= 8; bank++) if (adata->asic_type != CHIP_STONEY) {
acp_set_sram_bank_state(adata->acp_mmio, bank, for (bank = 5; bank <= 8; bank++)
acp_set_sram_bank_state(adata->acp_mmio, bank,
true); true);
}
config_acp_dma(adata->acp_mmio, config_acp_dma(adata->acp_mmio,
adata->capture_stream->runtime->private_data); adata->capture_stream->runtime->private_data,
adata->asic_type);
} }
acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
return 0; return 0;
...@@ -1013,7 +1158,7 @@ static int acp_pcm_runtime_resume(struct device *dev) ...@@ -1013,7 +1158,7 @@ static int acp_pcm_runtime_resume(struct device *dev)
{ {
struct audio_drv_data *adata = dev_get_drvdata(dev); struct audio_drv_data *adata = dev_get_drvdata(dev);
acp_init(adata->acp_mmio); acp_init(adata->acp_mmio, adata->asic_type);
acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
return 0; return 0;
} }
...@@ -1028,14 +1173,15 @@ static struct platform_driver acp_dma_driver = { ...@@ -1028,14 +1173,15 @@ static struct platform_driver acp_dma_driver = {
.probe = acp_audio_probe, .probe = acp_audio_probe,
.remove = acp_audio_remove, .remove = acp_audio_remove,
.driver = { .driver = {
.name = "acp_audio_dma", .name = DRV_NAME,
.pm = &acp_pm_ops, .pm = &acp_pm_ops,
}, },
}; };
module_platform_driver(acp_dma_driver); module_platform_driver(acp_dma_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
MODULE_DESCRIPTION("AMD ACP PCM Driver"); MODULE_DESCRIPTION("AMD ACP PCM Driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:acp-dma-audio"); MODULE_ALIAS("platform:"DRV_NAME);
/*
* Machine driver for AMD ACP Audio engine using Realtek RT5645 codec
*
* Copyright 2017 Advanced Micro Devices, Inc.
*
* This file is modified from rt288 machine driver
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*
*/
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/acpi.h>
#include "../codecs/rt5645.h"
#define CZ_PLAT_CLK 24000000
static struct snd_soc_jack cz_jack;
static int cz_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
CZ_PLAT_CLK, params_rate(params) * 512);
if (ret < 0) {
dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1,
params_rate(params) * 512, SND_SOC_CLOCK_OUT);
if (ret < 0) {
dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
return ret;
}
return ret;
}
static int cz_init(struct snd_soc_pcm_runtime *rtd)
{
int ret;
struct snd_soc_card *card;
struct snd_soc_codec *codec;
codec = rtd->codec;
card = rtd->card;
ret = snd_soc_card_jack_new(card, "Headset Jack",
SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
&cz_jack, NULL, 0);
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
}
rt5645_set_jack_detect(codec, &cz_jack, &cz_jack, &cz_jack);
return 0;
}
static struct snd_soc_ops cz_aif1_ops = {
.hw_params = cz_aif1_hw_params,
};
static struct snd_soc_dai_link cz_dai_rt5650[] = {
{
.name = "amd-rt5645-play",
.stream_name = "RT5645_AIF1",
.platform_name = "acp_audio_dma.0.auto",
.cpu_dai_name = "designware-i2s.1.auto",
.codec_dai_name = "rt5645-aif1",
.codec_name = "i2c-10EC5650:00",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBM_CFM,
.init = cz_init,
.ops = &cz_aif1_ops,
},
{
.name = "amd-rt5645-cap",
.stream_name = "RT5645_AIF1",
.platform_name = "acp_audio_dma.0.auto",
.cpu_dai_name = "designware-i2s.2.auto",
.codec_dai_name = "rt5645-aif1",
.codec_name = "i2c-10EC5650:00",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBM_CFM,
.ops = &cz_aif1_ops,
},
};
static const struct snd_soc_dapm_widget cz_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_SPK("Speakers", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
};
static const struct snd_soc_dapm_route cz_audio_route[] = {
{"Headphones", NULL, "HPOL"},
{"Headphones", NULL, "HPOR"},
{"RECMIXL", NULL, "Headset Mic"},
{"RECMIXR", NULL, "Headset Mic"},
{"Speakers", NULL, "SPOL"},
{"Speakers", NULL, "SPOR"},
{"DMIC L2", NULL, "Int Mic"},
{"DMIC R2", NULL, "Int Mic"},
};
static const struct snd_kcontrol_new cz_mc_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphones"),
SOC_DAPM_PIN_SWITCH("Speakers"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Int Mic"),
};
static struct snd_soc_card cz_card = {
.name = "acprt5650",
.owner = THIS_MODULE,
.dai_link = cz_dai_rt5650,
.num_links = ARRAY_SIZE(cz_dai_rt5650),
.dapm_widgets = cz_widgets,
.num_dapm_widgets = ARRAY_SIZE(cz_widgets),
.dapm_routes = cz_audio_route,
.num_dapm_routes = ARRAY_SIZE(cz_audio_route),
.controls = cz_mc_controls,
.num_controls = ARRAY_SIZE(cz_mc_controls),
};
static int cz_probe(struct platform_device *pdev)
{
int ret;
struct snd_soc_card *card;
card = &cz_card;
cz_card.dev = &pdev->dev;
platform_set_drvdata(pdev, card);
ret = devm_snd_soc_register_card(&pdev->dev, &cz_card);
if (ret) {
dev_err(&pdev->dev,
"devm_snd_soc_register_card(%s) failed: %d\n",
cz_card.name, ret);
return ret;
}
return 0;
}
static const struct acpi_device_id cz_audio_acpi_match[] = {
{ "AMDI1002", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match);
static struct platform_driver cz_pcm_driver = {
.driver = {
.name = "cz-rt5645",
.acpi_match_table = ACPI_PTR(cz_audio_acpi_match),
.pm = &snd_soc_pm_ops,
},
.probe = cz_probe,
};
module_platform_driver(cz_pcm_driver);
MODULE_AUTHOR("akshu.agrawal@amd.com");
MODULE_DESCRIPTION("cz-rt5645 audio support");
MODULE_LICENSE("GPL v2");
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
/* Capture SRAM address (as a source in dma descriptor) */ /* Capture SRAM address (as a source in dma descriptor) */
#define ACP_SHARED_RAM_BANK_5_ADDRESS 0x400A000 #define ACP_SHARED_RAM_BANK_5_ADDRESS 0x400A000
#define ACP_SHARED_RAM_BANK_3_ADDRESS 0x4006000
#define ACP_DMA_RESET_TIME 10000 #define ACP_DMA_RESET_TIME 10000
#define ACP_CLOCK_EN_TIME_OUT_VALUE 0x000000FF #define ACP_CLOCK_EN_TIME_OUT_VALUE 0x000000FF
...@@ -68,6 +69,7 @@ ...@@ -68,6 +69,7 @@
#define CAPTURE_START_DMA_DESCR_CH15 6 #define CAPTURE_START_DMA_DESCR_CH15 6
#define CAPTURE_END_DMA_DESCR_CH15 7 #define CAPTURE_END_DMA_DESCR_CH15 7
#define mmACP_I2S_16BIT_RESOLUTION_EN 0x5209
enum acp_dma_priority_level { enum acp_dma_priority_level {
/* 0x0 Specifies the DMA channel is given normal priority */ /* 0x0 Specifies the DMA channel is given normal priority */
ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0, ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0,
...@@ -82,9 +84,26 @@ struct audio_substream_data { ...@@ -82,9 +84,26 @@ struct audio_substream_data {
u16 num_of_pages; u16 num_of_pages;
u16 direction; u16 direction;
uint64_t size; uint64_t size;
u64 renderbytescount;
u64 capturebytescount;
void __iomem *acp_mmio; void __iomem *acp_mmio;
}; };
struct audio_drv_data {
struct snd_pcm_substream *play_stream;
struct snd_pcm_substream *capture_stream;
void __iomem *acp_mmio;
u32 asic_type;
};
union acp_dma_count {
struct {
u32 low;
u32 high;
} bcount;
u64 bytescount;
};
enum { enum {
ACP_TILE_P1 = 0, ACP_TILE_P1 = 0,
ACP_TILE_P2, ACP_TILE_P2,
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gcd.h> #include <linux/gcd.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
...@@ -293,16 +294,99 @@ int arizona_init_gpio(struct snd_soc_codec *codec) ...@@ -293,16 +294,99 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
} }
EXPORT_SYMBOL_GPL(arizona_init_gpio); EXPORT_SYMBOL_GPL(arizona_init_gpio);
int arizona_init_notifiers(struct snd_soc_codec *codec) int arizona_init_common(struct arizona *arizona)
{ {
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona_pdata *pdata = &arizona->pdata;
struct arizona *arizona = priv->arizona; unsigned int val, mask;
int i;
BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier); BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier);
for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) {
/* Default is 0 so noop with defaults */
if (pdata->out_mono[i])
val = ARIZONA_OUT1_MONO;
else
val = 0;
regmap_update_bits(arizona->regmap,
ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
ARIZONA_OUT1_MONO, val);
}
for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
if (pdata->spk_mute[i])
regmap_update_bits(arizona->regmap,
ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
ARIZONA_SPK1_MUTE_ENDIAN_MASK |
ARIZONA_SPK1_MUTE_SEQ1_MASK,
pdata->spk_mute[i]);
if (pdata->spk_fmt[i])
regmap_update_bits(arizona->regmap,
ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
ARIZONA_SPK1_FMT_MASK,
pdata->spk_fmt[i]);
}
for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
/* Default for both is 0 so noop with defaults */
val = pdata->dmic_ref[i] << ARIZONA_IN1_DMIC_SUP_SHIFT;
if (pdata->inmode[i] & ARIZONA_INMODE_DMIC)
val |= 1 << ARIZONA_IN1_MODE_SHIFT;
switch (arizona->type) {
case WM8998:
case WM1814:
regmap_update_bits(arizona->regmap,
ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 8),
ARIZONA_IN1L_SRC_SE_MASK,
(pdata->inmode[i] & ARIZONA_INMODE_SE)
<< ARIZONA_IN1L_SRC_SE_SHIFT);
regmap_update_bits(arizona->regmap,
ARIZONA_ADC_DIGITAL_VOLUME_1R + (i * 8),
ARIZONA_IN1R_SRC_SE_MASK,
(pdata->inmode[i] & ARIZONA_INMODE_SE)
<< ARIZONA_IN1R_SRC_SE_SHIFT);
mask = ARIZONA_IN1_DMIC_SUP_MASK |
ARIZONA_IN1_MODE_MASK;
break;
default:
if (pdata->inmode[i] & ARIZONA_INMODE_SE)
val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT;
mask = ARIZONA_IN1_DMIC_SUP_MASK |
ARIZONA_IN1_MODE_MASK |
ARIZONA_IN1_SINGLE_ENDED_MASK;
break;
}
regmap_update_bits(arizona->regmap,
ARIZONA_IN1L_CONTROL + (i * 8),
mask, val);
}
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(arizona_init_notifiers); EXPORT_SYMBOL_GPL(arizona_init_common);
int arizona_init_vol_limit(struct arizona *arizona)
{
int i;
for (i = 0; i < ARRAY_SIZE(arizona->pdata.out_vol_limit); ++i) {
if (arizona->pdata.out_vol_limit[i])
regmap_update_bits(arizona->regmap,
ARIZONA_DAC_VOLUME_LIMIT_1L + i * 4,
ARIZONA_OUT1L_VOL_LIM_MASK,
arizona->pdata.out_vol_limit[i]);
}
return 0;
}
EXPORT_SYMBOL_GPL(arizona_init_vol_limit);
const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None", "None",
...@@ -2695,6 +2779,80 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol, ...@@ -2695,6 +2779,80 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
} }
EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put); EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
int arizona_of_get_audio_pdata(struct arizona *arizona)
{
struct arizona_pdata *pdata = &arizona->pdata;
struct device_node *np = arizona->dev->of_node;
struct property *prop;
const __be32 *cur;
u32 val;
u32 pdm_val[ARIZONA_MAX_PDM_SPK];
int ret;
int count = 0;
count = 0;
of_property_for_each_u32(np, "wlf,inmode", prop, cur, val) {
if (count == ARRAY_SIZE(pdata->inmode))
break;
pdata->inmode[count] = val;
count++;
}
count = 0;
of_property_for_each_u32(np, "wlf,dmic-ref", prop, cur, val) {
if (count == ARRAY_SIZE(pdata->dmic_ref))
break;
pdata->dmic_ref[count] = val;
count++;
}
count = 0;
of_property_for_each_u32(np, "wlf,out-mono", prop, cur, val) {
if (count == ARRAY_SIZE(pdata->out_mono))
break;
pdata->out_mono[count] = !!val;
count++;
}
count = 0;
of_property_for_each_u32(np, "wlf,max-channels-clocked", prop, cur, val) {
if (count == ARRAY_SIZE(pdata->max_channels_clocked))
break;
pdata->max_channels_clocked[count] = val;
count++;
}
count = 0;
of_property_for_each_u32(np, "wlf,out-volume-limit", prop, cur, val) {
if (count == ARRAY_SIZE(pdata->out_vol_limit))
break;
pdata->out_vol_limit[count] = val;
count++;
}
ret = of_property_read_u32_array(np, "wlf,spk-fmt",
pdm_val, ARRAY_SIZE(pdm_val));
if (ret >= 0)
for (count = 0; count < ARRAY_SIZE(pdata->spk_fmt); ++count)
pdata->spk_fmt[count] = pdm_val[count];
ret = of_property_read_u32_array(np, "wlf,spk-mute",
pdm_val, ARRAY_SIZE(pdm_val));
if (ret >= 0)
for (count = 0; count < ARRAY_SIZE(pdata->spk_mute); ++count)
pdata->spk_mute[count] = pdm_val[count];
return 0;
}
EXPORT_SYMBOL_GPL(arizona_of_get_audio_pdata);
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -313,7 +313,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source, ...@@ -313,7 +313,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
int arizona_init_spk(struct snd_soc_codec *codec); int arizona_init_spk(struct snd_soc_codec *codec);
int arizona_init_gpio(struct snd_soc_codec *codec); int arizona_init_gpio(struct snd_soc_codec *codec);
int arizona_init_mono(struct snd_soc_codec *codec); int arizona_init_mono(struct snd_soc_codec *codec);
int arizona_init_notifiers(struct snd_soc_codec *codec);
int arizona_init_common(struct arizona *arizona);
int arizona_init_vol_limit(struct arizona *arizona);
int arizona_init_spk_irqs(struct arizona *arizona); int arizona_init_spk_irqs(struct arizona *arizona);
int arizona_free_spk_irqs(struct arizona *arizona); int arizona_free_spk_irqs(struct arizona *arizona);
...@@ -350,4 +352,6 @@ static inline int arizona_unregister_notifier(struct snd_soc_codec *codec, ...@@ -350,4 +352,6 @@ static inline int arizona_unregister_notifier(struct snd_soc_codec *codec,
return blocking_notifier_chain_unregister(&arizona->notifier, nb); return blocking_notifier_chain_unregister(&arizona->notifier, nb);
} }
int arizona_of_get_audio_pdata(struct arizona *arizona);
#endif #endif
...@@ -1130,7 +1130,6 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec) ...@@ -1130,7 +1130,6 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
arizona_init_gpio(codec); arizona_init_gpio(codec);
arizona_init_mono(codec); arizona_init_mono(codec);
arizona_init_notifiers(codec);
ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec); ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
if (ret) if (ret)
...@@ -1230,6 +1229,14 @@ static int cs47l24_probe(struct platform_device *pdev) ...@@ -1230,6 +1229,14 @@ static int cs47l24_probe(struct platform_device *pdev)
if (!cs47l24) if (!cs47l24)
return -ENOMEM; return -ENOMEM;
if (IS_ENABLED(CONFIG_OF)) {
if (!dev_get_platdata(arizona->dev)) {
ret = arizona_of_get_audio_pdata(arizona);
if (ret < 0)
return ret;
}
}
platform_set_drvdata(pdev, cs47l24); platform_set_drvdata(pdev, cs47l24);
cs47l24->core.arizona = arizona; cs47l24->core.arizona = arizona;
...@@ -1288,6 +1295,11 @@ static int cs47l24_probe(struct platform_device *pdev) ...@@ -1288,6 +1295,11 @@ static int cs47l24_probe(struct platform_device *pdev)
return ret; return ret;
} }
arizona_init_common(arizona);
ret = arizona_init_vol_limit(arizona);
if (ret < 0)
goto err_dsp_irq;
ret = arizona_init_spk_irqs(arizona); ret = arizona_init_spk_irqs(arizona);
if (ret < 0) if (ret < 0)
goto err_dsp_irq; goto err_dsp_irq;
......
...@@ -1951,7 +1951,6 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) ...@@ -1951,7 +1951,6 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
return ret; return ret;
arizona_init_gpio(codec); arizona_init_gpio(codec);
arizona_init_notifiers(codec);
snd_soc_component_disable_pin(component, "HAPTICS"); snd_soc_component_disable_pin(component, "HAPTICS");
...@@ -2043,6 +2042,14 @@ static int wm5102_probe(struct platform_device *pdev) ...@@ -2043,6 +2042,14 @@ static int wm5102_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, wm5102); platform_set_drvdata(pdev, wm5102);
if (IS_ENABLED(CONFIG_OF)) {
if (!dev_get_platdata(arizona->dev)) {
ret = arizona_of_get_audio_pdata(arizona);
if (ret < 0)
return ret;
}
}
mutex_init(&arizona->dac_comp_lock); mutex_init(&arizona->dac_comp_lock);
wm5102->core.arizona = arizona; wm5102->core.arizona = arizona;
...@@ -2098,6 +2105,11 @@ static int wm5102_probe(struct platform_device *pdev) ...@@ -2098,6 +2105,11 @@ static int wm5102_probe(struct platform_device *pdev)
return ret; return ret;
} }
arizona_init_common(arizona);
ret = arizona_init_vol_limit(arizona);
if (ret < 0)
goto err_dsp_irq;
ret = arizona_init_spk_irqs(arizona); ret = arizona_init_spk_irqs(arizona);
if (ret < 0) if (ret < 0)
goto err_dsp_irq; goto err_dsp_irq;
......
...@@ -2290,7 +2290,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) ...@@ -2290,7 +2290,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_gpio(codec); arizona_init_gpio(codec);
arizona_init_mono(codec); arizona_init_mono(codec);
arizona_init_notifiers(codec);
for (i = 0; i < WM5110_NUM_ADSP; ++i) { for (i = 0; i < WM5110_NUM_ADSP; ++i) {
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec); ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
...@@ -2398,6 +2397,14 @@ static int wm5110_probe(struct platform_device *pdev) ...@@ -2398,6 +2397,14 @@ static int wm5110_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, wm5110); platform_set_drvdata(pdev, wm5110);
if (IS_ENABLED(CONFIG_OF)) {
if (!dev_get_platdata(arizona->dev)) {
ret = arizona_of_get_audio_pdata(arizona);
if (ret < 0)
return ret;
}
}
wm5110->core.arizona = arizona; wm5110->core.arizona = arizona;
wm5110->core.num_inputs = 8; wm5110->core.num_inputs = 8;
...@@ -2454,6 +2461,11 @@ static int wm5110_probe(struct platform_device *pdev) ...@@ -2454,6 +2461,11 @@ static int wm5110_probe(struct platform_device *pdev)
return ret; return ret;
} }
arizona_init_common(arizona);
ret = arizona_init_vol_limit(arizona);
if (ret < 0)
goto err_dsp_irq;
ret = arizona_init_spk_irqs(arizona); ret = arizona_init_spk_irqs(arizona);
if (ret < 0) if (ret < 0)
goto err_dsp_irq; goto err_dsp_irq;
......
...@@ -1068,8 +1068,6 @@ static int wm8997_codec_probe(struct snd_soc_codec *codec) ...@@ -1068,8 +1068,6 @@ static int wm8997_codec_probe(struct snd_soc_codec *codec)
if (ret < 0) if (ret < 0)
return ret; return ret;
arizona_init_notifiers(codec);
snd_soc_component_disable_pin(component, "HAPTICS"); snd_soc_component_disable_pin(component, "HAPTICS");
priv->core.arizona->dapm = dapm; priv->core.arizona->dapm = dapm;
...@@ -1136,6 +1134,14 @@ static int wm8997_probe(struct platform_device *pdev) ...@@ -1136,6 +1134,14 @@ static int wm8997_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, wm8997); platform_set_drvdata(pdev, wm8997);
if (IS_ENABLED(CONFIG_OF)) {
if (!dev_get_platdata(arizona->dev)) {
ret = arizona_of_get_audio_pdata(arizona);
if (ret < 0)
return ret;
}
}
wm8997->core.arizona = arizona; wm8997->core.arizona = arizona;
wm8997->core.num_inputs = 4; wm8997->core.num_inputs = 4;
...@@ -1168,6 +1174,11 @@ static int wm8997_probe(struct platform_device *pdev) ...@@ -1168,6 +1174,11 @@ static int wm8997_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev); pm_runtime_idle(&pdev->dev);
arizona_init_common(arizona);
ret = arizona_init_vol_limit(arizona);
if (ret < 0)
return ret;
ret = arizona_init_spk_irqs(arizona); ret = arizona_init_spk_irqs(arizona);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -1284,7 +1284,6 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec) ...@@ -1284,7 +1284,6 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec)
return ret; return ret;
arizona_init_gpio(codec); arizona_init_gpio(codec);
arizona_init_notifiers(codec);
snd_soc_component_disable_pin(component, "HAPTICS"); snd_soc_component_disable_pin(component, "HAPTICS");
...@@ -1353,6 +1352,14 @@ static int wm8998_probe(struct platform_device *pdev) ...@@ -1353,6 +1352,14 @@ static int wm8998_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, wm8998); platform_set_drvdata(pdev, wm8998);
if (IS_ENABLED(CONFIG_OF)) {
if (!dev_get_platdata(arizona->dev)) {
ret = arizona_of_get_audio_pdata(arizona);
if (ret < 0)
return ret;
}
}
wm8998->core.arizona = arizona; wm8998->core.arizona = arizona;
wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */ wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */
...@@ -1377,6 +1384,8 @@ static int wm8998_probe(struct platform_device *pdev) ...@@ -1377,6 +1384,8 @@ static int wm8998_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev); pm_runtime_idle(&pdev->dev);
arizona_init_common(arizona);
ret = arizona_init_spk_irqs(arizona); ret = arizona_init_spk_irqs(arizona);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -29,21 +29,41 @@ ...@@ -29,21 +29,41 @@
static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)
{ {
pxa2xx_ac97_try_warm_reset(ac97); pxa2xx_ac97_try_warm_reset();
pxa2xx_ac97_finish_reset(ac97); pxa2xx_ac97_finish_reset();
} }
static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
{ {
pxa2xx_ac97_try_cold_reset(ac97); pxa2xx_ac97_try_cold_reset();
pxa2xx_ac97_finish_reset(ac97); pxa2xx_ac97_finish_reset();
}
static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
unsigned short reg)
{
int ret;
ret = pxa2xx_ac97_read(ac97->num, reg);
if (ret < 0)
return 0;
else
return (unsigned short)(ret & 0xffff);
}
static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
int ret;
ret = pxa2xx_ac97_write(ac97->num, reg, val);
} }
static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.read = pxa2xx_ac97_read, .read = pxa2xx_ac97_legacy_read,
.write = pxa2xx_ac97_write, .write = pxa2xx_ac97_legacy_write,
.warm_reset = pxa2xx_ac97_warm_reset, .warm_reset = pxa2xx_ac97_warm_reset,
.reset = pxa2xx_ac97_cold_reset, .reset = pxa2xx_ac97_cold_reset,
}; };
......
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