mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 03:10:52 +07:00
Merge remote-tracking branches 'asoc/topic/ac97', 'asoc/topic/ac97-mfd', 'asoc/topic/amd' and 'asoc/topic/arizona-mfd' into asoc-next
This commit is contained in:
commit
242f66c845
@ -65,45 +65,6 @@ Optional properties:
|
||||
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.
|
||||
|
||||
- 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
|
||||
they are being externally supplied. As covered in
|
||||
Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
@ -112,6 +73,7 @@ Optional properties:
|
||||
Also see child specific device properties:
|
||||
Regulator - ../regulator/arizona-regulator.txt
|
||||
Extcon - ../extcon/extcon-arizona.txt
|
||||
Sound - ../sound/arizona.txt
|
||||
|
||||
Example:
|
||||
|
||||
|
53
Documentation/devicetree/bindings/sound/wlf,arizona.txt
Normal file
53
Documentation/devicetree/bindings/sound/wlf,arizona.txt
Normal file
@ -0,0 +1,53 @@
|
||||
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
|
||||
F: Documentation/devicetree/bindings/regulator/arizona-regulator.txt
|
||||
F: Documentation/devicetree/bindings/mfd/arizona.txt
|
||||
F: Documentation/devicetree/bindings/mfd/wm831x.txt
|
||||
F: Documentation/devicetree/bindings/sound/wlf,arizona.txt
|
||||
F: arch/arm/mach-s3c64xx/mach-crag6410*
|
||||
F: drivers/clk/clk-wm83*.c
|
||||
F: drivers/extcon/extcon-arizona.c
|
||||
|
@ -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].num_resources = 4;
|
||||
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].num_resources = 1;
|
||||
|
@ -23,34 +23,9 @@
|
||||
#ifndef __AMD_SHARED_H__
|
||||
#define __AMD_SHARED_H__
|
||||
|
||||
#define AMD_MAX_USEC_TIMEOUT 200000 /* 200 ms */
|
||||
#include <drm/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,
|
||||
};
|
||||
#define AMD_MAX_USEC_TIMEOUT 200000 /* 200 ms */
|
||||
|
||||
/*
|
||||
* Chip flags
|
||||
|
@ -727,7 +727,7 @@ config TOUCHSCREEN_WM831X
|
||||
|
||||
config TOUCHSCREEN_WM97XX
|
||||
tristate "Support for WM97xx AC97 touchscreen controllers"
|
||||
depends on AC97_BUS
|
||||
depends on AC97_BUS || AC97_BUS_NEW
|
||||
help
|
||||
Say Y here if you have a Wolfson Microelectronics WM97xx
|
||||
touchscreen connected to your system. Note that this option
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <linux/pm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mfd/wm97xx.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/wm97xx.h>
|
||||
#include <linux/uaccess.h>
|
||||
@ -581,27 +582,85 @@ static void wm97xx_ts_input_close(struct input_dev *idev)
|
||||
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(dev);
|
||||
int ret = 0, id = 0;
|
||||
struct wm97xx_pdata *pdata = dev_get_platdata(wm->dev);
|
||||
int ret;
|
||||
|
||||
wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL);
|
||||
if (!wm)
|
||||
wm->input_dev = devm_input_allocate_device(wm->dev);
|
||||
if (wm->input_dev == NULL)
|
||||
return -ENOMEM;
|
||||
mutex_init(&wm->codec_mutex);
|
||||
|
||||
wm->dev = dev;
|
||||
dev_set_drvdata(dev, wm);
|
||||
wm->ac97 = to_ac97_t(dev);
|
||||
/* 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);
|
||||
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 */
|
||||
id = wm97xx_reg_read(wm, AC97_VENDOR_ID1);
|
||||
if (id != WM97XX_ID1) {
|
||||
dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id);
|
||||
ret = -ENODEV;
|
||||
goto alloc_err;
|
||||
dev_err(wm->dev,
|
||||
"Device with vendor %04x is not a wm97xx\n", id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
|
||||
@ -629,8 +688,7 @@ static int wm97xx_probe(struct device *dev)
|
||||
default:
|
||||
dev_err(wm->dev, "Support for wm97%02x not compiled in.\n",
|
||||
wm->id & 0xff);
|
||||
ret = -ENODEV;
|
||||
goto alloc_err;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* set up physical characteristics */
|
||||
@ -644,79 +702,58 @@ static int wm97xx_probe(struct device *dev)
|
||||
wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
|
||||
wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
|
||||
|
||||
wm->input_dev = input_allocate_device();
|
||||
if (wm->input_dev == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto alloc_err;
|
||||
}
|
||||
return wm97xx_register_touch(wm);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
static void wm97xx_remove_battery(struct wm97xx *wm)
|
||||
{
|
||||
platform_device_unregister(wm->battery_dev);
|
||||
}
|
||||
|
||||
__set_bit(EV_ABS, wm->input_dev->evbit);
|
||||
__set_bit(EV_KEY, wm->input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, wm->input_dev->keybit);
|
||||
static int wm97xx_add_battery(struct wm97xx *wm,
|
||||
struct wm97xx_batt_pdata *pdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
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 = dev;
|
||||
|
||||
ret = input_register_device(wm->input_dev);
|
||||
if (ret < 0)
|
||||
goto dev_alloc_err;
|
||||
|
||||
/* register our battery device */
|
||||
wm->battery_dev = platform_device_alloc("wm97xx-battery", -1);
|
||||
if (!wm->battery_dev) {
|
||||
ret = -ENOMEM;
|
||||
goto batt_err;
|
||||
}
|
||||
platform_set_drvdata(wm->battery_dev, wm);
|
||||
wm->battery_dev->dev.parent = dev;
|
||||
wm->battery_dev->dev.platform_data = pdata ? pdata->batt_pdata : NULL;
|
||||
ret = platform_device_add(wm->battery_dev);
|
||||
if (ret < 0)
|
||||
goto batt_reg_err;
|
||||
if (!wm->battery_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* 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 = dev;
|
||||
wm->touch_dev->dev.platform_data = pdata;
|
||||
ret = platform_device_add(wm->touch_dev);
|
||||
platform_set_drvdata(wm->battery_dev, wm);
|
||||
wm->battery_dev->dev.parent = wm->dev;
|
||||
wm->battery_dev->dev.platform_data = pdata;
|
||||
ret = platform_device_add(wm->battery_dev);
|
||||
if (ret)
|
||||
platform_device_put(wm->battery_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm97xx_probe(struct device *dev)
|
||||
{
|
||||
struct wm97xx *wm;
|
||||
int ret;
|
||||
struct wm97xx_pdata *pdata = dev_get_platdata(dev);
|
||||
|
||||
wm = devm_kzalloc(dev, sizeof(struct wm97xx), GFP_KERNEL);
|
||||
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)
|
||||
goto touch_reg_err;
|
||||
goto batt_err;
|
||||
|
||||
return ret;
|
||||
|
||||
touch_reg_err:
|
||||
platform_device_put(wm->touch_dev);
|
||||
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);
|
||||
|
||||
batt_err:
|
||||
wm97xx_unregister_touch(wm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -724,14 +761,45 @@ static int wm97xx_remove(struct device *dev)
|
||||
{
|
||||
struct wm97xx *wm = dev_get_drvdata(dev);
|
||||
|
||||
platform_device_unregister(wm->battery_dev);
|
||||
platform_device_unregister(wm->touch_dev);
|
||||
input_unregister_device(wm->input_dev);
|
||||
kfree(wm);
|
||||
wm97xx_remove_battery(wm);
|
||||
wm97xx_unregister_touch(wm);
|
||||
|
||||
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)
|
||||
{
|
||||
struct wm97xx *wm = dev_get_drvdata(dev);
|
||||
@ -828,21 +896,41 @@ EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
|
||||
|
||||
static struct device_driver wm97xx_driver = {
|
||||
.name = "wm97xx-ts",
|
||||
#ifdef CONFIG_AC97_BUS
|
||||
.bus = &ac97_bus_type,
|
||||
#endif
|
||||
.owner = THIS_MODULE,
|
||||
.probe = wm97xx_probe,
|
||||
.remove = wm97xx_remove,
|
||||
.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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
driver_unregister(&wm97xx_driver);
|
||||
platform_driver_unregister(&wm97xx_mfd_driver);
|
||||
}
|
||||
|
||||
module_init(wm97xx_init);
|
||||
|
@ -1746,6 +1746,20 @@ config MFD_WM8994
|
||||
core support for the WM8994, in order to use the actual
|
||||
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
|
||||
tristate "Support for ST Microelectronics STw481x"
|
||||
depends on I2C && (ARCH_NOMADIK || COMPILE_TEST)
|
||||
|
@ -74,6 +74,7 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o
|
||||
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
|
||||
wm8994-objs := wm8994-core.o wm8994-irq.o wm8994-regmap.o
|
||||
obj-$(CONFIG_MFD_WM8994) += wm8994.o
|
||||
obj-$(CONFIG_MFD_WM97xx) += wm97xx-core.o
|
||||
|
||||
obj-$(CONFIG_TPS6105X) += tps6105x.o
|
||||
obj-$(CONFIG_TPS65010) += tps65010.o
|
||||
|
@ -797,12 +797,7 @@ EXPORT_SYMBOL_GPL(arizona_of_get_type);
|
||||
static int arizona_of_get_core_pdata(struct arizona *arizona)
|
||||
{
|
||||
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 count = 0;
|
||||
|
||||
pdata->reset = of_get_named_gpio(arizona->dev->of_node, "wlf,reset", 0);
|
||||
if (pdata->reset == -EPROBE_DEFER) {
|
||||
@ -836,64 +831,6 @@ static int arizona_of_get_core_pdata(struct arizona *arizona)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1026,7 +963,7 @@ int arizona_dev_init(struct arizona *arizona)
|
||||
const char * const mclk_name[] = { "mclk1", "mclk2" };
|
||||
struct device *dev = arizona->dev;
|
||||
const char *type_name = NULL;
|
||||
unsigned int reg, val, mask;
|
||||
unsigned int reg, val;
|
||||
int (*apply_patch)(struct arizona *) = NULL;
|
||||
const struct mfd_cell *subdevs = NULL;
|
||||
int n_subdevs, ret, i;
|
||||
@ -1429,73 +1366,6 @@ int arizona_dev_init(struct arizona *arizona)
|
||||
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_enable(arizona->dev);
|
||||
|
||||
|
366
drivers/mfd/wm97xx-core.c
Normal file
366
drivers/mfd/wm97xx-core.c
Normal file
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* 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");
|
||||
|
52
include/drm/amd_asic_type.h
Normal file
52
include/drm/amd_asic_type.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 {
|
||||
/** Mode for outputs */
|
||||
int out_mono[ARIZONA_MAX_OUTPUT];
|
||||
|
||||
/** Limit output volumes */
|
||||
unsigned int out_vol_limit[2 * ARIZONA_MAX_OUTPUT];
|
||||
|
||||
/** PDM speaker mute setting */
|
||||
unsigned int spk_mute[ARIZONA_MAX_PDM_SPK];
|
||||
|
||||
|
25
include/linux/mfd/wm97xx.h
Normal file
25
include/linux/mfd/wm97xx.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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
|
118
include/sound/ac97/codec.h
Normal file
118
include/sound/ac97/codec.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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
|
20
include/sound/ac97/compat.h
Normal file
20
include/sound/ac97/compat.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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
|
85
include/sound/ac97/controller.h
Normal file
85
include/sound/ac97/controller.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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
|
262
include/sound/ac97/regs.h
Normal file
262
include/sound/ac97/regs.h
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <sound/ac97/regs.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/info.h>
|
||||
@ -35,244 +36,6 @@
|
||||
/* maximum number of devices on the AC97 bus */
|
||||
#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 */
|
||||
#define AC97_SIGMATEL_OUTSEL 0x64 /* Output Select, STAC9758 */
|
||||
#define AC97_SIGMATEL_INSEL 0x66 /* Input Select, STAC9758 */
|
||||
|
@ -2,10 +2,13 @@
|
||||
#ifndef PXA2XX_LIB_H
|
||||
#define PXA2XX_LIB_H
|
||||
|
||||
#include <uapi/sound/asound.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
|
||||
/* PCM */
|
||||
struct snd_pcm_substream;
|
||||
struct snd_pcm_hw_params;
|
||||
struct snd_pcm;
|
||||
|
||||
extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params);
|
||||
@ -22,12 +25,12 @@ extern void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm);
|
||||
|
||||
/* AC97 */
|
||||
|
||||
extern unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg);
|
||||
extern void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val);
|
||||
extern int pxa2xx_ac97_read(int slot, unsigned short reg);
|
||||
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_cold_reset(struct snd_ac97 *ac97);
|
||||
extern void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97);
|
||||
extern bool pxa2xx_ac97_try_warm_reset(void);
|
||||
extern bool pxa2xx_ac97_try_cold_reset(void);
|
||||
extern void pxa2xx_ac97_finish_reset(void);
|
||||
|
||||
extern int pxa2xx_ac97_hw_suspend(void);
|
||||
extern int pxa2xx_ac97_hw_resume(void);
|
||||
|
@ -80,6 +80,8 @@ source "sound/hda/Kconfig"
|
||||
|
||||
source "sound/ppc/Kconfig"
|
||||
|
||||
source "sound/ac97/Kconfig"
|
||||
|
||||
source "sound/aoa/Kconfig"
|
||||
|
||||
source "sound/arm/Kconfig"
|
||||
|
@ -11,6 +11,7 @@ obj-$(CONFIG_SND_AOA) += aoa/
|
||||
|
||||
# This one must be compilable even if sound is configured out
|
||||
obj-$(CONFIG_AC97_BUS) += ac97_bus.o
|
||||
obj-$(CONFIG_AC97_BUS_NEW) += ac97/
|
||||
|
||||
ifeq ($(CONFIG_SND),y)
|
||||
obj-y += last.o
|
||||
|
19
sound/ac97/Kconfig
Normal file
19
sound/ac97/Kconfig
Normal file
@ -0,0 +1,19 @@
|
||||
#
|
||||
# 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
|
8
sound/ac97/Makefile
Normal file
8
sound/ac97/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# 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
|
16
sound/ac97/ac97_core.h
Normal file
16
sound/ac97/ac97_core.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
539
sound/ac97/bus.c
Normal file
539
sound/ac97/bus.c
Normal file
@ -0,0 +1,539 @@
|
||||
/*
|
||||
* 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>");
|
15
sound/ac97/codec.c
Normal file
15
sound/ac97/codec.c
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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_* */
|
||||
|
108
sound/ac97/snd_ac97_compat.c
Normal file
108
sound/ac97/snd_ac97_compat.c
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <sound/pxa2xx-lib.h>
|
||||
|
||||
#include <mach/irqs.h>
|
||||
@ -46,38 +45,41 @@ extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio);
|
||||
* 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;
|
||||
|
||||
if (slot > 0)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&car_mutex);
|
||||
|
||||
/* set up primary or secondary codec space */
|
||||
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
|
||||
reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
|
||||
reg_addr = slot ? &SAC_REG_BASE : &PAC_REG_BASE;
|
||||
reg_addr += (reg >> 1);
|
||||
|
||||
/* start read access across the ac97 link */
|
||||
GSR = GSR_CDONE | GSR_SDONE;
|
||||
gsr_bits = 0;
|
||||
val = *reg_addr;
|
||||
val = (*reg_addr & 0xffff);
|
||||
if (reg == AC97_GPIO_STATUS)
|
||||
goto out;
|
||||
if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
|
||||
!((GSR | gsr_bits) & GSR_SDONE)) {
|
||||
printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
|
||||
__func__, reg, GSR | gsr_bits);
|
||||
val = -1;
|
||||
val = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* valid data now */
|
||||
GSR = GSR_CDONE | GSR_SDONE;
|
||||
gsr_bits = 0;
|
||||
val = *reg_addr;
|
||||
val = (*reg_addr & 0xffff);
|
||||
/* but we've just started another cycle... */
|
||||
wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
|
||||
|
||||
@ -86,29 +88,32 @@ out: mutex_unlock(&car_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pxa2xx_ac97_read);
|
||||
|
||||
void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
|
||||
unsigned short val)
|
||||
int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val)
|
||||
{
|
||||
volatile u32 *reg_addr;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&car_mutex);
|
||||
|
||||
/* set up primary or secondary codec space */
|
||||
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
|
||||
reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
|
||||
reg_addr = slot ? &SAC_REG_BASE : &PAC_REG_BASE;
|
||||
reg_addr += (reg >> 1);
|
||||
|
||||
GSR = GSR_CDONE | GSR_SDONE;
|
||||
gsr_bits = 0;
|
||||
*reg_addr = val;
|
||||
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",
|
||||
__func__, reg, GSR | gsr_bits);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
mutex_unlock(&car_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pxa2xx_ac97_write);
|
||||
|
||||
@ -188,7 +193,7 @@ static inline void pxa_ac97_cold_pxa3xx(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
|
||||
bool pxa2xx_ac97_try_warm_reset(void)
|
||||
{
|
||||
unsigned long gsr;
|
||||
unsigned int timeout = 100;
|
||||
@ -225,7 +230,7 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
|
||||
}
|
||||
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 int timeout = 1000;
|
||||
@ -263,7 +268,7 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
|
||||
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_SDONE_IE|GCR_CDONE_IE;
|
||||
|
@ -29,19 +29,38 @@
|
||||
|
||||
#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)) {
|
||||
pxa2xx_ac97_try_warm_reset(ac97);
|
||||
}
|
||||
if (!pxa2xx_ac97_try_cold_reset())
|
||||
pxa2xx_ac97_try_warm_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 __always_unused ret;
|
||||
|
||||
ret = pxa2xx_ac97_write(ac97->num, reg, val);
|
||||
}
|
||||
|
||||
static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
|
||||
.read = pxa2xx_ac97_read,
|
||||
.write = pxa2xx_ac97_write,
|
||||
.reset = pxa2xx_ac97_reset,
|
||||
.read = pxa2xx_ac97_legacy_read,
|
||||
.write = pxa2xx_ac97_legacy_write,
|
||||
.reset = pxa2xx_ac97_legacy_reset,
|
||||
};
|
||||
|
||||
static struct pxad_param pxa2xx_ac97_pcm_out_req = {
|
||||
|
@ -2,3 +2,10 @@ config SND_SOC_AMD_ACP
|
||||
tristate "AMD Audio Coprocessor support"
|
||||
help
|
||||
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.
|
||||
|
@ -1,3 +1,5 @@
|
||||
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 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <drm/amd_asic_type.h>
|
||||
#include "acp.h"
|
||||
|
||||
#define PLAYBACK_MIN_NUM_PERIODS 2
|
||||
@ -35,6 +35,13 @@
|
||||
#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
|
||||
#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 = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
|
||||
@ -73,10 +80,42 @@ static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
|
||||
.periods_max = CAPTURE_MAX_NUM_PERIODS,
|
||||
};
|
||||
|
||||
struct audio_drv_data {
|
||||
struct snd_pcm_substream *play_stream;
|
||||
struct snd_pcm_substream *capture_stream;
|
||||
void __iomem *acp_mmio;
|
||||
static const struct snd_pcm_hardware acp_st_pcm_hardware_playback = {
|
||||
.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 = 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)
|
||||
@ -143,8 +182,8 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
|
||||
* system memory <-> ACP SRAM
|
||||
*/
|
||||
static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
|
||||
u32 size, int direction,
|
||||
u32 pte_offset)
|
||||
u32 size, int direction,
|
||||
u32 pte_offset, u32 asic_type)
|
||||
{
|
||||
u16 i;
|
||||
u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
|
||||
@ -154,24 +193,46 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
|
||||
dmadscr[i].xfer_val = 0;
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12 + i;
|
||||
dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS +
|
||||
(size / 2) - (i * (size/2));
|
||||
dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS
|
||||
+ (i * (size/2));
|
||||
dmadscr[i].src = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS
|
||||
+ (pte_offset * SZ_4K) + (i * (size/2));
|
||||
dmadscr[i].xfer_val |=
|
||||
(ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) |
|
||||
(size / 2);
|
||||
switch (asic_type) {
|
||||
case CHIP_STONEY:
|
||||
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 {
|
||||
dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH14 + i;
|
||||
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);
|
||||
switch (asic_type) {
|
||||
case CHIP_STONEY:
|
||||
dmadscr[i].src = ACP_SHARED_RAM_BANK_3_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_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,
|
||||
&dmadscr[i]);
|
||||
@ -192,7 +253,8 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
|
||||
* ACP SRAM <-> I2S
|
||||
*/
|
||||
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;
|
||||
@ -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;
|
||||
/* dmadscr[i].src is unused by hardware. */
|
||||
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));
|
||||
break;
|
||||
default:
|
||||
dmadscr[i].dest =
|
||||
ACP_SHARED_RAM_BANK_5_ADDRESS +
|
||||
(i * (size / 2));
|
||||
}
|
||||
dmadscr[i].xfer_val |= BIT(22) |
|
||||
(FROM_ACP_I2S_1 << 16) | (size / 2);
|
||||
}
|
||||
@ -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,
|
||||
struct audio_substream_data *audio_config)
|
||||
struct audio_substream_data *audio_config,
|
||||
u32 asic_type)
|
||||
{
|
||||
u32 pte_offset;
|
||||
|
||||
@ -284,11 +356,11 @@ static void config_acp_dma(void __iomem *acp_mmio,
|
||||
|
||||
/* Configure System memory <-> ACP SRAM DMA descriptors */
|
||||
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 */
|
||||
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 */
|
||||
@ -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. */
|
||||
static int acp_init(void __iomem *acp_mmio)
|
||||
static int acp_init(void __iomem *acp_mmio, u32 asic_type)
|
||||
{
|
||||
u16 bank;
|
||||
u32 val, count, sram_pte_offset;
|
||||
@ -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.
|
||||
* Now, turn off all of them. This can't be done in 'poweron' of
|
||||
* 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++)
|
||||
acp_set_sram_bank_state(acp_mmio, bank, false);
|
||||
if (asic_type != CHIP_STONEY) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -572,9 +655,9 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
|
||||
valid_irq = true;
|
||||
if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_13) ==
|
||||
PLAYBACK_START_DMA_DESCR_CH13)
|
||||
dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
|
||||
else
|
||||
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,
|
||||
1, 0);
|
||||
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)
|
||||
if (adata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
runtime->hw = acp_pcm_hardware_playback;
|
||||
else
|
||||
runtime->hw = acp_pcm_hardware_capture;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
switch (intr_data->asic_type) {
|
||||
case CHIP_STONEY:
|
||||
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,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
@ -652,14 +748,22 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
intr_data->play_stream = substream;
|
||||
for (bank = 1; bank <= 4; bank++)
|
||||
acp_set_sram_bank_state(intr_data->acp_mmio, bank,
|
||||
true);
|
||||
/* 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.
|
||||
*/
|
||||
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 {
|
||||
intr_data->capture_stream = substream;
|
||||
for (bank = 5; bank <= 8; bank++)
|
||||
acp_set_sram_bank_state(intr_data->acp_mmio, bank,
|
||||
true);
|
||||
if (intr_data->asic_type != CHIP_STONEY) {
|
||||
for (bank = 5; bank <= 8; bank++)
|
||||
acp_set_sram_bank_state(intr_data->acp_mmio,
|
||||
bank, true);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -673,6 +777,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
struct page *pg;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
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;
|
||||
rtd = runtime->private_data;
|
||||
@ -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->direction = substream->stream;
|
||||
|
||||
config_acp_dma(rtd->acp_mmio, rtd);
|
||||
config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type);
|
||||
status = 0;
|
||||
} else {
|
||||
status = -ENOMEM;
|
||||
@ -713,40 +819,48 @@ static int acp_dma_hw_free(struct snd_pcm_substream *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)
|
||||
{
|
||||
u16 dscr;
|
||||
u32 mul, dma_config, period_bytes;
|
||||
u32 buffersize;
|
||||
u32 pos = 0;
|
||||
u64 bytescount = 0;
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
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);
|
||||
bytescount = acp_get_byte_count(rtd->acp_mmio, substream->stream);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
dscr = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CUR_DSCR_13);
|
||||
|
||||
if (dscr == PLAYBACK_START_DMA_DESCR_CH13)
|
||||
mul = 0;
|
||||
else
|
||||
mul = 1;
|
||||
pos = (mul * period_bytes);
|
||||
if (bytescount > rtd->renderbytescount)
|
||||
bytescount = bytescount - rtd->renderbytescount;
|
||||
} else {
|
||||
dma_config = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CNTL_14);
|
||||
if (dma_config != 0) {
|
||||
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;
|
||||
|
||||
if (bytescount > rtd->capturebytescount)
|
||||
bytescount = bytescount - rtd->capturebytescount;
|
||||
}
|
||||
pos = do_div(bytescount, buffersize);
|
||||
return bytes_to_frames(runtime, pos);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
PLAYBACK_START_DMA_DESCR_CH13,
|
||||
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 {
|
||||
config_acp_dma_channel(rtd->acp_mmio, ACP_TO_SYSRAM_CH_NUM,
|
||||
CAPTURE_START_DMA_DESCR_CH14,
|
||||
@ -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)
|
||||
{
|
||||
int ret;
|
||||
u32 loops = 1000;
|
||||
u32 loops = 4000;
|
||||
u64 bytescount = 0;
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
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)
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
bytescount = acp_get_byte_count(rtd->acp_mmio,
|
||||
substream->stream);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (rtd->renderbytescount == 0)
|
||||
rtd->renderbytescount = bytescount;
|
||||
acp_dma_start(rtd->acp_mmio,
|
||||
SYSRAM_TO_ACP_CH_NUM, false);
|
||||
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)
|
||||
ACP_TO_I2S_DMA_CH_NUM, true);
|
||||
|
||||
} else {
|
||||
if (rtd->capturebytescount == 0)
|
||||
rtd->capturebytescount = bytescount;
|
||||
acp_dma_start(rtd->acp_mmio,
|
||||
I2S_TO_ACP_DMA_CH_NUM, true);
|
||||
}
|
||||
@ -841,12 +945,15 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
* channels will stopped automatically after its transfer
|
||||
* 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,
|
||||
ACP_TO_I2S_DMA_CH_NUM);
|
||||
else
|
||||
rtd->renderbytescount = 0;
|
||||
} else {
|
||||
ret = acp_dma_stop(rtd->acp_mmio,
|
||||
I2S_TO_ACP_DMA_CH_NUM);
|
||||
rtd->capturebytescount = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
@ -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)
|
||||
{
|
||||
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,
|
||||
NULL, MIN_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)
|
||||
@ -875,14 +999,23 @@ static int acp_dma_close(struct snd_pcm_substream *substream)
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
adata->play_stream = NULL;
|
||||
for (bank = 1; bank <= 4; bank++)
|
||||
acp_set_sram_bank_state(adata->acp_mmio, bank,
|
||||
false);
|
||||
} else {
|
||||
/* 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.
|
||||
* 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;
|
||||
for (bank = 5; bank <= 8; bank++)
|
||||
acp_set_sram_bank_state(adata->acp_mmio, bank,
|
||||
false);
|
||||
if (adata->asic_type != CHIP_STONEY) {
|
||||
for (bank = 5; bank <= 8; bank++)
|
||||
acp_set_sram_bank_state(adata->acp_mmio, bank,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable ACP irq, when the current stream is being closed and
|
||||
@ -916,6 +1049,7 @@ static int acp_audio_probe(struct platform_device *pdev)
|
||||
int status;
|
||||
struct audio_drv_data *audio_drv_data;
|
||||
struct resource *res;
|
||||
const u32 *pdata = pdev->dev.platform_data;
|
||||
|
||||
audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data),
|
||||
GFP_KERNEL);
|
||||
@ -932,6 +1066,7 @@ static int acp_audio_probe(struct platform_device *pdev)
|
||||
|
||||
audio_drv_data->play_stream = NULL;
|
||||
audio_drv_data->capture_stream = NULL;
|
||||
audio_drv_data->asic_type = *pdata;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
@ -949,7 +1084,7 @@ static int acp_audio_probe(struct platform_device *pdev)
|
||||
dev_set_drvdata(&pdev->dev, audio_drv_data);
|
||||
|
||||
/* 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);
|
||||
if (status != 0) {
|
||||
@ -980,21 +1115,31 @@ static int acp_pcm_resume(struct device *dev)
|
||||
u16 bank;
|
||||
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) {
|
||||
for (bank = 1; bank <= 4; bank++)
|
||||
acp_set_sram_bank_state(adata->acp_mmio, bank,
|
||||
/* 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.
|
||||
*/
|
||||
if (adata->asic_type != CHIP_STONEY) {
|
||||
for (bank = 1; bank <= 4; bank++)
|
||||
acp_set_sram_bank_state(adata->acp_mmio, bank,
|
||||
true);
|
||||
}
|
||||
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) {
|
||||
for (bank = 5; bank <= 8; bank++)
|
||||
acp_set_sram_bank_state(adata->acp_mmio, bank,
|
||||
if (adata->asic_type != CHIP_STONEY) {
|
||||
for (bank = 5; bank <= 8; bank++)
|
||||
acp_set_sram_bank_state(adata->acp_mmio, bank,
|
||||
true);
|
||||
}
|
||||
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);
|
||||
return 0;
|
||||
@ -1013,7 +1158,7 @@ static int acp_pcm_runtime_resume(struct device *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);
|
||||
return 0;
|
||||
}
|
||||
@ -1028,14 +1173,15 @@ static struct platform_driver acp_dma_driver = {
|
||||
.probe = acp_audio_probe,
|
||||
.remove = acp_audio_remove,
|
||||
.driver = {
|
||||
.name = "acp_audio_dma",
|
||||
.name = DRV_NAME,
|
||||
.pm = &acp_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(acp_dma_driver);
|
||||
|
||||
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
|
||||
MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
|
||||
MODULE_DESCRIPTION("AMD ACP PCM Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:acp-dma-audio");
|
||||
MODULE_ALIAS("platform:"DRV_NAME);
|
||||
|
199
sound/soc/amd/acp-rt5645.c
Normal file
199
sound/soc/amd/acp-rt5645.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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 @@
|
||||
|
||||
/* Capture SRAM address (as a source in dma descriptor) */
|
||||
#define ACP_SHARED_RAM_BANK_5_ADDRESS 0x400A000
|
||||
#define ACP_SHARED_RAM_BANK_3_ADDRESS 0x4006000
|
||||
|
||||
#define ACP_DMA_RESET_TIME 10000
|
||||
#define ACP_CLOCK_EN_TIME_OUT_VALUE 0x000000FF
|
||||
@ -68,6 +69,7 @@
|
||||
#define CAPTURE_START_DMA_DESCR_CH15 6
|
||||
#define CAPTURE_END_DMA_DESCR_CH15 7
|
||||
|
||||
#define mmACP_I2S_16BIT_RESOLUTION_EN 0x5209
|
||||
enum acp_dma_priority_level {
|
||||
/* 0x0 Specifies the DMA channel is given normal priority */
|
||||
ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0,
|
||||
@ -82,9 +84,26 @@ struct audio_substream_data {
|
||||
u16 num_of_pages;
|
||||
u16 direction;
|
||||
uint64_t size;
|
||||
u64 renderbytescount;
|
||||
u64 capturebytescount;
|
||||
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 {
|
||||
ACP_TILE_P1 = 0,
|
||||
ACP_TILE_P2,
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gcd.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -293,16 +294,99 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
|
||||
}
|
||||
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 *arizona = priv->arizona;
|
||||
struct arizona_pdata *pdata = &arizona->pdata;
|
||||
unsigned int val, mask;
|
||||
int i;
|
||||
|
||||
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;
|
||||
}
|
||||
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] = {
|
||||
"None",
|
||||
@ -2695,6 +2779,80 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
|
||||
}
|
||||
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_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -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_gpio(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_free_spk_irqs(struct arizona *arizona);
|
||||
@ -350,4 +352,6 @@ static inline int arizona_unregister_notifier(struct snd_soc_codec *codec,
|
||||
return blocking_notifier_chain_unregister(&arizona->notifier, nb);
|
||||
}
|
||||
|
||||
int arizona_of_get_audio_pdata(struct arizona *arizona);
|
||||
|
||||
#endif
|
||||
|
@ -1130,7 +1130,6 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
|
||||
|
||||
arizona_init_gpio(codec);
|
||||
arizona_init_mono(codec);
|
||||
arizona_init_notifiers(codec);
|
||||
|
||||
ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
|
||||
if (ret)
|
||||
@ -1230,6 +1229,14 @@ static int cs47l24_probe(struct platform_device *pdev)
|
||||
if (!cs47l24)
|
||||
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);
|
||||
|
||||
cs47l24->core.arizona = arizona;
|
||||
@ -1288,6 +1295,11 @@ static int cs47l24_probe(struct platform_device *pdev)
|
||||
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);
|
||||
if (ret < 0)
|
||||
goto err_dsp_irq;
|
||||
|
@ -1951,7 +1951,6 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
|
||||
return ret;
|
||||
|
||||
arizona_init_gpio(codec);
|
||||
arizona_init_notifiers(codec);
|
||||
|
||||
snd_soc_component_disable_pin(component, "HAPTICS");
|
||||
|
||||
@ -2043,6 +2042,14 @@ static int wm5102_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
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);
|
||||
|
||||
wm5102->core.arizona = arizona;
|
||||
@ -2098,6 +2105,11 @@ static int wm5102_probe(struct platform_device *pdev)
|
||||
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);
|
||||
if (ret < 0)
|
||||
goto err_dsp_irq;
|
||||
|
@ -2290,7 +2290,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
|
||||
|
||||
arizona_init_gpio(codec);
|
||||
arizona_init_mono(codec);
|
||||
arizona_init_notifiers(codec);
|
||||
|
||||
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
|
||||
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
|
||||
@ -2398,6 +2397,14 @@ static int wm5110_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
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.num_inputs = 8;
|
||||
|
||||
@ -2454,6 +2461,11 @@ static int wm5110_probe(struct platform_device *pdev)
|
||||
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);
|
||||
if (ret < 0)
|
||||
goto err_dsp_irq;
|
||||
|
@ -1068,8 +1068,6 @@ static int wm8997_codec_probe(struct snd_soc_codec *codec)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
arizona_init_notifiers(codec);
|
||||
|
||||
snd_soc_component_disable_pin(component, "HAPTICS");
|
||||
|
||||
priv->core.arizona->dapm = dapm;
|
||||
@ -1136,6 +1134,14 @@ static int wm8997_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
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.num_inputs = 4;
|
||||
|
||||
@ -1168,6 +1174,11 @@ static int wm8997_probe(struct platform_device *pdev)
|
||||
pm_runtime_enable(&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);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -1284,7 +1284,6 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec)
|
||||
return ret;
|
||||
|
||||
arizona_init_gpio(codec);
|
||||
arizona_init_notifiers(codec);
|
||||
|
||||
snd_soc_component_disable_pin(component, "HAPTICS");
|
||||
|
||||
@ -1353,6 +1352,14 @@ static int wm8998_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
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.num_inputs = 3; /* IN1L, IN1R, IN2 */
|
||||
|
||||
@ -1377,6 +1384,8 @@ static int wm8998_probe(struct platform_device *pdev)
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_idle(&pdev->dev);
|
||||
|
||||
arizona_init_common(arizona);
|
||||
|
||||
ret = arizona_init_spk_irqs(arizona);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -29,21 +29,41 @@
|
||||
|
||||
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)
|
||||
{
|
||||
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 = {
|
||||
.read = pxa2xx_ac97_read,
|
||||
.write = pxa2xx_ac97_write,
|
||||
.read = pxa2xx_ac97_legacy_read,
|
||||
.write = pxa2xx_ac97_legacy_write,
|
||||
.warm_reset = pxa2xx_ac97_warm_reset,
|
||||
.reset = pxa2xx_ac97_cold_reset,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user