* [PATCH 04/10] power: supply: axp20x_usb_power: add 100mA max current limit for AXP223
From: Quentin Schulz @ 2016-11-25 9:09 UTC (permalink / raw)
To: sre, robh+dt, mark.rutland, wens, linux, maxime.ripard, lee.jones
Cc: Quentin Schulz, linux-pm, devicetree, linux-kernel,
linux-arm-kernel, thomas.petazzoni
In-Reply-To: <20161125090921.23138-1-quentin.schulz@free-electrons.com>
The X-Powers AXP223 shares most of its behaviour with the AXP221 PMIC
but allows the VBUS power supply max current to be set to 100mA (like
the AXP209 PMIC).
This basically adds a new compatible to the VBUS power supply driver and
adds a check on the compatible when setting current max limit.
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---
drivers/power/supply/axp20x_usb_power.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index 638cb52..6d5d451 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -88,11 +88,10 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
switch (v & AXP20X_VBUS_CLIMIT_MASK) {
case AXP20X_VBUC_CLIMIT_100mA:
- if (power->axp20x_id == AXP202_ID) {
- val->intval = 100000;
- } else {
+ if (power->axp20x_id == AXP221_ID)
val->intval = -1; /* No 100mA limit */
- }
+ else
+ val->intval = 100000;
break;
case AXP20X_VBUC_CLIMIT_500mA:
val->intval = 500000;
@@ -268,6 +267,9 @@ static const struct of_device_id axp20x_usb_power_match[] = {
}, {
.compatible = "x-powers,axp221-usb-power-supply",
.data = (void *)AXP221_ID,
+ }, {
+ .compatible = "x-powers,axp223-usb-power-supply",
+ .data = (void *)AXP223_ID,
}, { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
@@ -324,7 +326,8 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
usb_power_desc = &axp20x_usb_power_desc;
irq_names = axp20x_irq_names;
- } else if (power->axp20x_id == AXP221_ID) {
+ } else if (power->axp20x_id == AXP221_ID ||
+ power->axp20x_id == AXP223_ID) {
usb_power_desc = &axp22x_usb_power_desc;
irq_names = axp22x_irq_names;
} else {
--
2.9.3
^ permalink raw reply related
* [PATCH 03/10] Documentation: DT: binding: axp20x_usb_power: add axp223 compatible
From: Quentin Schulz @ 2016-11-25 9:09 UTC (permalink / raw)
To: sre, robh+dt, mark.rutland, wens, linux, maxime.ripard, lee.jones
Cc: thomas.petazzoni, devicetree, linux-pm, linux-kernel,
Quentin Schulz, linux-arm-kernel
In-Reply-To: <20161125090921.23138-1-quentin.schulz@free-electrons.com>
This adds the "x-powers,axp223-usb-power-supply" to the list of
compatibles for AXP20X VBUS power supply driver.
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---
Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt b/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt
index f1d7bee..bf3953c 100644
--- a/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt
+++ b/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt
@@ -3,6 +3,7 @@ AXP20x USB power supply
Required Properties:
-compatible: One of: "x-powers,axp202-usb-power-supply"
"x-powers,axp221-usb-power-supply"
+ "x-powers,axp223-usb-power-supply"
This node is a subnode of the axp20x PMIC.
--
2.9.3
^ permalink raw reply related
* [PATCH 02/10] power: supply: axp20x_usb_power: set min voltage and max current from sysfs
From: Quentin Schulz @ 2016-11-25 9:09 UTC (permalink / raw)
To: sre, robh+dt, mark.rutland, wens, linux, maxime.ripard, lee.jones
Cc: thomas.petazzoni, devicetree, linux-pm, linux-kernel,
Quentin Schulz, linux-arm-kernel
In-Reply-To: <20161125090921.23138-1-quentin.schulz@free-electrons.com>
AXP20X and AXP22X PMICs allow setting the min voltage and max current of
VBUS power supply. This adds entries in sysfs to allow to do so.
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---
drivers/power/supply/axp20x_usb_power.c | 72 +++++++++++++++++++++++++++++++++
include/linux/mfd/axp20x.h | 3 ++
2 files changed, 75 insertions(+)
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index b19754e..638cb52 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -155,6 +155,74 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
return 0;
}
+static int axp20x_usb_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
+ int ret, val1;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ switch (val->intval) {
+ case 4000000:
+ case 4100000:
+ case 4200000:
+ case 4300000:
+ case 4400000:
+ case 4500000:
+ case 4600000:
+ case 4700000:
+ val1 = (val->intval - 4000000) / 100000;
+ ret = regmap_update_bits(power->regmap,
+ AXP20X_VBUS_IPSOUT_MGMT,
+ AXP20X_VBUS_VHOLD_MASK,
+ val1 << 3);
+ if (ret)
+ return ret;
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ switch (val->intval) {
+ case 100000:
+ if (power->axp20x_id == AXP221_ID)
+ return -EINVAL;
+ case 500000:
+ case 900000:
+ val1 = (900000 - val->intval) / 400000;
+ ret = regmap_update_bits(power->regmap,
+ AXP20X_VBUS_IPSOUT_MGMT,
+ AXP20X_VBUS_CLIMIT_MASK, val1);
+ if (ret)
+ return ret;
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
+ psp == POWER_SUPPLY_PROP_CURRENT_MAX;
+}
+
static enum power_supply_property axp20x_usb_power_properties[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
@@ -178,7 +246,9 @@ static const struct power_supply_desc axp20x_usb_power_desc = {
.type = POWER_SUPPLY_TYPE_USB,
.properties = axp20x_usb_power_properties,
.num_properties = ARRAY_SIZE(axp20x_usb_power_properties),
+ .property_is_writeable = axp20x_usb_power_prop_writeable,
.get_property = axp20x_usb_power_get_property,
+ .set_property = axp20x_usb_power_set_property,
};
static const struct power_supply_desc axp22x_usb_power_desc = {
@@ -186,7 +256,9 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
.type = POWER_SUPPLY_TYPE_USB,
.properties = axp22x_usb_power_properties,
.num_properties = ARRAY_SIZE(axp22x_usb_power_properties),
+ .property_is_writeable = axp20x_usb_power_prop_writeable,
.get_property = axp20x_usb_power_get_property,
+ .set_property = axp20x_usb_power_set_property,
};
static const struct of_device_id axp20x_usb_power_match[] = {
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index fec597f..8883595 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -56,6 +56,9 @@ enum {
#define AXP20X_LDO24_V_OUT 0x28
#define AXP20X_LDO3_V_OUT 0x29
#define AXP20X_VBUS_IPSOUT_MGMT 0x30
+
+#define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3)
+
#define AXP20X_V_OFF 0x31
#define AXP20X_OFF_CTRL 0x32
#define AXP20X_CHRG_CTRL1 0x33
--
2.9.3
^ permalink raw reply related
* [PATCH 01/10] power: supply: axp20x_usb_power: use of_device_id data field instead of device_is_compatible
From: Quentin Schulz @ 2016-11-25 9:09 UTC (permalink / raw)
To: sre, robh+dt, mark.rutland, wens, linux, maxime.ripard, lee.jones
Cc: thomas.petazzoni, devicetree, linux-pm, linux-kernel,
Quentin Schulz, linux-arm-kernel
In-Reply-To: <20161125090921.23138-1-quentin.schulz@free-electrons.com>
This replaces calls to of_device_is_compatible to check data field of
of_device_id matched when probing the driver.
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---
drivers/power/supply/axp20x_usb_power.c | 39 ++++++++++++++++++++-------------
1 file changed, 24 insertions(+), 15 deletions(-)
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index 6af6feb..b19754e 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -17,6 +17,7 @@
#include <linux/mfd/axp20x.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
@@ -45,6 +46,7 @@ struct axp20x_usb_power {
struct device_node *np;
struct regmap *regmap;
struct power_supply *supply;
+ int axp20x_id;
};
static irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
@@ -86,8 +88,7 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
switch (v & AXP20X_VBUS_CLIMIT_MASK) {
case AXP20X_VBUC_CLIMIT_100mA:
- if (of_device_is_compatible(power->np,
- "x-powers,axp202-usb-power-supply")) {
+ if (power->axp20x_id == AXP202_ID) {
val->intval = 100000;
} else {
val->intval = -1; /* No 100mA limit */
@@ -130,8 +131,7 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
val->intval = POWER_SUPPLY_HEALTH_GOOD;
- if (of_device_is_compatible(power->np,
- "x-powers,axp202-usb-power-supply")) {
+ if (power->axp20x_id == AXP202_ID) {
ret = regmap_read(power->regmap,
AXP20X_USB_OTG_STATUS, &v);
if (ret)
@@ -189,6 +189,17 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
.get_property = axp20x_usb_power_get_property,
};
+static const struct of_device_id axp20x_usb_power_match[] = {
+ {
+ .compatible = "x-powers,axp202-usb-power-supply",
+ .data = (void *)AXP202_ID,
+ }, {
+ .compatible = "x-powers,axp221-usb-power-supply",
+ .data = (void *)AXP221_ID,
+ }, { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
+
static int axp20x_usb_power_probe(struct platform_device *pdev)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
@@ -200,11 +211,16 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
"VBUS_PLUGIN", "VBUS_REMOVAL", NULL };
static const char * const *irq_names;
const struct power_supply_desc *usb_power_desc;
+ const struct of_device_id *of_id;
int i, irq, ret;
if (!of_device_is_available(pdev->dev.of_node))
return -ENODEV;
+ of_id = of_match_device(axp20x_usb_power_match, &pdev->dev);
+ if (!of_id)
+ return -ENODEV;
+
if (!axp20x) {
dev_err(&pdev->dev, "Parent drvdata not set\n");
return -EINVAL;
@@ -214,11 +230,12 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
if (!power)
return -ENOMEM;
+ power->axp20x_id = (int)of_id->data;
+
power->np = pdev->dev.of_node;
power->regmap = axp20x->regmap;
- if (of_device_is_compatible(power->np,
- "x-powers,axp202-usb-power-supply")) {
+ if (power->axp20x_id == AXP202_ID) {
/* Enable vbus valid checking */
ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
AXP20X_VBUS_MON_VBUS_VALID,
@@ -235,8 +252,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
usb_power_desc = &axp20x_usb_power_desc;
irq_names = axp20x_irq_names;
- } else if (of_device_is_compatible(power->np,
- "x-powers,axp221-usb-power-supply")) {
+ } else if (power->axp20x_id == AXP221_ID) {
usb_power_desc = &axp22x_usb_power_desc;
irq_names = axp22x_irq_names;
} else {
@@ -272,13 +288,6 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id axp20x_usb_power_match[] = {
- { .compatible = "x-powers,axp202-usb-power-supply" },
- { .compatible = "x-powers,axp221-usb-power-supply" },
- { }
-};
-MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
-
static struct platform_driver axp20x_usb_power_driver = {
.probe = axp20x_usb_power_probe,
.driver = {
--
2.9.3
^ permalink raw reply related
* [PATCH 00/10] add support for VBUS max current and min voltage limits AXP20X and AXP22X PMICs
From: Quentin Schulz @ 2016-11-25 9:09 UTC (permalink / raw)
To: sre, robh+dt, mark.rutland, wens, linux, maxime.ripard, lee.jones
Cc: Quentin Schulz, linux-pm, devicetree, linux-kernel,
linux-arm-kernel, thomas.petazzoni
The X-Powers AXP209 and AXP20X PMICs are able to set a limit for the VBUS power
supply for both max current and min voltage supplied. This series of patch adds
the possibility to set these limits from sysfs.
Also, the AXP223 PMIC shares most of its behaviour with the AXP221 but the
former can set the VBUS power supply max current to 100mA, unlike the latter.
The AXP223 VBUS power supply driver used to probe on the AXP221 compatible. This
series of patch introduces a new compatible for the AXP223 to be able to set the
current max limit to 100mA.
With that new compatible, boards having the AXP223 see their DT updated to use
the VBUS power supply driver with the correct compatible.
This series of patch also migrates from of_device_is_compatible function to the
data field of of_device_id to identify the compatible used to probe. This
improves the code readability.
Quentin Schulz (10):
power: supply: axp20x_usb_power: use of_device_id data field instead
of device_is_compatible
power: supply: axp20x_usb_power: set min voltage and max current from
sysfs
Documentation: DT: binding: axp20x_usb_power: add axp223 compatible
power: supply: axp20x_usb_power: add 100mA max current limit for
AXP223
mfd: axp20x: add separate MFD cell for AXP223
ARM: dtsi: add DTSI for AXP223
ARM: dts: sun8i-a33-olinuxino: use AXP223 DTSI
ARM: dts: sun8i-a33-sinlinx-sina33: use AXP223 DTSI
ARM: dts: sun8i-r16-parrot: use AXP223 DTSI
ARM: dtsi: sun8i-reference-design-tablet: use AXP223 DTSI
.../bindings/power/supply/axp20x_usb_power.txt | 1 +
arch/arm/boot/dts/axp223.dtsi | 55 ++++++++++
arch/arm/boot/dts/sun8i-a33-olinuxino.dts | 2 +-
arch/arm/boot/dts/sun8i-a33-sinlinx-sina33.dts | 2 +-
arch/arm/boot/dts/sun8i-r16-parrot.dts | 2 +-
.../boot/dts/sun8i-reference-design-tablet.dtsi | 2 +-
drivers/mfd/axp20x.c | 22 +++-
drivers/power/supply/axp20x_usb_power.c | 120 +++++++++++++++++----
include/linux/mfd/axp20x.h | 3 +
9 files changed, 186 insertions(+), 23 deletions(-)
create mode 100644 arch/arm/boot/dts/axp223.dtsi
--
2.9.3
^ permalink raw reply
* Re: [net-next PATCH v1 0/2] stmmac: dwmac-meson8b: configurable RGMII TX delay
From: Giuseppe CAVALLARO @ 2016-11-25 8:59 UTC (permalink / raw)
To: Jerome Brunet, Martin Blumenstingl,
linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
davem-fT/PcQaiUtIeIZ0/mPfg9Q, khilman-rdvid1DuHRBWk0Htik3J/w,
mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
alexandre.torgue-qxv4g6HH51o, carlo-KA+7E9HrN00dnm+yROfE0A
In-Reply-To: <1480003681.17538.142.camel-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
On 11/24/2016 5:08 PM, Jerome Brunet wrote:
> On Thu, 2016-11-24 at 15:34 +0100, Martin Blumenstingl wrote:
>> Currently the dwmac-meson8b stmmac glue driver uses a hardcoded 1/4
>> cycle TX clock delay. This seems to work fine for many boards (for
>> example Odroid-C2 or Amlogic's reference boards) but there are some
>> others where TX traffic is simply broken.
>> There are probably multiple reasons why it's working on some boards
>> while it's broken on others:
>> - some of Amlogic's reference boards are using a Micrel PHY
>> - hardware circuit design
>> - maybe more...
>>
>> This raises a question though:
>> Which device is supposed to enable the TX delay when both MAC and PHY
>> support it? And should we implement it for each PHY / MAC separately
>> or should we think about a more generic solution (currently it's not
>> possible to disable the TX delay generated by the RTL8211F PHY via
>> devicetree when using phy-mode "rgmii")?
>>
>> iperf3 results on my Mecool BB2 board (Meson GXM, RTL8211F PHY) with
>> TX clock delay disabled on the MAC (as it's enabled in the PHY
>> driver).
>> TX throughput was virtually zero before:
>> $ iperf3 -c 192.168.1.100 -R
>> Connecting to host 192.168.1.100, port 5201
>> Reverse mode, remote host 192.168.1.100 is sending
>> [ 4] local 192.168.1.206 port 52828 connected to 192.168.1.100 port
>> 5201
>> [ ID] Interval Transfer Bandwidth
>> [ 4] 0.00-1.00 sec 108 MBytes 901
>> Mbits/sec
>> [ 4] 1.00-2.00 sec 94.2 MBytes 791
>> Mbits/sec
>> [ 4] 2.00-3.00 sec 96.5 MBytes 810
>> Mbits/sec
>> [ 4] 3.00-4.00 sec 96.2 MBytes 808
>> Mbits/sec
>> [ 4] 4.00-5.00 sec 96.6 MBytes 810
>> Mbits/sec
>> [ 4] 5.00-6.00 sec 96.5 MBytes 810
>> Mbits/sec
>> [ 4] 6.00-7.00 sec 96.6 MBytes 810
>> Mbits/sec
>> [ 4] 7.00-8.00 sec 96.5 MBytes 809
>> Mbits/sec
>> [ 4] 8.00-9.00 sec 105 MBytes 884
>> Mbits/sec
>> [ 4] 9.00-10.00 sec 111 MBytes 934
>> Mbits/sec
>> - - - - - - - - - - - - - - - - - - - - - - - - -
>> [ ID] Interval Transfer Bandwidth Retr
>> [ 4] 0.00-10.00 sec 1000 MBytes 839
>> Mbits/sec 0 sender
>> [ 4] 0.00-10.00 sec 998 MBytes 837
>> Mbits/sec receiver
>>
>> iperf Done.
>> $ iperf3 -c 192.168.1.100
>> Connecting to host 192.168.1.100, port 5201
>> [ 4] local 192.168.1.206 port 52832 connected to 192.168.1.100 port
>> 5201
>> [ ID] Interval Transfer Bandwidth Retr Cwnd
>> [ 4] 0.00-1.01 sec 99.5 MBytes 829 Mbits/sec 117 139
>> KBytes
>> [ 4] 1.01-2.00 sec 105 MBytes 884 Mbits/sec 129 70.7
>> KBytes
>> [ 4] 2.00-3.01 sec 107 MBytes 889 Mbits/sec 106 187
>> KBytes
>> [ 4] 3.01-4.01 sec 105 MBytes 878 Mbits/sec 92 143
>> KBytes
>> [ 4] 4.01-5.00 sec 105 MBytes 882 Mbits/sec 140 129
>> KBytes
>> [ 4] 5.00-6.01 sec 106 MBytes 883 Mbits/sec 115 195
>> KBytes
>> [ 4] 6.01-7.00 sec 102 MBytes 863 Mbits/sec 133 70.7
>> KBytes
>> [ 4] 7.00-8.01 sec 106 MBytes 884 Mbits/sec 143 97.6
>> KBytes
>> [ 4] 8.01-9.01 sec 104 MBytes 875 Mbits/sec 124 107
>> KBytes
>> [ 4] 9.01-10.01 sec 105 MBytes 876 Mbits/sec 90 139
>> KBytes
>> - - - - - - - - - - - - - - - - - - - - - - - - -
>> [ ID] Interval Transfer Bandwidth Retr
>> [ 4] 0.00-10.01 sec 1.02 GBytes 874
>> Mbits/sec 1189 sender
>> [ 4] 0.00-10.01 sec 1.02 GBytes 873
>> Mbits/sec receiver
>>
>
> Cool, one more board working ;)
> I tried your patch on another board (MXQ_V2.1, with sheep printed on
> the PCB). It 's not improving the situation for this one unfortunately.
> Actually I already tried playing with the TX delay on the MAC and PHY
> but I could get any good results with the boards I have.
>
> It is strange that we can adjust the delay by 2ns steps, when delay
> seens by the phy should be 2ns ...
>
Ok, as said, I could expect that extra delay was needed on GiGa.
FYI, on ST platforms, this extra delay was added in the pintctrl
dtsi. I mean, in some way, ad-hoc setup was solved at device-tree
level. Usually, extra delay could depend on the PCB.
peppe
>> iperf Done.
>>
>>
>> Martin Blumenstingl (2):
>> net: dt-bindings: add RGMII TX delay configuration to meson8b-dwmac
>> net: stmmac: dwmac-meson8b: make the RGMII TX delay configurable
>>
>> Documentation/devicetree/bindings/net/meson-dwmac.txt | 11
>> +++++++++++
>> drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c | 16
>> +++++++++++-----
>> include/dt-bindings/net/dwmac-meson8b.h | 18
>> ++++++++++++++++++
>> 3 files changed, 40 insertions(+), 5 deletions(-)
>> create mode 100644 include/dt-bindings/net/dwmac-meson8b.h
>>
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH mmc/next] mmc: sh_mmcif: Document r8a73a4, r8a7778 and sh73a0 DT bindings
From: Geert Uytterhoeven @ 2016-11-25 8:58 UTC (permalink / raw)
To: Simon Horman
Cc: Ulf Hansson, Linux MMC List,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Magnus Damm,
Linux-Renesas
In-Reply-To: <20161125075614.GB14431-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
On Fri, Nov 25, 2016 at 8:56 AM, Simon Horman
<horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org> wrote:
> Simply document new compatibility strings as the driver is already
> activated using a fallback compatibility string.
>
> These compat strings are in keeping with those for all other
> Renesas ARM based SoCs with sh_mmcif enabled in mainline.
>
> Signed-off-by: Simon Horman <horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
Acked-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: [PATCH V5 3/3] ARM64 LPC: LPC driver implementation on Hip06
From: Gabriele Paoloni @ 2016-11-25 8:46 UTC (permalink / raw)
To: Arnd Bergmann, linux-arm-kernel@lists.infradead.org
Cc: mark.rutland@arm.com, benh@kernel.crashing.org,
liviu.dudau@arm.com, Linuxarm, lorenzo.pieralisi@arm.com,
xuwei (O), Jason Gunthorpe, T homas Petazzoni,
linux-serial@vger.kernel.org, catalin.marinas@arm.com,
devicetree@vger.kernel.org, minyard@acm.org, will.deacon@arm.com,
John Garry, zourongrong@gmail.com, robh+dt@kernel.org,
bhelgaas@go og le.com, kantyzc@163.com, zhichang.yuan02
In-Reply-To: <4675465.4Qhqy6WU4X@wuerfel>
Hi Arnd
Many thanks for your contribution, much appreciated
I have some comments...see inline below
> -----Original Message-----
> From: Arnd Bergmann [mailto:arnd@arndb.de]
> Sent: 23 November 2016 23:23
> To: linux-arm-kernel@lists.infradead.org
> Cc: Gabriele Paoloni; mark.rutland@arm.com; catalin.marinas@arm.com;
> linux-pci@vger.kernel.org; liviu.dudau@arm.com; Linuxarm;
> lorenzo.pieralisi@arm.com; xuwei (O); Jason Gunthorpe; T homas
> Petazzoni; linux-serial@vger.kernel.org; benh@kernel.crashing.org;
> devicetree@vger.kernel.org; minyard@acm.org; will.deacon@arm.com; John
> Garry; olof@lixom.net; robh+dt@kernel.org; bhelgaas@go og le.com;
> kantyzc@163.com; zhichang.yuan02@gmail.com; linux-
> kernel@vger.kernel.org; Yuanzhichang; zourongrong@gmail.com
> Subject: Re: [PATCH V5 3/3] ARM64 LPC: LPC driver implementation on
> Hip06
>
> On Wednesday, November 23, 2016 6:07:11 PM CET Arnd Bergmann wrote:
> > On Wednesday, November 23, 2016 3:22:33 PM CET Gabriele Paoloni
> wrote:
> > > From: Arnd Bergmann [mailto:arnd@arndb.de]
> > > > On Friday, November 18, 2016 5:03:11 PM CET Gabriele Paoloni
> wrote:
> >
> > Please don't proliferate the use of
> > pci_pio_to_address/pci_address_to_pio here, computing the physical
> > address from the logical address is trivial, you just need to
> > subtract the start of the range that you already use when matching
> > the port number range.
> >
> > The only thing we need here is to make of_address_to_resource()
> > return the correct logical port number that was registered for
> > a given host device when asked to translate an address that
> > does not have a CPU address associated with it.
>
> Ok, I admit this was a little harder than I expected, but see below
> for a rough outline of how I think it can be done.
>
> This makes it possible to translate bus specific I/O port numbers
> from device nodes into Linux port numbers, and gives a way to register
> them. We could take this further and completely remove
> pci_pio_to_address
> and pci_address_to_pio if we make the I/O port translation always
> go through the io_range list, looking up up the hostbridge by fwnode,
> but we don't have to do that now.
>
> The patch is completely untested and probably buggy, it just seemed
> easier to put out a prototype than to keep going in circles with the
> discussion.
>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>
> diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
> index bf601d4df8cf..6cadf0501bb0 100644
> --- a/drivers/acpi/pci_root.c
> +++ b/drivers/acpi/pci_root.c
> @@ -730,7 +730,8 @@ static void acpi_pci_root_validate_resources(struct
> device *dev,
> }
> }
>
> -static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
> +static void acpi_pci_root_remap_iospace(struct fwnode_handle *node,
> + struct resource_entry *entry)
> {
> #ifdef PCI_IOBASE
> struct resource *res = entry->res;
> @@ -739,11 +740,7 @@ static void acpi_pci_root_remap_iospace(struct
> resource_entry *entry)
> resource_size_t length = resource_size(res);
> unsigned long port;
>
> - if (pci_register_io_range(cpu_addr, length))
> - goto err;
> -
> - port = pci_address_to_pio(cpu_addr);
> - if (port == (unsigned long)-1)
> + if (pci_register_io_range(node, cpu_addr, length, &port))
> goto err;
>
> res->start = port;
> @@ -781,7 +778,8 @@ int acpi_pci_probe_root_resources(struct
> acpi_pci_root_info *info)
> else {
> resource_list_for_each_entry_safe(entry, tmp, list) {
> if (entry->res->flags & IORESOURCE_IO)
> - acpi_pci_root_remap_iospace(entry);
> + acpi_pci_root_remap_iospace(&device->fwnode,
> + entry);
>
> if (entry->res->flags & IORESOURCE_DISABLED)
> resource_list_destroy_entry(entry);
> diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
> index a50025a3777f..df96955a43f8 100644
> --- a/drivers/block/nbd.c
> +++ b/drivers/block/nbd.c
> @@ -760,8 +760,10 @@ static int __nbd_ioctl(struct block_device *bdev,
> struct nbd_device *nbd,
> set_bit(NBD_RUNNING, &nbd->runtime_flags);
> blk_mq_update_nr_hw_queues(&nbd->tag_set, nbd-
> >num_connections);
> args = kcalloc(num_connections, sizeof(*args), GFP_KERNEL);
> - if (!args)
> + if (!args) {
> + error = -ENOMEM;
> goto out_err;
> + }
> nbd->task_recv = current;
> mutex_unlock(&nbd->config_lock);
>
> diff --git a/drivers/of/address.c b/drivers/of/address.c
> index 02b2903fe9d2..5decaba96eed 100644
> --- a/drivers/of/address.c
> +++ b/drivers/of/address.c
> @@ -2,6 +2,7 @@
> #define pr_fmt(fmt) "OF: " fmt
>
> #include <linux/device.h>
> +#include <linux/fwnode.h>
> #include <linux/io.h>
> #include <linux/ioport.h>
> #include <linux/module.h>
> @@ -323,14 +324,9 @@ int of_pci_range_to_resource(struct of_pci_range
> *range,
>
> if (res->flags & IORESOURCE_IO) {
> unsigned long port;
> - err = pci_register_io_range(range->cpu_addr, range->size);
> + err = pci_register_io_range(&np->fwnode, range->cpu_addr,
> range->size, &port);
> if (err)
> goto invalid_range;
> - port = pci_address_to_pio(range->cpu_addr);
> - if (port == (unsigned long)-1) {
> - err = -EINVAL;
> - goto invalid_range;
> - }
> res->start = port;
> } else {
> if ((sizeof(resource_size_t) < 8) &&
> @@ -479,7 +475,7 @@ static int of_empty_ranges_quirk(struct device_node
> *np)
> return false;
> }
>
> -static int of_translate_one(struct device_node *parent, struct of_bus
> *bus,
> +static u64 of_translate_one(struct device_node *parent, struct of_bus
> *bus,
> struct of_bus *pbus, __be32 *addr,
> int na, int ns, int pna, const char *rprop)
> {
> @@ -507,7 +503,7 @@ static int of_translate_one(struct device_node
> *parent, struct of_bus *bus,
> ranges = of_get_property(parent, rprop, &rlen);
> if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
> pr_debug("no ranges; cannot translate\n");
> - return 1;
> + return OF_BAD_ADDR;
> }
> if (ranges == NULL || rlen == 0) {
> offset = of_read_number(addr, na);
> @@ -528,7 +524,7 @@ static int of_translate_one(struct device_node
> *parent, struct of_bus *bus,
> }
> if (offset == OF_BAD_ADDR) {
> pr_debug("not found !\n");
> - return 1;
> + return offset;
> }
> memcpy(addr, ranges + na, 4 * pna);
>
> @@ -537,7 +533,10 @@ static int of_translate_one(struct device_node
> *parent, struct of_bus *bus,
> pr_debug("with offset: %llx\n", (unsigned long long)offset);
>
> /* Translate it into parent bus space */
> - return pbus->translate(addr, offset, pna);
> + if (pbus->translate(addr, offset, pna))
> + return OF_BAD_ADDR;
> +
> + return offset;
> }
>
> /*
> @@ -549,9 +548,14 @@ static int of_translate_one(struct device_node
> *parent, struct of_bus *bus,
> * that translation is impossible (that is we are not dealing with a
> value
> * that can be mapped to a cpu physical address). This is not really
> specified
> * that way, but this is traditionally the way IBM at least do things
> + *
> + * Whenever the translation fails, the *host pointer will be set to
> the
> + * device that lacks a tranlation, and the return code is relative to
> + * that node.
This seems to be wrong to me. We are abusing of the error conditions.
So effectively if there is a buggy DT for an IO resource we end up
assuming that we are using a special IO device with unmapped addresses.
The patch at the bottom apply on top of this one and I think is a more
reasonable approach
> */
> static u64 __of_translate_address(struct device_node *dev,
> - const __be32 *in_addr, const char *rprop)
> + const __be32 *in_addr, const char *rprop,
> + struct device_node **host)
> {
> struct device_node *parent = NULL;
> struct of_bus *bus, *pbus;
> @@ -564,6 +568,7 @@ static u64 __of_translate_address(struct
> device_node *dev,
> /* Increase refcount at current level */
> of_node_get(dev);
>
> + *host = NULL;
> /* Get parent & match bus type */
> parent = of_get_parent(dev);
> if (parent == NULL)
> @@ -600,8 +605,9 @@ static u64 __of_translate_address(struct
> device_node *dev,
> pbus = of_match_bus(parent);
> pbus->count_cells(dev, &pna, &pns);
> if (!OF_CHECK_COUNTS(pna, pns)) {
> - pr_err("Bad cell count for %s\n",
> - of_node_full_name(dev));
> + pr_debug("Bad cell count for %s\n",
> + of_node_full_name(dev));
> + *host = of_node_get(parent);
> break;
> }
>
> @@ -609,7 +615,9 @@ static u64 __of_translate_address(struct
> device_node *dev,
> pbus->name, pna, pns, of_node_full_name(parent));
>
> /* Apply bus translation */
> - if (of_translate_one(dev, bus, pbus, addr, na, ns, pna,
> rprop))
> + result = of_translate_one(dev, bus, pbus, addr, na, ns,
> + pna, rprop);
> + if (result == OF_BAD_ADDR)
It seems to me that here you missed "*host = of_node_get(parent);"..?
> break;
>
> /* Complete the move up one level */
> @@ -628,13 +636,32 @@ static u64 __of_translate_address(struct
> device_node *dev,
>
> u64 of_translate_address(struct device_node *dev, const __be32
> *in_addr)
> {
> - return __of_translate_address(dev, in_addr, "ranges");
> + struct device_node *host;
> + u64 ret;
> +
> + ret = __of_translate_address(dev, in_addr, "ranges", &host);
> + if (host) {
> + of_node_put(host);
> + return OF_BAD_ADDR;
> + }
> +
> + return ret;
> }
> EXPORT_SYMBOL(of_translate_address);
>
> u64 of_translate_dma_address(struct device_node *dev, const __be32
> *in_addr)
> {
> - return __of_translate_address(dev, in_addr, "dma-ranges");
> + struct device_node *host;
> + u64 ret;
> +
> + ret = __of_translate_address(dev, in_addr, "dma-ranges", &host);
> +
> + if (host) {
> + of_node_put(host);
> + return OF_BAD_ADDR;
> + }
> +
> + return ret;
> }
> EXPORT_SYMBOL(of_translate_dma_address);
>
> @@ -676,29 +703,48 @@ const __be32 *of_get_address(struct device_node
> *dev, int index, u64 *size,
> }
> EXPORT_SYMBOL(of_get_address);
>
> +extern unsigned long extio_translate(struct fwnode_handle *node,
> unsigned long offset);
> +
> +u64 of_translate_ioport(struct device_node *dev, const __be32
> *in_addr)
> +{
> + u64 taddr;
> + unsigned long port;
> + struct device_node *host;
> +
> + taddr = __of_translate_address(dev, in_addr, "ranges", &host);
> + if (host) {
> + /* host specific port access */
> + port = extio_translate(&host->fwnode, taddr);
> + of_node_put(host);
> + } else {
> + /* memory mapped I/O range */
> + port = pci_address_to_pio(taddr);
> + if (port == (unsigned long)-1)
> + return OF_BAD_ADDR;
> + }
> +
> + return port;
> +}
> +
> static int __of_address_to_resource(struct device_node *dev,
> const __be32 *addrp, u64 size, unsigned int flags,
> const char *name, struct resource *r)
> {
> u64 taddr;
>
> - if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
> + if (flags & IORESOURCE_MEM)
> + taddr = of_translate_address(dev, addrp);
> + else if (flags & IORESOURCE_IO)
> + taddr = of_translate_ioport(dev, addrp);
> + else
> return -EINVAL;
> - taddr = of_translate_address(dev, addrp);
> +
> if (taddr == OF_BAD_ADDR)
> return -EINVAL;
> memset(r, 0, sizeof(struct resource));
> - if (flags & IORESOURCE_IO) {
> - unsigned long port;
> - port = pci_address_to_pio(taddr);
> - if (port == (unsigned long)-1)
> - return -EINVAL;
> - r->start = port;
> - r->end = port + size - 1;
> - } else {
> - r->start = taddr;
> - r->end = taddr + size - 1;
> - }
> +
> + r->start = taddr;
> + r->end = taddr + size - 1;
> r->flags = flags;
> r->name = name ? name : dev->full_name;
>
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index eda6a7cf0e54..320ab9fbf6af 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -3249,6 +3249,7 @@ EXPORT_SYMBOL(pci_request_regions_exclusive);
> #ifdef PCI_IOBASE
> struct io_range {
> struct list_head list;
> + struct fwnode_handle *node;
> phys_addr_t start;
> resource_size_t size;
> };
> @@ -3257,11 +3258,14 @@ static LIST_HEAD(io_range_list);
> static DEFINE_SPINLOCK(io_range_lock);
> #endif
>
> +#define IO_RANGE_IOEXT (resource_size_t)(-1ull)
> +
> /*
> * Record the PCI IO range (expressed as CPU physical address + size).
> * Return a negative value if an error has occured, zero otherwise
> */
> -int __weak pci_register_io_range(phys_addr_t addr, resource_size_t
> size)
> +int __weak pci_register_io_range(struct fwnode_handle *node,
> phys_addr_t addr,
> + resource_size_t size, unsigned long *port)
> {
> int err = 0;
>
> @@ -3272,7 +3276,12 @@ int __weak pci_register_io_range(phys_addr_t
> addr, resource_size_t size)
> /* check if the range hasn't been previously recorded */
> spin_lock(&io_range_lock);
> list_for_each_entry(range, &io_range_list, list) {
> - if (addr >= range->start && addr + size <= range->start +
> size) {
> + if (node == range->node)
> + goto end_register;
> +
It seems to me that the condition above is sufficient; i.e.
we can remove the one here below...?
> + if (addr != IO_RANGE_IOEXT &&
> + addr >= range->start &&
> + addr + size <= range->start + size) {
> /* range already registered, bail out */
> goto end_register;
> }
> @@ -3298,6 +3307,7 @@ int __weak pci_register_io_range(phys_addr_t
> addr, resource_size_t size)
> goto end_register;
> }
>
> + range->node = node;
> range->start = addr;
> range->size = size;
>
> @@ -3305,11 +3315,26 @@ int __weak pci_register_io_range(phys_addr_t
> addr, resource_size_t size)
>
> end_register:
> spin_unlock(&io_range_lock);
> +
> + *port = allocated_size;
> +#else
> + /*
> + * powerpc and microblaze have their own registration,
> + * just look up the value here
> + */
> + *port = pci_address_to_pio(addr);
> #endif
>
> return err;
> }
>
> +#ifdef CONFIG_IOEXT
> +int ioext_register_io_range
> +{
> + return pci_register_io_range(node, IO_RANGE_IOEXT, size, port);
> +}
> +#endif
> +
> phys_addr_t pci_pio_to_address(unsigned long pio)
> {
> phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index 6bd94a803e8f..b7a8fa3da3ca 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -1192,7 +1192,8 @@ int __must_check pci_bus_alloc_resource(struct
> pci_bus *bus,
> void *alignf_data);
>
>
> -int pci_register_io_range(phys_addr_t addr, resource_size_t size);
> +int pci_register_io_range(struct fwnode_handle *node, phys_addr_t
> addr,
> + resource_size_t size, unsigned long *port);
> unsigned long pci_address_to_pio(phys_addr_t addr);
> phys_addr_t pci_pio_to_address(unsigned long pio);
> int pci_remap_iospace(const struct resource *res, phys_addr_t
> phys_addr);
I think the patch below is a more reasonable approach to identify
a host that does not support address translation and it should
guarantee safety against broken DTs...
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 5decaba..9bfc526 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -540,6 +540,49 @@ static u64 of_translate_one(struct device_node *parent, struct of_bus *bus,
}
Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
/*
+ * of_isa_indirect_io - get the IO address from some isa reg property value.
+ * For some isa/lpc devices, no ranges property in ancestor node.
+ * The device addresses are described directly in their regs property.
+ * This fixup function will be called to get the IO address of isa/lpc
+ * devices when the normal of_translation failed.
+ *
+ * @parent: points to the parent dts node;
+ * @bus: points to the of_bus which can be used to parse address;
+ * @addr: the address from reg property;
+ * @na: the address cell counter of @addr;
+ * @presult: store the address paresed from @addr;
+ *
+ * return 1 when successfully get the I/O address;
+ * 0 will return for some failures.
+ */
+static int of_get_isa_indirect_io(struct device_node *parent,
+ struct of_bus *bus, __be32 *addr,
+ int na, u64 *presult)
+{
+ unsigned int flags;
+ unsigned int rlen;
+
+ /* whether support indirectIO */
+ if (!indirect_io_enabled())
+ return 0;
+
+ if (!of_bus_isa_match(parent))
+ return 0;
+
+ flags = bus->get_flags(addr);
+ if (!(flags & IORESOURCE_IO))
+ return 0;
+
+ /* there is ranges property, apply the normal translation directly. */
+ if (of_get_property(parent, "ranges", &rlen))
+ return 0;
+
+ *presult = of_read_number(addr + 1, na - 1);
+ /* this fixup is only valid for specific I/O range. */
+ return addr_is_indirect_io(*presult);
+}
+
+/*
* Translate an address from the device-tree into a CPU physical address,
* this walks up the tree and applies the various bus mappings on the
* way.
@@ -600,14 +643,23 @@ static u64 __of_translate_address(struct device_node *dev,
result = of_read_number(addr, na);
break;
}
+ /*
+ * For indirectIO device which has no ranges property, get
+ * the address from reg directly.
+ */
+ if (of_get_isa_indirect_io(dev, bus, addr, na, &result)) {
+ pr_debug("isa indirectIO matched(%s)..addr = 0x%llx\n",
+ of_node_full_name(dev), result);
+ *host = of_node_get(parent);
+ break;
+ }
/* Get new parent bus and counts */
pbus = of_match_bus(parent);
pbus->count_cells(dev, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
- pr_debug("Bad cell count for %s\n",
+ pr_err("Bad cell count for %s\n",
of_node_full_name(dev));
- *host = of_node_get(parent);
break;
}
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 3786473..14848d8 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -24,6 +24,23 @@ struct of_pci_range {
#define for_each_of_pci_range(parser, range) \
for (; of_pci_range_parser_one(parser, range);)
+#ifndef indirect_io_enabled
+#define indirect_io_enabled indirect_io_enabled
+static inline bool indirect_io_enabled(void)
+{
+ return false;
+}
+#endif
+
+#ifndef addr_is_indirect_io
+#define addr_is_indirect_io addr_is_indirect_io
+static inline int addr_is_indirect_io(u64 taddr)
+{
+ return 0;
+}
+#endif
+
+
/* Translate a DMA address from device space to CPU space */
extern u64 of_translate_dma_address(struct device_node *dev,
const __be32 *in_addr);
--
2.7.4
^ permalink raw reply related
* Re: [PATCH 6/10] mmc: sdhci-xenon: Add Marvell Xenon SDHC core functionality
From: Ziji Hu @ 2016-11-25 8:45 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter
Cc: Gregory CLEMENT, linux-mmc@vger.kernel.org, Jason Cooper,
Andrew Lunn, Sebastian Hesselbarth, Rob Herring,
devicetree@vger.kernel.org, Thomas Petazzoni,
linux-arm-kernel@lists.infradead.org, Jimmy Xu, Jisheng Zhang,
Nadav Haklai, Ryan Gao, Doug Jones, Victor Gu, Wei(SOCP) Liu,
Wilson Ding, Romain Perier
In-Reply-To: <76ce72f8-4b86-3b83-544f-b9a7ef871393@marvell.com>
Hi Ulf,
On 2016/11/24 23:00, Ziji Hu wrote:
> Hi Ulf,
>
> On 2016/11/24 21:34, Ulf Hansson wrote:
<snip>
>>>>> +
>>>>> + /*
>>>>> + * Xenon Specific property:
>>>>> + * emmc: explicitly indicate whether this slot is for eMMC
>>>>> + * slotno: the index of slot. Refer to SDHC_SYS_CFG_INFO register
>>>>> + * tun-count: the interval between re-tuning
>>>>> + * PHY type: "sdhc phy", "emmc phy 5.0" or "emmc phy 5.1"
>>>>> + */
>>>>> + if (of_property_read_bool(np, "marvell,xenon-emmc"))
>>>>> + priv->emmc_slot = true;
>>>>
>>>> So, you need this because of the eMMC voltage switch behaviour, right?
>>>>
>>>> Then I would rather like to describe this a generic DT bindings for
>>>> the eMMC voltage level support. There have acutally been some earlier
>>>> discussions for this, but we haven't yet made some changes.
>>>>
>>>> I think what is missing is a mmc-ddr-3_3v DT binding, which when set,
>>>> allows the host driver to accept I/O voltage switches to 3.3V. If not
>>>> supported the ->start_signal_voltage_switch() ops may return -EINVAL.
>>>> This would inform the mmc core to move on to the next supported
>>>> voltage level. There might be some minor additional changes to the mmc
>>>> card initialization sequence, but those should be simple.
>>>>
>>>> I can help out to look into this, unless you want to do it yourself of course!?
>>>>
>>> Yes. One of the reasons is to provide eMMC specific voltage setting.
>>> But in my very own opinion, it should be irrelevant to voltage level.
>>> The eMMC voltage setting on our SDHC is different from SD/SDIO voltage switch.
>>> It will become more complex with different SOC implementation details.
>>
>> Got it. Although I think we can cope with that fine just by using the
>> different SD/eMMC speed modes settings defined in DT (or from the
>> SDHCI caps register)
>>
> In my very opinion, I'm not sure if there is any corner case that driver cannot
> determine the eMMC card type from DT and SDHC caps.
>
>>> Unfortunately, MMC driver cannot determine the card type yet when eMMC voltage
>>> setting should be executed.
>>> Thus an flag is required here to tell driver to execute eMMC voltage setting.
>>>
>>> Besides, additional eMMC specific settings might be implemented in future, besides
>>> voltage setting. Most of them should be completed before MMC driver recognizes the
>>> card type. Thus I have to keep this flag to indicate current SDHC is for eMMC.
>>
>> I doubt you will need a generic "eMMC" flag, but let's see when we go forward.
>>
>> Currently it's clear you don't need such a flag, so I will submit a
>> change adding a DT binding for "mmc-ddr-3_3v" then we can take it from
>> there, to see if it suits your needs.
>>
Another reason for a special "xenon-emmc" property is that our host IP usually can
support both eMMC and SD. Whether a host is used as eMMC or SD depends on the
final implementation of the actual product.
Thus our host driver needs to know whether current SDHC is fixed as eMMC or SD.
So far, It can only get the information from DT.
After out host driver get the card type information from DT, it can prepare eMMC
specific voltage, set eMMC specific mmc->caps/caps2 flags and do other
vendor specific init, before card init procedure.
Otherwise, our host driver has to wait until card type is determined in mmc_rescan().
A generic "eMMC" flag is unnecessary. I just require a private property,
which is only used in our host driver and DT.
Thank you.
Best regards,
Hu Ziji
>
> Actually, our eMMC is usually fixed as 1.8V.
>
> The pair "no-sd" + "no-sdio" can provide the similar information.
> But I'm not sure if it is proper to use those two property in such a way.
>
> Thank you.
>
> Best regards
> Hu Ziji
>
>> [...]
>>
>> Kind regards
>> Uffe
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH V3 2/4] mfd: pv88080: MFD core support
From: Lee Jones @ 2016-11-25 8:34 UTC (permalink / raw)
To: Eric Hyeung Dong Jeong
Cc: LINUX-KERNEL, Alexandre Courbot, DEVICETREE, LINUX-GPIO,
Liam Girdwood, Linus Walleij, Mark Brown, Mark Rutland,
Rob Herring, Support Opensource
In-Reply-To: <8FA4409277D6E54680546B8811D8541002A088125C@NB-EX-MBX01.diasemi.com>
On Fri, 25 Nov 2016, Eric Hyeung Dong Jeong wrote:
> On Monday, November 21, 2016 10:09 PM, Lee Jones Wrote:
> > On Fri, 18 Nov 2016, Eric Jeong wrote:
> >
> > >
> > > From: Eric Jeong <eric.jeong.opensource@diasemi.com>
> > >
> > > This patch adds supports for PV88080 MFD core device.
> > >
> > > It provides communication through the I2C interface.
> > > It contains the following components:
> > > - Regulators
> > > - Configurable GPIOs
> > >
> > > Kconfig and Makefile are updated to reflect support for PV88080 PMIC.
> > >
> > > Signed-off-by: Eric Jeong <eric.jeong.opensource@diasemi.com>
> > >
> > > ---
> > > This patch applies against linux-next and next-20161117
> > >
> > > Hi,
> > >
> > > This patch adds MFD core driver for PV88080 PMIC.
> > > This is done as part of the existing PV88080 regulator driver by
> > > expending the driver for GPIO function support.
> > >
> > > Change since PATCH V2
> > > - Make one file insted of usging core and i2c file
> > > - Use devm_ function to be managed resource automatically
> > > - Separated mfd_cell and regmap_irq_chip declaration for clarification.
> > > - Updated Kconfig to use OF and assign yes to I2C
> > >
> > > Change since PATCH V1
> > > - Patch separated from PATCH V1
> > >
> > > Regards,
> > > Eric Jeong, Dialog Semiconductor Ltd.
> > >
> > >
> > > drivers/mfd/Kconfig | 12 ++
> > > drivers/mfd/Makefile | 1 +
> > > drivers/mfd/pv88080.c | 331 +++++++++++++++++++++++++++++++++++++++++++
> > > include/linux/mfd/pv88080.h | 222 +++++++++++++++++++++++++++++
> > > 4 files changed, 566 insertions(+)
> > > create mode 100644 drivers/mfd/pv88080.c create mode 100644
> > > include/linux/mfd/pv88080.h
It's a good idea to cut out all of the code/comments that is either
not relevant, or you are not providing comment (besides "will do")
on.
> > > +struct pv88080 {
> > > + struct device *dev;
> > > + struct regmap *regmap;
> > > + unsigned long type;
> >
> > Does this really need to be in here?
>
> The *type* member is used for separating silicon type.
> And, regulator and gpio driver also use the member to check the type
> for proper configuration without additional code.
> That is the reason that the member is added in the structure.
I don't see how this is being used, so assuming the other Maintainers
are happy with the implementation it's okay for this to live here.
However, please consider changing to something better than "value" or
"type". Perhaps "varian"t or "model" or similar would be better?
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH] v4l: async: make v4l2 coexists with devicetree nodes in a dt overlay
From: Sakari Ailus @ 2016-11-25 8:21 UTC (permalink / raw)
To: Javi Merino
Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Pantelis Antoniou,
Mauro Carvalho Chehab, Javier Martinez Canillas, Sakari Ailus,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw
In-Reply-To: <20161123161511.GB1753@ct-lt-587>
Hi Javi,
On Wed, Nov 23, 2016 at 04:15:11PM +0000, Javi Merino wrote:
> On Wed, Nov 23, 2016 at 05:10:42PM +0200, Sakari Ailus wrote:
> > Hi Javi,
>
> Hi Sakari,
>
> > On Wed, Nov 23, 2016 at 10:09:57AM +0000, Javi Merino wrote:
> > > In asd's configured with V4L2_ASYNC_MATCH_OF, if the v4l2 subdev is in
> > > a devicetree overlay, its of_node pointer will be different each time
> > > the overlay is applied. We are not interested in matching the
> > > pointer, what we want to match is that the path is the one we are
> > > expecting. Change to use of_node_cmp() so that we continue matching
> > > after the overlay has been removed and reapplied.
> > >
> > > Cc: Mauro Carvalho Chehab <mchehab-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > > Cc: Javier Martinez Canillas <javier-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
> > > Cc: Sakari Ailus <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> > > Signed-off-by: Javi Merino <javi.merino-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > > ---
> > > Hi,
> > >
> > > I feel it is a bit of a hack, but I couldn't think of anything better.
> > > I'm ccing devicetree@ and Pantelis because there may be a simpler
> > > solution.
> >
> > First I have to admit that I'm not an expert when it comes to DT overlays.
> >
> > That said, my understanding is that the sub-device and the async sub-device
> > are supposed to point to the exactly same DT node. I wonder if there's
> > actually anything wrong in the current code.
> >
> > If the overlay has changed between probing the driver for the async notifier
> > and the async sub-device, there should be no match here, should there? The
> > two nodes actually point to a node in a different overlay in that case.
>
> Overlays are parts of the devicetree that can be added and removed.
> When the overlay is applied, the camera driver is probed and does
> v4l2_async_register_subdev(). However, v4l2_async_belongs() fails.
> The problem is with comparing pointers. I haven't looked at the
> implementation of overlays in detail, but what I see is that the
> of_node pointer changes when you remove and reapply an overlay (I
> guess it's dynamically allocated and when you remove the overlay, it's
> freed).
The concern here which we were discussing was whether the overlay should be
relied on having compliant configuration compared to the part which was not
part of the overlay.
As external components are involved, quite possibly also the ISP DT node
will require changes, not just the image source (TV tuner, camera sensor
etc.). This could be because of number of CSI-2 lanes or parallel bus width,
for instance.
I'd also be interested in having an actual driver implement support for
removing and adding a DT overlay first so we'd see how this would actually
work. We need both in order to be able to actually remove and add DT
overlays _without_ unbinding the ISP driver. Otherwise it should already
work in the current codebase.
--
Kind regards,
Sakari Ailus
e-mail: sakari.ailus-X3B1VOXEql0@public.gmane.org XMPP: sailus-PCDdDYkjdNMDXYZnReoRVg@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/2] mmc: core: add DT binding for CMD23
From: Ulf Hansson @ 2016-11-25 8:19 UTC (permalink / raw)
To: Jaehoon Chung
Cc: linux-mmc@vger.kernel.org, devicetree@vger.kernel.org,
Rob Herring, Mark Rutland, Adrian Hunter
In-Reply-To: <20161125065215.10833-1-jh80.chung@samsung.com>
On 25 November 2016 at 07:52, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> Provide the option to configure one type of multiple block read/wrte
> transatction (CMD23 - it's optional.)
>
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> ---
> drivers/mmc/core/host.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
> index 98f25ff..9bdc369 100644
> --- a/drivers/mmc/core/host.c
> +++ b/drivers/mmc/core/host.c
> @@ -321,6 +321,8 @@ int mmc_of_parse(struct mmc_host *host)
> host->caps2 |= MMC_CAP2_NO_SD;
> if (of_property_read_bool(np, "no-mmc"))
> host->caps2 |= MMC_CAP2_NO_MMC;
> + if (of_property_read_bool(np, "cap-mmc-cmd23"))
> + host->caps |= MMC_CAP_CMD23;
>
> host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
> if (host->dsr_req && (host->dsr & ~0xffff)) {
> --
> 2.10.1
>
I don't think this as HW configuration, but more a SW configuration.
Thus we don't need a DT binding for it, right?
Kind regards
Uffe
^ permalink raw reply
* Re: [PATCH v3 2/6] iio: adc: Add support for STM32 ADC core
From: Fabrice Gasnier @ 2016-11-25 8:09 UTC (permalink / raw)
To: Jonathan Cameron, linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-I+IVW8TIWO2tmTQ+vhA3Yw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg
In-Reply-To: <cd958ae5-90e4-b6c5-8a23-84c49011e8e3-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On 11/24/2016 09:40 PM, Jonathan Cameron wrote:
> On 21/11/16 08:54, Fabrice Gasnier wrote:
>> On 11/19/2016 01:17 PM, Jonathan Cameron wrote:
>>> On 15/11/16 15:30, Fabrice Gasnier wrote:
>>>> Add core driver for STMicroelectronics STM32 ADC (Analog to Digital
>>>> Converter). STM32 ADC can be composed of up to 3 ADCs with shared
>>>> resources like clock prescaler, common interrupt line and analog
>>>> reference voltage.
>>>> This core driver basically manages shared resources.
>>>>
>>>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
>>> There is nothing in here that demands selecting a fixed regulator.
>>> I've also switched the select regulator over to depends on inline with
>>> other drivers in IIO that have a hard dependency on regulators.
>>> Other than that which showed up during build tests, looks good to me.
>>> Shout if I've broken anything with this change.
>> Hi Jonathan, All,
>>
>> First many thanks.
>> This is not a big deal. Only thing is: I think patch 4 of this series (on stm32_defconfig) need to be updated
>> to accommodate this change. E.g. :
>> +CONFIG_REGULATOR=y
>> +CONFIG_REGULATOR_FIXED_VOLTAGE=y
>>
>> Shall I send a new version of this series (all patches), including your changes, with updated defconfig as well ?
>> Or only updated patch on defconfig is enough ?
> Just update those that haven't already been applied.
Hi,
I'll update these only.
Thanks,
Fabrice
>
> Thanks,
>
> Jonathan
>> Please advise,
>> Fabrice
>>> Applied to the togreg branch of iio.git and pushed out as testing for
>>> the autobuilders to play with it.
>>>
>>> Thanks,
>>>
>>> Jonathan
>>>> ---
>>>> drivers/iio/adc/Kconfig | 13 ++
>>>> drivers/iio/adc/Makefile | 1 +
>>>> drivers/iio/adc/stm32-adc-core.c | 303 +++++++++++++++++++++++++++++++++++++++
>>>> drivers/iio/adc/stm32-adc-core.h | 52 +++++++
>>>> 4 files changed, 369 insertions(+)
>>>> create mode 100644 drivers/iio/adc/stm32-adc-core.c
>>>> create mode 100644 drivers/iio/adc/stm32-adc-core.h
>>>>
>>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>>> index 7edcf32..ff30239 100644
>>>> --- a/drivers/iio/adc/Kconfig
>>>> +++ b/drivers/iio/adc/Kconfig
>>>> @@ -419,6 +419,19 @@ config ROCKCHIP_SARADC
>>>> To compile this driver as a module, choose M here: the
>>>> module will be called rockchip_saradc.
>>>> +config STM32_ADC_CORE
>>>> + tristate "STMicroelectronics STM32 adc core"
>>>> + depends on ARCH_STM32 || COMPILE_TEST
>>>> + depends on OF
>>>> + select REGULATOR
>>>> + select REGULATOR_FIXED_VOLTAGE
>>>> + help
>>>> + Select this option to enable the core driver for STMicroelectronics
>>>> + STM32 analog-to-digital converter (ADC).
>>>> +
>>>> + This driver can also be built as a module. If so, the module
>>>> + will be called stm32-adc-core.
>>>> +
>>>> config STX104
>>>> tristate "Apex Embedded Systems STX104 driver"
>>>> depends on X86 && ISA_BUS_API
>>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>>> index 7a40c04..a1e8f44 100644
>>>> --- a/drivers/iio/adc/Makefile
>>>> +++ b/drivers/iio/adc/Makefile
>>>> @@ -41,6 +41,7 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
>>>> obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
>>>> obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>>>> obj-$(CONFIG_STX104) += stx104.o
>>>> +obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>>>> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>>> obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>>>> obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
>>>> diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
>>>> new file mode 100644
>>>> index 0000000..4214b0c
>>>> --- /dev/null
>>>> +++ b/drivers/iio/adc/stm32-adc-core.c
>>>> @@ -0,0 +1,303 @@
>>>> +/*
>>>> + * This file is part of STM32 ADC driver
>>>> + *
>>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>>> + * Author: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>.
>>>> + *
>>>> + * Inspired from: fsl-imx25-tsadc
>>>> + *
>>>> + * License type: GPLv2
>>>> + *
>>>> + * 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 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, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include <linux/clk.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/irqchip/chained_irq.h>
>>>> +#include <linux/irqdesc.h>
>>>> +#include <linux/irqdomain.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of_device.h>
>>>> +#include <linux/regulator/consumer.h>
>>>> +#include <linux/slab.h>
>>>> +
>>>> +#include "stm32-adc-core.h"
>>>> +
>>>> +/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
>>>> +#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
>>>> +#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
>>>> +
>>>> +/* STM32F4_ADC_CSR - bit fields */
>>>> +#define STM32F4_EOC3 BIT(17)
>>>> +#define STM32F4_EOC2 BIT(9)
>>>> +#define STM32F4_EOC1 BIT(1)
>>>> +
>>>> +/* STM32F4_ADC_CCR - bit fields */
>>>> +#define STM32F4_ADC_ADCPRE_SHIFT 16
>>>> +#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
>>>> +
>>>> +/* STM32 F4 maximum analog clock rate (from datasheet) */
>>>> +#define STM32F4_ADC_MAX_CLK_RATE 36000000
>>>> +
>>>> +/**
>>>> + * struct stm32_adc_priv - stm32 ADC core private data
>>>> + * @irq: irq for ADC block
>>>> + * @domain: irq domain reference
>>>> + * @aclk: clock reference for the analog circuitry
>>>> + * @vref: regulator reference
>>>> + * @common: common data for all ADC instances
>>>> + */
>>>> +struct stm32_adc_priv {
>>>> + int irq;
>>>> + struct irq_domain *domain;
>>>> + struct clk *aclk;
>>>> + struct regulator *vref;
>>>> + struct stm32_adc_common common;
>>>> +};
>>>> +
>>>> +static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
>>>> +{
>>>> + return container_of(com, struct stm32_adc_priv, common);
>>>> +}
>>>> +
>>>> +/* STM32F4 ADC internal common clock prescaler division ratios */
>>>> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
>>>> +
>>>> +/**
>>>> + * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
>>>> + * @priv: stm32 ADC core private data
>>>> + * Select clock prescaler used for analog conversions, before using ADC.
>>>> + */
>>>> +static int stm32f4_adc_clk_sel(struct platform_device *pdev,
>>>> + struct stm32_adc_priv *priv)
>>>> +{
>>>> + unsigned long rate;
>>>> + u32 val;
>>>> + int i;
>>>> +
>>>> + rate = clk_get_rate(priv->aclk);
>>>> + for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
>>>> + if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
>>>> + break;
>>>> + }
>>>> + if (i >= ARRAY_SIZE(stm32f4_pclk_div))
>>>> + return -EINVAL;
>>>> +
>>>> + val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
>>>> + val &= ~STM32F4_ADC_ADCPRE_MASK;
>>>> + val |= i << STM32F4_ADC_ADCPRE_SHIFT;
>>>> + writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
>>>> +
>>>> + dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
>>>> + rate / (stm32f4_pclk_div[i] * 1000));
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/* ADC common interrupt for all instances */
>>>> +static void stm32_adc_irq_handler(struct irq_desc *desc)
>>>> +{
>>>> + struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
>>>> + struct irq_chip *chip = irq_desc_get_chip(desc);
>>>> + u32 status;
>>>> +
>>>> + chained_irq_enter(chip, desc);
>>>> + status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
>>>> +
>>>> + if (status & STM32F4_EOC1)
>>>> + generic_handle_irq(irq_find_mapping(priv->domain, 0));
>>>> +
>>>> + if (status & STM32F4_EOC2)
>>>> + generic_handle_irq(irq_find_mapping(priv->domain, 1));
>>>> +
>>>> + if (status & STM32F4_EOC3)
>>>> + generic_handle_irq(irq_find_mapping(priv->domain, 2));
>>>> +
>>>> + chained_irq_exit(chip, desc);
>>>> +};
>>>> +
>>>> +static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
>>>> + irq_hw_number_t hwirq)
>>>> +{
>>>> + irq_set_chip_data(irq, d->host_data);
>>>> + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
>>>> +{
>>>> + irq_set_chip_and_handler(irq, NULL, NULL);
>>>> + irq_set_chip_data(irq, NULL);
>>>> +}
>>>> +
>>>> +static const struct irq_domain_ops stm32_adc_domain_ops = {
>>>> + .map = stm32_adc_domain_map,
>>>> + .unmap = stm32_adc_domain_unmap,
>>>> + .xlate = irq_domain_xlate_onecell,
>>>> +};
>>>> +
>>>> +static int stm32_adc_irq_probe(struct platform_device *pdev,
>>>> + struct stm32_adc_priv *priv)
>>>> +{
>>>> + struct device_node *np = pdev->dev.of_node;
>>>> +
>>>> + priv->irq = platform_get_irq(pdev, 0);
>>>> + if (priv->irq < 0) {
>>>> + dev_err(&pdev->dev, "failed to get irq\n");
>>>> + return priv->irq;
>>>> + }
>>>> +
>>>> + priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
>>>> + &stm32_adc_domain_ops,
>>>> + priv);
>>>> + if (!priv->domain) {
>>>> + dev_err(&pdev->dev, "Failed to add irq domain\n");
>>>> + return -ENOMEM;
>>>> + }
>>>> +
>>>> + irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
>>>> + irq_set_handler_data(priv->irq, priv);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static void stm32_adc_irq_remove(struct platform_device *pdev,
>>>> + struct stm32_adc_priv *priv)
>>>> +{
>>>> + int hwirq;
>>>> +
>>>> + for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
>>>> + irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
>>>> + irq_domain_remove(priv->domain);
>>>> + irq_set_chained_handler(priv->irq, NULL);
>>>> +}
>>>> +
>>>> +static int stm32_adc_probe(struct platform_device *pdev)
>>>> +{
>>>> + struct stm32_adc_priv *priv;
>>>> + struct device_node *np = pdev->dev.of_node;
>>>> + struct resource *res;
>>>> + int ret;
>>>> +
>>>> + if (!pdev->dev.of_node)
>>>> + return -ENODEV;
>>>> +
>>>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>>>> + if (!priv)
>>>> + return -ENOMEM;
>>>> +
>>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>> + priv->common.base = devm_ioremap_resource(&pdev->dev, res);
>>>> + if (IS_ERR(priv->common.base))
>>>> + return PTR_ERR(priv->common.base);
>>>> +
>>>> + priv->vref = devm_regulator_get(&pdev->dev, "vref");
>>>> + if (IS_ERR(priv->vref)) {
>>>> + ret = PTR_ERR(priv->vref);
>>>> + dev_err(&pdev->dev, "vref get failed, %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = regulator_enable(priv->vref);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "vref enable failed\n");
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = regulator_get_voltage(priv->vref);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
>>>> + goto err_regulator_disable;
>>>> + }
>>>> + priv->common.vref_mv = ret / 1000;
>>>> + dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
>>>> +
>>>> + priv->aclk = devm_clk_get(&pdev->dev, "adc");
>>>> + if (IS_ERR(priv->aclk)) {
>>>> + ret = PTR_ERR(priv->aclk);
>>>> + dev_err(&pdev->dev, "Can't get 'adc' clock\n");
>>>> + goto err_regulator_disable;
>>>> + }
>>>> +
>>>> + ret = clk_prepare_enable(priv->aclk);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "adc clk enable failed\n");
>>>> + goto err_regulator_disable;
>>>> + }
>>>> +
>>>> + ret = stm32f4_adc_clk_sel(pdev, priv);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "adc clk selection failed\n");
>>>> + goto err_clk_disable;
>>>> + }
>>>> +
>>>> + ret = stm32_adc_irq_probe(pdev, priv);
>>>> + if (ret < 0)
>>>> + goto err_clk_disable;
>>>> +
>>>> + platform_set_drvdata(pdev, &priv->common);
>>>> +
>>>> + ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "failed to populate DT children\n");
>>>> + goto err_irq_remove;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +
>>>> +err_irq_remove:
>>>> + stm32_adc_irq_remove(pdev, priv);
>>>> +
>>>> +err_clk_disable:
>>>> + clk_disable_unprepare(priv->aclk);
>>>> +
>>>> +err_regulator_disable:
>>>> + regulator_disable(priv->vref);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int stm32_adc_remove(struct platform_device *pdev)
>>>> +{
>>>> + struct stm32_adc_common *common = platform_get_drvdata(pdev);
>>>> + struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
>>>> +
>>>> + of_platform_depopulate(&pdev->dev);
>>>> + stm32_adc_irq_remove(pdev, priv);
>>>> + clk_disable_unprepare(priv->aclk);
>>>> + regulator_disable(priv->vref);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static const struct of_device_id stm32_adc_of_match[] = {
>>>> + { .compatible = "st,stm32f4-adc-core" },
>>>> + {},
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
>>>> +
>>>> +static struct platform_driver stm32_adc_driver = {
>>>> + .probe = stm32_adc_probe,
>>>> + .remove = stm32_adc_remove,
>>>> + .driver = {
>>>> + .name = "stm32-adc-core",
>>>> + .of_match_table = stm32_adc_of_match,
>>>> + },
>>>> +};
>>>> +module_platform_driver(stm32_adc_driver);
>>>> +
>>>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>");
>>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC core driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> +MODULE_ALIAS("platform:stm32-adc-core");
>>>> diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
>>>> new file mode 100644
>>>> index 0000000..081fa5f
>>>> --- /dev/null
>>>> +++ b/drivers/iio/adc/stm32-adc-core.h
>>>> @@ -0,0 +1,52 @@
>>>> +/*
>>>> + * This file is part of STM32 ADC driver
>>>> + *
>>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>>> + * Author: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>.
>>>> + *
>>>> + * License type: GPLv2
>>>> + *
>>>> + * 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 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, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#ifndef __STM32_ADC_H
>>>> +#define __STM32_ADC_H
>>>> +
>>>> +/*
>>>> + * STM32 - ADC global register map
>>>> + * ________________________________________________________
>>>> + * | Offset | Register |
>>>> + * --------------------------------------------------------
>>>> + * | 0x000 | Master ADC1 |
>>>> + * --------------------------------------------------------
>>>> + * | 0x100 | Slave ADC2 |
>>>> + * --------------------------------------------------------
>>>> + * | 0x200 | Slave ADC3 |
>>>> + * --------------------------------------------------------
>>>> + * | 0x300 | Master & Slave common regs |
>>>> + * --------------------------------------------------------
>>>> + */
>>>> +#define STM32_ADC_MAX_ADCS 3
>>>> +#define STM32_ADCX_COMN_OFFSET 0x300
>>>> +
>>>> +/**
>>>> + * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
>>>> + * @base: control registers base cpu addr
>>>> + * @vref_mv: vref voltage (mv)
>>>> + */
>>>> +struct stm32_adc_common {
>>>> + void __iomem *base;
>>>> + int vref_mv;
>>>> +};
>>>> +
>>>> +#endif
>>>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH mmc/next] mmc: sh_mmcif: Document r8a73a4, r8a7779 and sh73a0 DT bindings
From: Simon Horman @ 2016-11-25 7:56 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Ulf Hansson, linux-mmc, devicetree, Magnus Damm,
linux-renesas-soc
In-Reply-To: <e0e1d92b-1b79-ec53-def0-25a080edc073@cogentembedded.com>
On Thu, Nov 24, 2016 at 09:50:38PM +0300, Sergei Shtylyov wrote:
> Hello.
>
> On 11/24/2016 09:17 PM, Simon Horman wrote:
>
> >Simply document new compatibility strings as the driver is already
> >activated using a fallback compatibility string.
> >
> >These compat strings are in keeping with those for all other
> >Renesas ARM based SoCs with sh_mmcif enabled in mainline.
> >
> >Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
> >---
> >I plan to follow-up with patches to use these new compat strings
> >to bring the DT files of the SoCs in question in-line with those
> >for other Renesas ARM based SoCs with sh_mmcif enabled in mainline.
> >---
> > Documentation/devicetree/bindings/mmc/renesas,mmcif.txt | 3 +++
> > 1 file changed, 3 insertions(+)
> >
> >diff --git a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
> >index ff611fa66871..e4ba92aa035e 100644
> >--- a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
> >+++ b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
> >@@ -8,11 +8,14 @@ Required properties:
> >
> > - compatible: should be "renesas,mmcif-<soctype>", "renesas,sh-mmcif" as a
> > fallback. Examples with <soctype> are:
> >+ - "renesas,mmcif-r8a73a4" for the MMCIF found in r8a73a4 SoCs
> > - "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs
> >+ - "renesas,mmcif-r8a7778" for the MMCIF found in r8a7778 SoCs
>
> 7779 in the subject, 7778 here.
Thanks, I have reposted with an updated subject.
^ permalink raw reply
* [PATCH mmc/next] mmc: sh_mmcif: Document r8a73a4, r8a7778 and sh73a0 DT bindings
From: Simon Horman @ 2016-11-25 7:56 UTC (permalink / raw)
To: Ulf Hansson
Cc: linux-mmc-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Magnus Damm,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA, Simon Horman
Simply document new compatibility strings as the driver is already
activated using a fallback compatibility string.
These compat strings are in keeping with those for all other
Renesas ARM based SoCs with sh_mmcif enabled in mainline.
Signed-off-by: Simon Horman <horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
---
Reposted with r8a7778 instead of r8a7779 in subject
I have also posted patches to use these new compat strings
to bring the DT files of the SoCs in question in-line with those
for other Renesas ARM based SoCs with sh_mmcif enabled in mainline.
---
Documentation/devicetree/bindings/mmc/renesas,mmcif.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
index ff611fa66871..e4ba92aa035e 100644
--- a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
+++ b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
@@ -8,11 +8,14 @@ Required properties:
- compatible: should be "renesas,mmcif-<soctype>", "renesas,sh-mmcif" as a
fallback. Examples with <soctype> are:
+ - "renesas,mmcif-r8a73a4" for the MMCIF found in r8a73a4 SoCs
- "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs
+ - "renesas,mmcif-r8a7778" for the MMCIF found in r8a7778 SoCs
- "renesas,mmcif-r8a7790" for the MMCIF found in r8a7790 SoCs
- "renesas,mmcif-r8a7791" for the MMCIF found in r8a7791 SoCs
- "renesas,mmcif-r8a7793" for the MMCIF found in r8a7793 SoCs
- "renesas,mmcif-r8a7794" for the MMCIF found in r8a7794 SoCs
+ - "renesas,mmcif-sh73a0" for the MMCIF found in sh73a0 SoCs
- clocks: reference to the functional clock
--
2.7.0.rc3.207.g0ac5344
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH v2 0/3] arm64: dts: r8a7796: Add CAN/CAN FD support
From: Simon Horman @ 2016-11-25 7:48 UTC (permalink / raw)
To: Chris Paterson
Cc: Marc Kleine-Budde, Rob Herring, Mark Rutland, Geert Uytterhoeven,
Ramesh Shanmugasundaram, linux-arm-kernel, devicetree,
linux-renesas-soc
In-Reply-To: <1480004021-4037-1-git-send-email-chris.paterson2@renesas.com>
On Thu, Nov 24, 2016 at 04:13:38PM +0000, Chris Paterson wrote:
> This series adds CAN and CAN FD support to the r8a7796.
>
> Changes since v1:
> - Split device tree changes from bindings documentation.
> - Rebased on renesas-devel-20161123v2-v4.9-rc6.
>
>
> Chris Paterson (3):
> arm64: dts: r8a7796: Add CAN external clock support
> arm64: dts: r8a7796: Add CAN support
> arm64: dts: r8a7796: Add CAN FD support
Thanks Chris,
I have queued these up for v4.11 in the devel branch of the renesas tree.
^ permalink raw reply
* Re: [PATCH v2 0/2] can: Add r8a7796 support to CAN/CAN FD
From: Marc Kleine-Budde @ 2016-11-25 7:19 UTC (permalink / raw)
To: Chris Paterson
Cc: Wolfgang Grandegger, Rob Herring, Mark Rutland,
Geert Uytterhoeven, Simon Horman, Ramesh Shanmugasundaram,
linux-can, netdev, devicetree, linux-renesas-soc
In-Reply-To: <1480003917-3953-1-git-send-email-chris.paterson2@renesas.com>
[-- Attachment #1.1: Type: text/plain, Size: 774 bytes --]
On 11/24/2016 05:11 PM, Chris Paterson wrote:
> This series adds support for r8a7796 to rcar_can and rcar_canfd.
>
> Original series: [PATCH 0/3] arm64: dts: r8a7796: Add CAN/CAN FD support.
>
> Changes since v1:
> - Split bindings documentation changes from device tree changes.
> - Rebased on renesas-devel-20161123v2-v4.9-rc6.
>
>
> Chris Paterson (2):
> can: rcar_can: Add r8a7796 support
> can: rcar_canfd: Add r8a7796 support
Both added to can-next.
Thanks,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v6 3/5] ARM: dts: sun8i-h3: add HDMI video nodes
From: Jean-François Moine @ 2016-11-25 6:57 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Dave Airlie, Maxime Ripard, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org
In-Reply-To: <6896101480051640-ddF1QkQ5CrZxpj1cXAZ9Bg@public.gmane.org>
On Fri, 25 Nov 2016 13:27:20 +0800
Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org> wrote:
> > + assigned-clocks = <&ccu CLK_PLL_DE>,
>
> Cannot get the patch built on 4.9-rc, as CLK_PLL_DE is not an exported clock.
Hi,
Sorry, I forgot it. The symbol goes to
include/dt-bindings/clock/sun8i-h3-ccu.h
as:
#define CLK_PLL_DE 13
--
Ken ar c'hentañ | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* [PATCH 2/2] Documetationi: mmc: add cap-mmc-cmd23 proprerty's decription
From: Jaehoon Chung @ 2016-11-25 6:52 UTC (permalink / raw)
To: linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
adrian.hunter-ral2JQCrhuEAvxtiuMwx3w, Jaehoon Chung
In-Reply-To: <20161125065215.10833-1-jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
This patch adds description for cap-mmc-cmd23.
There are two typse of multiple block read/write transaction.
One is Open-ended, other is Pre-defined block count(CMD23).
CMD23 is optional command. So user can choose this type.
Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
Documentation/devicetree/bindings/mmc/mmc.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
index 8a37782..29f60f6 100644
--- a/Documentation/devicetree/bindings/mmc/mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/mmc.txt
@@ -52,6 +52,7 @@ Optional properties:
- no-sdio: controller is limited to send sdio cmd during initialization
- no-sd: controller is limited to send sd cmd during initialization
- no-mmc: controller is limited to send mmc cmd during initialization
+- cap-mmc-cmd23: CMD23(pre-defined block count) is supported
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
polarity properties, we have to fix the meaning of the "normal" and "inverted"
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 1/2] mmc: core: add DT binding for CMD23
From: Jaehoon Chung @ 2016-11-25 6:52 UTC (permalink / raw)
To: linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
adrian.hunter-ral2JQCrhuEAvxtiuMwx3w, Jaehoon Chung
Provide the option to configure one type of multiple block read/wrte
transatction (CMD23 - it's optional.)
Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
drivers/mmc/core/host.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 98f25ff..9bdc369 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -321,6 +321,8 @@ int mmc_of_parse(struct mmc_host *host)
host->caps2 |= MMC_CAP2_NO_SD;
if (of_property_read_bool(np, "no-mmc"))
host->caps2 |= MMC_CAP2_NO_MMC;
+ if (of_property_read_bool(np, "cap-mmc-cmd23"))
+ host->caps |= MMC_CAP_CMD23;
host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
if (host->dsr_req && (host->dsr & ~0xffff)) {
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* RE: [PATCH V3 2/4] mfd: pv88080: MFD core support
From: Eric Hyeung Dong Jeong @ 2016-11-25 6:03 UTC (permalink / raw)
To: Lee Jones, Eric Hyeung Dong Jeong
Cc: LINUX-KERNEL, Alexandre Courbot, DEVICETREE, LINUX-GPIO,
Liam Girdwood, Linus Walleij, Mark Brown, Mark Rutland,
Rob Herring, Support Opensource
In-Reply-To: <20161121130919.GB3917@dell>
On Monday, November 21, 2016 10:09 PM, Lee Jones Wrote:
>
> On Fri, 18 Nov 2016, Eric Jeong wrote:
>
> >
> > From: Eric Jeong <eric.jeong.opensource@diasemi.com>
> >
> > This patch adds supports for PV88080 MFD core device.
> >
> > It provides communication through the I2C interface.
> > It contains the following components:
> > - Regulators
> > - Configurable GPIOs
> >
> > Kconfig and Makefile are updated to reflect support for PV88080 PMIC.
> >
> > Signed-off-by: Eric Jeong <eric.jeong.opensource@diasemi.com>
> >
> > ---
> > This patch applies against linux-next and next-20161117
> >
> > Hi,
> >
> > This patch adds MFD core driver for PV88080 PMIC.
> > This is done as part of the existing PV88080 regulator driver by
> > expending the driver for GPIO function support.
> >
> > Change since PATCH V2
> > - Make one file insted of usging core and i2c file
> > - Use devm_ function to be managed resource automatically
> > - Separated mfd_cell and regmap_irq_chip declaration for clarification.
> > - Updated Kconfig to use OF and assign yes to I2C
> >
> > Change since PATCH V1
> > - Patch separated from PATCH V1
> >
> > Regards,
> > Eric Jeong, Dialog Semiconductor Ltd.
> >
> >
> > drivers/mfd/Kconfig | 12 ++
> > drivers/mfd/Makefile | 1 +
> > drivers/mfd/pv88080.c | 331 +++++++++++++++++++++++++++++++++++++++++++
> > include/linux/mfd/pv88080.h | 222 +++++++++++++++++++++++++++++
> > 4 files changed, 566 insertions(+)
> > create mode 100644 drivers/mfd/pv88080.c create mode 100644
> > include/linux/mfd/pv88080.h
> >
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index
> > 06dc9b0..75abf2d 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -792,6 +792,18 @@ config MFD_PM8921_CORE
> > Say M here if you want to include support for PM8921 chip as a module.
> > This will build a module called "pm8921-core".
> >
> > +config MFD_PV88080
> > + tristate "Powerventure Semiconductor PV88080 PMIC Support"
> > + select MFD_CORE
> > + select REGMAP_I2C
> > + select REGMAP_IRQ
> > + depends on I2C=y && OF
> > + help
> > + Say yes here for support for the Powerventure Semiconductor PV88080 PMIC.
> > + This includes the I2C driver and core APIs.
> > + Additional drivers must be enabled in order to use the functionality
> > + of the device.
> > +
> > config MFD_QCOM_RPM
> > tristate "Qualcomm Resource Power Manager (RPM)"
> > depends on ARCH_QCOM && OF
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index
> > db39377..e9e16c6 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -173,6 +173,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
> > obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
> > obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
> > obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
> > +obj-$(CONFIG_MFD_PV88080) += pv88080.o
> > obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o
> > obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
> > obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
> > diff --git a/drivers/mfd/pv88080.c b/drivers/mfd/pv88080.c new file
> > mode 100644 index 0000000..518b44f
> > --- /dev/null
> > +++ b/drivers/mfd/pv88080.c
> > @@ -0,0 +1,331 @@
> > +/*
> > + * pv88080-i2c.c - I2C access driver for PV88080
>
> Remove the filename.
>
> They have a habit of becoming out of date (like now).
OK, I will do that.
>
> > + * Copyright (C) 2016 Powerventure Semiconductor Ltd.
> > + *
> > + * 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.
> > + */
> > +
> > +#include <linux/i2c.h>
> > +#include <linux/module.h>
> > +#include <linux/regmap.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/mfd/core.h>
> > +#include <linux/module.h>
>
> Alphabetical.
OK, I see.
>
> > +#include <linux/mfd/pv88080.h>
>
> This doesn't need to be separated from the rest.
OK.
>
> > +#define PV88080_REG_EVENT_A_OFFSET 0
> > +#define PV88080_REG_EVENT_B_OFFSET 1
> > +#define PV88080_REG_EVENT_C_OFFSET 2
>
> Spaces after 'define'.
OK I will do that.
>
> > +static const struct resource regulators_aa_resources[] = {
> > + {
> > + .name = "VDD_TEMP_FAULT",
> > + .start = PV88080_AA_IRQ_VDD_FLT,
> > + .end = PV88080_AA_IRQ_OVER_TEMP,
> > + .flags = IORESOURCE_IRQ,
> > + },
> > +};
> > +
> > +static const struct resource regulators_ba_resources[] = {
> > + {
> > + .name = "VDD_TEMP_FAULT",
> > + .start = PV88080_BA_IRQ_VDD_FLT,
> > + .end = PV88080_BA_IRQ_OVER_TEMP,
> > + .flags = IORESOURCE_IRQ,
> > + },
> > +};
>
> Use the DEFINE_RES_* macros.
I will use the macros.
>
> > +static const struct mfd_cell pv88080_aa_cells[] = {
> > + {
> > + .name = "pv88080-regulator",
> > + .num_resources = ARRAY_SIZE(regulators_aa_resources),
> > + .resources = regulators_aa_resources,
> > + .of_compatible = "pvs,pv88080-regulator",
> > + },
> > + {
> > + .name = "pv88080-gpio",
> > + .of_compatible = "pvs,pv88080-gpio",
> > + },
> > +};
> > +
> > +static const struct mfd_cell pv88080_ba_cells[] = {
> > + {
> > + .name = "pv88080-regulator",
> > + .num_resources = ARRAY_SIZE(regulators_ba_resources),
> > + .resources = regulators_ba_resources,
> > + .of_compatible = "pvs,pv88080-regulator",
> > + },
> > + {
> > + .name = "pv88080-gpio",
> > + .of_compatible = "pvs,pv88080-gpio",
> > + },
> > +};
> > +
> > +static const struct regmap_irq pv88080_aa_irqs[] = {
> > + /* PV88080 event A register for AA/AB silicon */
> > + [PV88080_AA_IRQ_VDD_FLT] = {
> > + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> > + .mask = PV88080_M_VDD_FLT,
> > + },
> > + [PV88080_AA_IRQ_OVER_TEMP] = {
> > + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> > + .mask = PV88080_M_OVER_TEMP,
> > + },
> > + [PV88080_AA_IRQ_SEQ_RDY] = {
> > + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> > + .mask = PV88080_M_SEQ_RDY,
> > + },
> > + /* PV88080 event B register for AA/AB silicon */
> > + [PV88080_AA_IRQ_HVBUCK_OV] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_HVBUCK_OV,
> > + },
> > + [PV88080_AA_IRQ_HVBUCK_UV] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_HVBUCK_UV,
> > + },
> > + [PV88080_AA_IRQ_HVBUCK_SCP] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_HVBUCK_SCP,
> > + },
> > + [PV88080_AA_IRQ_BUCK1_SCP] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_BUCK1_SCP,
> > + },
> > + [PV88080_AA_IRQ_BUCK2_SCP] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_BUCK2_SCP,
> > + },
> > + [PV88080_AA_IRQ_BUCK3_SCP] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_BUCK3_SCP,
> > + },
> > + /* PV88080 event C register for AA/AB silicon */
> > + [PV88080_AA_IRQ_GPIO_FLAG0] = {
> > + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> > + .mask = PV88080_M_GPIO_FLAG0,
> > + },
> > + [PV88080_AA_IRQ_GPIO_FLAG1] = {
> > + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> > + .mask = PV88080_M_GPIO_FLAG1,
> > + },
> > +};
> > +
> > +static const struct regmap_irq pv88080_ba_irqs[] = {
> > + /* PV88080 event A register for BA/BB silicon */
> > + [PV88080_BA_IRQ_VDD_FLT] = {
> > + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> > + .mask = PV88080_M_VDD_FLT,
> > + },
> > + [PV88080_BA_IRQ_OVER_TEMP] = {
> > + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> > + .mask = PV88080_M_OVER_TEMP,
> > + },
> > + [PV88080_BA_IRQ_SEQ_RDY] = {
> > + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> > + .mask = PV88080_M_SEQ_RDY,
> > + },
> > + [PV88080_BA_IRQ_EXT_OT] = {
> > + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> > + .mask = PV88080_M_EXT_OT,
> > + },
> > + /* PV88080 event B register for BA/BB silicon */
> > + [PV88080_BA_IRQ_HVBUCK_OV] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_HVBUCK_OV,
> > + },
> > + [PV88080_BA_IRQ_HVBUCK_UV] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_HVBUCK_UV,
> > + },
> > + [PV88080_BA_IRQ_HVBUCK_SCP] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_HVBUCK_SCP,
> > + },
> > + [PV88080_BA_IRQ_BUCK1_SCP] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_BUCK1_SCP,
> > + },
> > + [PV88080_BA_IRQ_BUCK2_SCP] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_BUCK2_SCP,
> > + },
> > + [PV88080_BA_IRQ_BUCK3_SCP] = {
> > + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> > + .mask = PV88080_M_BUCK3_SCP,
> > + },
> > + /* PV88080 event C register for BA/BB silicon */
> > + [PV88080_BA_IRQ_GPIO_FLAG0] = {
> > + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> > + .mask = PV88080_M_GPIO_FLAG0,
> > + },
> > + [PV88080_BA_IRQ_GPIO_FLAG1] = {
> > + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> > + .mask = PV88080_M_GPIO_FLAG1,
> > + },
> > + [PV88080_BA_IRQ_BUCK1_DROP_TIMEOUT] = {
> > + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> > + .mask = PV88080_M_BUCK1_DROP_TIMEOUT,
> > + },
> > + [PV88080_BA_IRQ_BUCK2_DROP_TIMEOUT] = {
> > + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> > + .mask = PV88080_M_BUCK2_DROP_TIMEOUT,
> > + },
> > + [PB88080_BA_IRQ_BUCK3_DROP_TIMEOUT] = {
> > + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> > + .mask = PV88080_M_BUCk3_DROP_TIMEOUT,
> > + },
> > +};
> > +
> > +static const struct regmap_irq_chip pv88080_aa_irq_chip = {
> > + .name = "pv88080-irq",
> > + .irqs = pv88080_aa_irqs,
> > + .num_irqs = ARRAY_SIZE(pv88080_aa_irqs),
> > + .num_regs = 3,
> > + .status_base = PV88080_REG_EVENT_A,
> > + .mask_base = PV88080_REG_MASK_A,
> > + .ack_base = PV88080_REG_EVENT_A,
> > + .init_ack_masked = true,
> > +};
> > +
> > +static const struct regmap_irq_chip pv88080_ba_irq_chip = {
> > + .name = "pv88080-irq",
> > + .irqs = pv88080_ba_irqs,
> > + .num_irqs = ARRAY_SIZE(pv88080_ba_irqs),
> > + .num_regs = 3,
> > + .status_base = PV88080_REG_EVENT_A,
> > + .mask_base = PV88080_REG_MASK_A,
> > + .ack_base = PV88080_REG_EVENT_A,
> > + .init_ack_masked = true,
> > +};
> > +
> > +static const struct regmap_config pv88080_regmap_config = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > +};
> > +
> > +static const struct of_device_id pv88080_of_match_table[] = {
> > + { .compatible = "pvs,pv88080", .data = (void *)TYPE_PV88080_AA },
> > + { .compatible = "pvs,pv88080-aa", .data = (void *)TYPE_PV88080_AA },
> > + { .compatible = "pvs,pv88080-ba", .data = (void *)TYPE_PV88080_BA },
> > + { },
> > +};
> > +MODULE_DEVICE_TABLE(of, pv88080_of_match_table);
> > +
> > +static int pv88080_probe(struct i2c_client *client,
> > + const struct i2c_device_id *ids)
> > +{
> > + struct pv88080 *chip;
> > + const struct of_device_id *match;
> > + const struct regmap_irq_chip *pv88080_irq_chips;
> > + const struct mfd_cell *pv88080_mfd_cells;
> > + int ret, n_devs;
> > +
> > + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
> > + if (!chip)
> > + return -ENOMEM;
> > +
> > + if (client->dev.of_node) {
> > + match = of_match_node(pv88080_of_match_table,
> > + client->dev.of_node);
> > + if (!match) {
> > + dev_err(&client->dev, "Failed to get of_match_node\n");
> > + return -EINVAL;
>
> -ENODEV
OK, Thank you.
>
> > + }
> > + chip->type = (unsigned long)match->data;
> > + } else {
> > + chip->type = ids->driver_data;
> > + }
> > +
> > + i2c_set_clientdata(client, chip);
> > +
> > + chip->irq = client->irq;
> > + chip->dev = &client->dev;
> > +
> > + chip->regmap = devm_regmap_init_i2c(client, &pv88080_regmap_config);
> > + if (IS_ERR(chip->regmap)) {
> > + dev_err(chip->dev, "Failed to initialize register map\n");
> > + return PTR_ERR(chip->regmap);
> > + }
> > +
> > + ret = regmap_write(chip->regmap, PV88080_REG_MASK_A, 0xFF);
> > + if (ret < 0) {
> > + dev_err(chip->dev, "Failed to mask A reg: %d\n", ret);
> > + return ret;
> > + }
> > + ret = regmap_write(chip->regmap, PV88080_REG_MASK_B, 0xFF);
> > + if (ret < 0) {
> > + dev_err(chip->dev, "Failed to mask B reg: %d\n", ret);
> > + return ret;
> > + }
> > + ret = regmap_write(chip->regmap, PV88080_REG_MASK_C, 0xFF);
> > + if (ret < 0) {
> > + dev_err(chip->dev, "Failed to mask C reg: %d\n", ret);
> > + return ret;
> > + }
>
> What do these calls do?
You are right. I will remove those calls.
>
> > + switch (chip->type) {
> > + case TYPE_PV88080_AA:
> > + pv88080_irq_chips = &pv88080_aa_irq_chip;
> > + pv88080_mfd_cells = pv88080_aa_cells;
> > + n_devs = ARRAY_SIZE(pv88080_aa_cells);
> > + break;
> > + case TYPE_PV88080_BA:
> > + pv88080_irq_chips = &pv88080_ba_irq_chip;
> > + pv88080_mfd_cells = pv88080_ba_cells;
> > + n_devs = ARRAY_SIZE(pv88080_ba_cells);
> > + break;
> > + }
> > +
> > + ret = devm_regmap_add_irq_chip(chip->dev, chip->regmap,
> > + chip->irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> > + 0, pv88080_irq_chips, &chip->irq_data);
> > + if (ret) {
> > + dev_err(chip->dev, "Failed to add IRQ %d: %d\n",
> > + chip->irq, ret);
> > + return ret;
> > + }
> > +
> > + ret = devm_mfd_add_devices(chip->dev, PLATFORM_DEVID_NONE,
> > + pv88080_mfd_cells, n_devs,
> > + NULL, 0, NULL);
> > + if (ret) {
> > + dev_err(chip->dev, "Failed to add MFD devices\n");
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct i2c_device_id pv88080_id_table[] = {
> > + { "pv88080", TYPE_PV88080_AA },
> > + { "pv88080-aa", TYPE_PV88080_AA },
> > + { "pv88080-ba", TYPE_PV88080_BA },
> > + { },
> > +};
> > +MODULE_DEVICE_TABLE(i2c, pv88080_id_table);
> > +
> > +static struct i2c_driver pv88080_driver = {
> > + .driver = {
> > + .name = "pv88080",
> > + .of_match_table = of_match_ptr(pv88080_of_match_table),
> > + },
> > + .probe = pv88080_probe,
> > + .id_table = pv88080_id_table,
> > +};
> > +module_i2c_driver(pv88080_driver);
> > +
> > +MODULE_AUTHOR("Eric Jeong <eric.jeong.opensource@diasemi.com>");
> > +MODULE_DESCRIPTION("MFD Driver for Powerventure PV88080");
> > +MODULE_LICENSE("GPL");
> > +
> > diff --git a/include/linux/mfd/pv88080.h b/include/linux/mfd/pv88080.h
> > new file mode 100644 index 0000000..76d6656
> > --- /dev/null
> > +++ b/include/linux/mfd/pv88080.h
> > @@ -0,0 +1,222 @@
> > +/*
> > + * pv88080.h - Declarations for PV88080.
>
> Remove filename.
OK. I will.
>
> > + * Copyright (C) 2016 Powerventure Semiconductor Ltd.
> > + *
> > + * 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.
> > + */
> > +
> > +#ifndef __PV88080_H__
> > +#define __PV88080_H__
> > +
> > +#include <linux/regulator/machine.h>
> > +#include <linux/device.h>
> > +#include <linux/regmap.h>
> > +
> > +/* System Control and Event Registers */
> > +#define PV88080_REG_STATUS_A 0x01
> > +#define PV88080_REG_EVENT_A 0x04
> > +#define PV88080_REG_MASK_A 0x09
> > +#define PV88080_REG_MASK_B 0x0A
> > +#define PV88080_REG_MASK_C 0x0B
> > +
> > +/* GPIO Registers - rev. AA */
> > +#define PV88080AA_REG_GPIO_INPUT 0x18
> > +#define PV88080AA_REG_GPIO_OUTPUT 0x19
> > +#define PV88080AA_REG_GPIO_GPIO0 0x1A
> > +
> > +/* Regulator Registers - rev. AA */
> > +#define PV88080AA_REG_HVBUCK_CONF1 0x2D
> > +#define PV88080AA_REG_HVBUCK_CONF2 0x2E
> > +#define PV88080AA_REG_BUCK1_CONF0 0x27
> > +#define PV88080AA_REG_BUCK1_CONF1 0x28
> > +#define PV88080AA_REG_BUCK1_CONF2 0x59
> > +#define PV88080AA_REG_BUCK1_CONF5 0x5C
> > +#define PV88080AA_REG_BUCK2_CONF0 0x29
> > +#define PV88080AA_REG_BUCK2_CONF1 0x2A
> > +#define PV88080AA_REG_BUCK2_CONF2 0x61
> > +#define PV88080AA_REG_BUCK2_CONF5 0x64
> > +#define PV88080AA_REG_BUCK3_CONF0 0x2B
> > +#define PV88080AA_REG_BUCK3_CONF1 0x2C
> > +#define PV88080AA_REG_BUCK3_CONF2 0x69
> > +#define PV88080AA_REG_BUCK3_CONF5 0x6C
> > +
> > +/* GPIO Registers - rev. BA */
> > +#define PV88080BA_REG_GPIO_INPUT 0x17
> > +#define PV88080BA_REG_GPIO_OUTPUT 0x18
> > +#define PV88080BA_REG_GPIO_GPIO0 0x19
> > +
> > +/* Regulator Registers - rev. BA */
> > +#define PV88080BA_REG_HVBUCK_CONF1 0x33
> > +#define PV88080BA_REG_HVBUCK_CONF2 0x34
> > +#define PV88080BA_REG_BUCK1_CONF0 0x2A
> > +#define PV88080BA_REG_BUCK1_CONF1 0x2C
> > +#define PV88080BA_REG_BUCK1_CONF2 0x5A
> > +#define PV88080BA_REG_BUCK1_CONF5 0x5D
> > +#define PV88080BA_REG_BUCK2_CONF0 0x2D
> > +#define PV88080BA_REG_BUCK2_CONF1 0x2F
> > +#define PV88080BA_REG_BUCK2_CONF2 0x63
> > +#define PV88080BA_REG_BUCK2_CONF5 0x66
> > +#define PV88080BA_REG_BUCK3_CONF0 0x30
> > +#define PV88080BA_REG_BUCK3_CONF1 0x32
> > +#define PV88080BA_REG_BUCK3_CONF2 0x6C
> > +#define PV88080BA_REG_BUCK3_CONF5 0x6F
> > +
> > +/* PV88080_REG_EVENT_A (addr=0x04) */
> > +#define PV88080_E_VDD_FLT 0x01
> > +#define PV88080_E_OVER_TEMP 0x02
> > +#define PV88080_E_SEQ_RDY 0x04
> > +#define PV88080_E_EXT_OT 0x08
> > +
> > +/* PV88080_REG_MASK_A (addr=0x09) */
> > +#define PV88080_M_VDD_FLT 0x01
> > +#define PV88080_M_OVER_TEMP 0x02
> > +#define PV88080_M_SEQ_RDY 0x04
> > +#define PV88080_M_EXT_OT 0x08
> > +
> > +/* PV88080_REG_EVENT_B (addr=0x05) */
> > +#define PV88080_E_HVBUCK_OV 0x01
> > +#define PV88080_E_HVBUCK_UV 0x02
> > +#define PV88080_E_HVBUCK_SCP 0x04
> > +#define PV88080_E_BUCK1_SCP 0x08
> > +#define PV88080_E_BUCK2_SCP 0x10
> > +#define PV88080_E_BUCK3_SCP 0x20
> > +
> > +/* PV88080_REG_MASK_B (addr=0x0A) */
> > +#define PV88080_M_HVBUCK_OV 0x01
> > +#define PV88080_M_HVBUCK_UV 0x02
> > +#define PV88080_M_HVBUCK_SCP 0x04
> > +#define PV88080_M_BUCK1_SCP 0x08
> > +#define PV88080_M_BUCK2_SCP 0x10
> > +#define PV88080_M_BUCK3_SCP 0x20
> > +
> > +/* PV88080_REG_EVENT_C (addr=0x06) */
> > +#define PV88080_E_GPIO_FLAG0 0x01
> > +#define PV88080_E_GPIO_FLAG1 0x02
> > +#define PV88080_E_BUCK1_DROP_TIMEOUT 0x08
> > +#define PV88080_E_BUCK2_DROP_TIMEOUT 0x10
> > +#define PV88080_E_BUCk3_DROP_TIMEOUT 0x20
> > +
> > +/* PV88080_REG_MASK_C (addr=0x0B) */
> > +#define PV88080_M_GPIO_FLAG0 0x01
> > +#define PV88080_M_GPIO_FLAG1 0x02
> > +#define PV88080_M_BUCK1_DROP_TIMEOUT 0x08
> > +#define PV88080_M_BUCK2_DROP_TIMEOUT 0x10
> > +#define PV88080_M_BUCk3_DROP_TIMEOUT 0x20
> > +
> > +/* PV88080xx_REG_GPIO_GPIO0 (addr=0x1A|0x19) */
> > +#define PV88080_GPIO_DIRECTION_MASK 0x01
> > +#define PV88080_GPIO_SINGLE_ENDED_MASK 0x02
> > +
> > +/* PV88080_REG_BUCK1_CONF0 (addr=0x27|0x2A) */
> > +#define PV88080_BUCK1_EN 0x80
> > +#define PV88080_VBUCK1_MASK 0x7F
> > +
> > +/* PV88080_REG_BUCK2_CONF0 (addr=0x29|0x2D) */
> > +#define PV88080_BUCK2_EN 0x80
> > +#define PV88080_VBUCK2_MASK 0x7F
> > +
> > +/* PV88080_REG_BUCK3_CONF0 (addr=0x2B|0x30) */
> > +#define PV88080_BUCK3_EN 0x80
> > +#define PV88080_VBUCK3_MASK 0x7F
> > +
> > +/* PV88080_REG_BUCK1_CONF1 (addr=0x28|0x2C) */
> > +#define PV88080_BUCK1_ILIM_SHIFT 2
> > +#define PV88080_BUCK1_ILIM_MASK 0x0C
> > +#define PV88080_BUCK1_MODE_MASK 0x03
> > +
> > +/* PV88080_REG_BUCK2_CONF1 (addr=0x2A|0x2F) */
> > +#define PV88080_BUCK2_ILIM_SHIFT 2
> > +#define PV88080_BUCK2_ILIM_MASK 0x0C
> > +#define PV88080_BUCK2_MODE_MASK 0x03
> > +
> > +/* PV88080_REG_BUCK3_CONF1 (addr=0x2C|0x32) */
> > +#define PV88080_BUCK3_ILIM_SHIFT 2
> > +#define PV88080_BUCK3_ILIM_MASK 0x0C
> > +#define PV88080_BUCK3_MODE_MASK 0x03
> > +
> > +#define PV88080_BUCK_MODE_SLEEP 0x00
> > +#define PV88080_BUCK_MODE_AUTO 0x01
> > +#define PV88080_BUCK_MODE_SYNC 0x02
> > +
> > +/* PV88080_REG_HVBUCK_CONF1 (addr=0x2D|0x33) */
> > +#define PV88080_VHVBUCK_MASK 0xFF
> > +
> > +/* PV88080_REG_HVBUCK_CONF1 (addr=0x2E|0x34) */
> > +#define PV88080_HVBUCK_EN 0x01
> > +
> > +/* PV88080_REG_BUCK2_CONF2 (addr=0x61|0x63) */
> > +/* PV88080_REG_BUCK3_CONF2 (addr=0x69|0x6C) */
> > +#define PV88080_BUCK_VDAC_RANGE_SHIFT 7
> > +#define PV88080_BUCK_VDAC_RANGE_MASK 0x01
> > +
> > +#define PV88080_BUCK_VDAC_RANGE_1 0x00
> > +#define PV88080_BUCK_VDAC_RANGE_2 0x01
> > +
> > +/* PV88080_REG_BUCK2_CONF5 (addr=0x64|0x66) */
> > +/* PV88080_REG_BUCK3_CONF5 (addr=0x6C|0x6F) */
> > +#define PV88080_BUCK_VRANGE_GAIN_SHIFT 0
> > +#define PV88080_BUCK_VRANGE_GAIN_MASK 0x01
> > +
> > +#define PV88080_BUCK_VRANGE_GAIN_1 0x00
> > +#define PV88080_BUCK_VRANGE_GAIN_2 0x01
> > +
> > +#define PV88080_MAX_REGULATORS 4
> > +
> > +enum pv88080_types {
> > + TYPE_PV88080_AA,
> > + TYPE_PV88080_BA,
> > +};
> > +
> > +/* Interrupts */
> > +enum pv88080_aa_irqs {
> > + PV88080_AA_IRQ_VDD_FLT,
> > + PV88080_AA_IRQ_OVER_TEMP,
> > + PV88080_AA_IRQ_SEQ_RDY,
> > + PV88080_AA_IRQ_HVBUCK_OV,
> > + PV88080_AA_IRQ_HVBUCK_UV,
> > + PV88080_AA_IRQ_HVBUCK_SCP,
> > + PV88080_AA_IRQ_BUCK1_SCP,
> > + PV88080_AA_IRQ_BUCK2_SCP,
> > + PV88080_AA_IRQ_BUCK3_SCP,
> > + PV88080_AA_IRQ_GPIO_FLAG0,
> > + PV88080_AA_IRQ_GPIO_FLAG1,
> > +};
> > +
> > +enum pv88080_ba_irqs {
> > + PV88080_BA_IRQ_VDD_FLT,
> > + PV88080_BA_IRQ_OVER_TEMP,
> > + PV88080_BA_IRQ_SEQ_RDY,
> > + PV88080_BA_IRQ_EXT_OT,
> > + PV88080_BA_IRQ_HVBUCK_OV,
> > + PV88080_BA_IRQ_HVBUCK_UV,
> > + PV88080_BA_IRQ_HVBUCK_SCP,
> > + PV88080_BA_IRQ_BUCK1_SCP,
> > + PV88080_BA_IRQ_BUCK2_SCP,
> > + PV88080_BA_IRQ_BUCK3_SCP,
> > + PV88080_BA_IRQ_GPIO_FLAG0,
> > + PV88080_BA_IRQ_GPIO_FLAG1,
> > + PV88080_BA_IRQ_BUCK1_DROP_TIMEOUT,
> > + PV88080_BA_IRQ_BUCK2_DROP_TIMEOUT,
> > + PB88080_BA_IRQ_BUCK3_DROP_TIMEOUT,
> > +};
> > +
> > +struct pv88080 {
> > + struct device *dev;
> > + struct regmap *regmap;
> > + unsigned long type;
>
> Does this really need to be in here?
The *type* member is used for separating silicon type.
And, regulator and gpio driver also use the member to check the type
for proper configuration without additional code.
That is the reason that the member is added in the structure.
>
> > + /* IRQ Data */
> > + int irq;
> > + struct regmap_irq_chip_data *irq_data; };
> > +
> > +#endif /* __PV88080_H__ */
> > +
>
> --
> Lee Jones
> Linaro STMicroelectronics Landing Team Lead Linaro.org │ Open source software for
> ARM SoCs Follow Linaro: Facebook | Twitter | Blog
I have added some comments. Thank you.
Regards
Eric
^ permalink raw reply
* Re: [PATCH 02/10] ASoC: sunxi: Add support for A23/A33/H3 codec's analog path controls
From: Icenowy Zheng @ 2016-11-25 5:51 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Mark Rutland, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, Liam Girdwood, Rob Herring,
linux-kernel@vger.kernel.org, Mark Brown, Maxime Ripard,
Mylene Josserand, Lee Jones, linux-arm-kernel@lists.infradead.org
In-Reply-To: <CAGb2v66Zb9-UdimKDn5tq9UPRY7wBZNNCCn-52bf=evP914sPg@mail.gmail.com>
25.11.2016, 13:46, "Chen-Yu Tsai" <wens@csie.org>:
> On Fri, Nov 25, 2016 at 1:43 PM, Icenowy Zheng <icenowy@aosc.xyz> wrote:
>> 12.11.2016, 14:57, "Chen-Yu Tsai" <wens@csie.org>:
>>> The internal codec on A23/A33/H3 is split into 2 parts. The
>>> analog path controls are routed through an embedded custom register
>>> bus accessed through the PRCM block.
>>>
>>> The SoCs share a common set of inputs, outputs, and audio paths.
>>> The following table lists the differences.
>>>
>>> ----------------------------------------
>>> | Feature \ SoC | A23 | A33 | H3 |
>>> ----------------------------------------
>>> | Headphone | v | v | |
>>> ----------------------------------------
>>> | Line Out | | | v |
>>> ----------------------------------------
>>> | Phone In/Out | v | v | |
>>> ----------------------------------------
>>>
>>> Add an ASoC component driver for it. This should be tied to the codec
>>> audio card as an auxiliary device. This patch adds the commont paths
>>> and controls, and variant specific headphone out and line out.
>>>
>>> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
>>> ---
>>> sound/soc/sunxi/Kconfig | 8 +
>>> sound/soc/sunxi/Makefile | 1 +
>>> sound/soc/sunxi/sun8i-codec-analog.c | 665 +++++++++++++++++++++++++++++++++++
>>> 3 files changed, 674 insertions(+)
>>> create mode 100644 sound/soc/sunxi/sun8i-codec-analog.c
>>>
>>> diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
>>> index dd2368297fd3..6c344e16aca4 100644
>>> --- a/sound/soc/sunxi/Kconfig
>>> +++ b/sound/soc/sunxi/Kconfig
>>> @@ -9,6 +9,14 @@ config SND_SUN4I_CODEC
>>> Select Y or M to add support for the Codec embedded in the Allwinner
>>> A10 and affiliated SoCs.
>>>
>>> +config SND_SUN8I_CODEC_ANALOG
>>> + tristate "Allwinner sun8i Codec Analog Controls Support"
>>> + depends on MACH_SUN8I || COMPILE_TEST
>>
>> sun50i-a64 has a similar (or the same?) codec to A33.
>
> I think the register offsets/fields were moved around again.
> Why does Allwinner always do that... :/
Yes, moved around :-(
Or maybe I should say "there's more registers on A64".
>
> ChenYu
>
>>> + select REGMAP
>>> + help
>>> + Say Y or M if you want to add support for the analog controls for
>>> + the codec embedded in newer Allwinner SoCs.
>>> +
>>> config SND_SUN4I_I2S
>>> tristate "Allwinner A10 I2S Support"
>>> select SND_SOC_GENERIC_DMAENGINE_PCM
>>> diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
>>> index 604c7b842837..241c0df9ca0c 100644
>>> --- a/sound/soc/sunxi/Makefile
>>> +++ b/sound/soc/sunxi/Makefile
>>> @@ -1,3 +1,4 @@
>>> obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
>>> obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
>>> obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
>>> +obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o
>>> diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c
>>> new file mode 100644
>>> index 000000000000..222bbd440b1e
>>> --- /dev/null
>>> +++ b/sound/soc/sunxi/sun8i-codec-analog.c
>>> @@ -0,0 +1,665 @@
>>> +/*
>>> + * This driver supports the analog controls for the internal codec
>>> + * found in Allwinner's A31s, A23, A33 and H3 SoCs.
>>> + *
>>> + * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +
>>> +#include <sound/soc.h>
>>> +#include <sound/soc-dapm.h>
>>> +#include <sound/tlv.h>
>>> +
>>> +/* Codec analog control register offsets and bit fields */
>>> +#define SUN8I_ADDA_HP_VOLC 0x00
>>> +#define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE 7
>>> +#define SUN8I_ADDA_HP_VOLC_HP_VOL 0
>>> +#define SUN8I_ADDA_LOMIXSC 0x01
>>> +#define SUN8I_ADDA_LOMIXSC_MIC1 6
>>> +#define SUN8I_ADDA_LOMIXSC_MIC2 5
>>> +#define SUN8I_ADDA_LOMIXSC_PHONE 4
>>> +#define SUN8I_ADDA_LOMIXSC_PHONEN 3
>>> +#define SUN8I_ADDA_LOMIXSC_LINEINL 2
>>> +#define SUN8I_ADDA_LOMIXSC_DACL 1
>>> +#define SUN8I_ADDA_LOMIXSC_DACR 0
>>> +#define SUN8I_ADDA_ROMIXSC 0x02
>>> +#define SUN8I_ADDA_ROMIXSC_MIC1 6
>>> +#define SUN8I_ADDA_ROMIXSC_MIC2 5
>>> +#define SUN8I_ADDA_ROMIXSC_PHONE 4
>>> +#define SUN8I_ADDA_ROMIXSC_PHONEP 3
>>> +#define SUN8I_ADDA_ROMIXSC_LINEINR 2
>>> +#define SUN8I_ADDA_ROMIXSC_DACR 1
>>> +#define SUN8I_ADDA_ROMIXSC_DACL 0
>>> +#define SUN8I_ADDA_DAC_PA_SRC 0x03
>>> +#define SUN8I_ADDA_DAC_PA_SRC_DACAREN 7
>>> +#define SUN8I_ADDA_DAC_PA_SRC_DACALEN 6
>>> +#define SUN8I_ADDA_DAC_PA_SRC_RMIXEN 5
>>> +#define SUN8I_ADDA_DAC_PA_SRC_LMIXEN 4
>>> +#define SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE 3
>>> +#define SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE 2
>>> +#define SUN8I_ADDA_DAC_PA_SRC_RHPIS 1
>>> +#define SUN8I_ADDA_DAC_PA_SRC_LHPIS 0
>>> +#define SUN8I_ADDA_PHONEIN_GCTRL 0x04
>>> +#define SUN8I_ADDA_PHONEIN_GCTRL_PHONEPG 4
>>> +#define SUN8I_ADDA_PHONEIN_GCTRL_PHONENG 0
>>> +#define SUN8I_ADDA_LINEIN_GCTRL 0x05
>>> +#define SUN8I_ADDA_LINEIN_GCTRL_LINEING 4
>>> +#define SUN8I_ADDA_LINEIN_GCTRL_PHONEG 0
>>> +#define SUN8I_ADDA_MICIN_GCTRL 0x06
>>> +#define SUN8I_ADDA_MICIN_GCTRL_MIC1G 4
>>> +#define SUN8I_ADDA_MICIN_GCTRL_MIC2G 0
>>> +#define SUN8I_ADDA_PAEN_HP_CTRL 0x07
>>> +#define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN 7
>>> +#define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN 7 /* H3 specific */
>>> +#define SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC 5
>>> +#define SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN 4
>>> +#define SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL 2
>>> +#define SUN8I_ADDA_PAEN_HP_CTRL_LTRNMUTE 1
>>> +#define SUN8I_ADDA_PAEN_HP_CTRL_RTLNMUTE 0
>>> +#define SUN8I_ADDA_PHONEOUT_CTRL 0x08
>>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTG 5
>>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTEN 4
>>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC1 3
>>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC2 2
>>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_RMIX 1
>>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_LMIX 0
>>> +#define SUN8I_ADDA_PHONE_GAIN_CTRL 0x09
>>> +#define SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL 3
>>> +#define SUN8I_ADDA_PHONE_GAIN_CTRL_PHONEPREG 0
>>> +#define SUN8I_ADDA_MIC2G_CTRL 0x0a
>>> +#define SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN 7
>>> +#define SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST 4
>>> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN 3
>>> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN 2
>>> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC 1
>>> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC 0
>>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL 0x0b
>>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN 7
>>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN 6
>>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE 5
>>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN 3
>>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST 0
>>> +#define SUN8I_ADDA_LADCMIXSC 0x0c
>>> +#define SUN8I_ADDA_LADCMIXSC_MIC1 6
>>> +#define SUN8I_ADDA_LADCMIXSC_MIC2 5
>>> +#define SUN8I_ADDA_LADCMIXSC_PHONE 4
>>> +#define SUN8I_ADDA_LADCMIXSC_PHONEN 3
>>> +#define SUN8I_ADDA_LADCMIXSC_LINEINL 2
>>> +#define SUN8I_ADDA_LADCMIXSC_OMIXRL 1
>>> +#define SUN8I_ADDA_LADCMIXSC_OMIXRR 0
>>> +#define SUN8I_ADDA_RADCMIXSC 0x0d
>>> +#define SUN8I_ADDA_RADCMIXSC_MIC1 6
>>> +#define SUN8I_ADDA_RADCMIXSC_MIC2 5
>>> +#define SUN8I_ADDA_RADCMIXSC_PHONE 4
>>> +#define SUN8I_ADDA_RADCMIXSC_PHONEP 3
>>> +#define SUN8I_ADDA_RADCMIXSC_LINEINR 2
>>> +#define SUN8I_ADDA_RADCMIXSC_OMIXR 1
>>> +#define SUN8I_ADDA_RADCMIXSC_OMIXL 0
>>> +#define SUN8I_ADDA_RES 0x0e
>>> +#define SUN8I_ADDA_RES_MMICBIAS_SEL 4
>>> +#define SUN8I_ADDA_RES_PA_ANTI_POP_CTRL 0
>>> +#define SUN8I_ADDA_ADC_AP_EN 0x0f
>>> +#define SUN8I_ADDA_ADC_AP_EN_ADCREN 7
>>> +#define SUN8I_ADDA_ADC_AP_EN_ADCLEN 6
>>> +#define SUN8I_ADDA_ADC_AP_EN_ADCG 0
>>> +
>>> +/* Analog control register access bits */
>>> +#define ADDA_PR 0x0 /* PRCM base + 0x1c0 */
>>> +#define ADDA_PR_RESET BIT(28)
>>> +#define ADDA_PR_WRITE BIT(24)
>>> +#define ADDA_PR_ADDR_SHIFT 16
>>> +#define ADDA_PR_ADDR_MASK GENMASK(4, 0)
>>> +#define ADDA_PR_DATA_IN_SHIFT 8
>>> +#define ADDA_PR_DATA_IN_MASK GENMASK(7, 0)
>>> +#define ADDA_PR_DATA_OUT_SHIFT 0
>>> +#define ADDA_PR_DATA_OUT_MASK GENMASK(7, 0)
>>> +
>>> +/* regmap access bits */
>>> +static int adda_reg_read(void *context, unsigned int reg, unsigned int *val)
>>> +{
>>> + void __iomem *base = (void __iomem *)context;
>>> + u32 tmp;
>>> +
>>> + /* De-assert reset */
>>> + writel(readl(base) | ADDA_PR_RESET, base);
>>> +
>>> + /* Clear write bit */
>>> + writel(readl(base) & ~ADDA_PR_WRITE, base);
>>> +
>>> + /* Set register address */
>>> + tmp = readl(base);
>>> + tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
>>> + tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
>>> + writel(tmp, base);
>>> +
>>> + /* Read back value */
>>> + *val = readl(base) & ADDA_PR_DATA_OUT_MASK;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int adda_reg_write(void *context, unsigned int reg, unsigned int val)
>>> +{
>>> + void __iomem *base = (void __iomem *)context;
>>> + u32 tmp;
>>> +
>>> + /* De-assert reset */
>>> + writel(readl(base) | ADDA_PR_RESET, base);
>>> +
>>> + /* Set register address */
>>> + tmp = readl(base);
>>> + tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
>>> + tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
>>> + writel(tmp, base);
>>> +
>>> + /* Set data to write */
>>> + tmp = readl(base);
>>> + tmp &= ~(ADDA_PR_DATA_IN_MASK << ADDA_PR_DATA_IN_SHIFT);
>>> + tmp |= (val & ADDA_PR_DATA_IN_MASK) << ADDA_PR_DATA_IN_SHIFT;
>>> + writel(tmp, base);
>>> +
>>> + /* Set write bit to signal a write */
>>> + writel(readl(base) | ADDA_PR_WRITE, base);
>>> +
>>> + /* Clear write bit */
>>> + writel(readl(base) & ~ADDA_PR_WRITE, base);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct regmap_config adda_pr_regmap_cfg = {
>>> + .name = "adda-pr",
>>> + .reg_bits = 5,
>>> + .reg_stride = 1,
>>> + .val_bits = 8,
>>> + .reg_read = adda_reg_read,
>>> + .reg_write = adda_reg_write,
>>> + .fast_io = true,
>>> + .max_register = 24,
>>> +};
>>> +
>>> +/* mixer controls */
>>> +static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = {
>>> + SOC_DAPM_DOUBLE_R("DAC Playback Switch",
>>> + SUN8I_ADDA_LOMIXSC,
>>> + SUN8I_ADDA_ROMIXSC,
>>> + SUN8I_ADDA_LOMIXSC_DACL, 1, 0),
>>> + SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
>>> + SUN8I_ADDA_LOMIXSC,
>>> + SUN8I_ADDA_ROMIXSC,
>>> + SUN8I_ADDA_LOMIXSC_DACR, 1, 0),
>>> + SOC_DAPM_DOUBLE_R("Line In Playback Switch",
>>> + SUN8I_ADDA_LOMIXSC,
>>> + SUN8I_ADDA_ROMIXSC,
>>> + SUN8I_ADDA_LOMIXSC_LINEINL, 1, 0),
>>> + SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
>>> + SUN8I_ADDA_LOMIXSC,
>>> + SUN8I_ADDA_ROMIXSC,
>>> + SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),
>>> + SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
>>> + SUN8I_ADDA_LOMIXSC,
>>> + SUN8I_ADDA_ROMIXSC,
>>> + SUN8I_ADDA_LOMIXSC_MIC2, 1, 0),
>>> +};
>>> +
>>> +/* ADC mixer controls */
>>> +static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = {
>>> + SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
>>> + SUN8I_ADDA_LADCMIXSC,
>>> + SUN8I_ADDA_RADCMIXSC,
>>> + SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),
>>> + SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
>>> + SUN8I_ADDA_LADCMIXSC,
>>> + SUN8I_ADDA_RADCMIXSC,
>>> + SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),
>>> + SOC_DAPM_DOUBLE_R("Line In Capture Switch",
>>> + SUN8I_ADDA_LADCMIXSC,
>>> + SUN8I_ADDA_RADCMIXSC,
>>> + SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0),
>>> + SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
>>> + SUN8I_ADDA_LADCMIXSC,
>>> + SUN8I_ADDA_RADCMIXSC,
>>> + SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),
>>> + SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
>>> + SUN8I_ADDA_LADCMIXSC,
>>> + SUN8I_ADDA_RADCMIXSC,
>>> + SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0),
>>> +};
>>> +
>>> +/* volume / mute controls */
>>> +static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale,
>>> + -450, 150, 0);
>>> +static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale,
>>> + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
>>> + 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
>>> +);
>>> +
>>> +static const struct snd_kcontrol_new sun8i_codec_common_controls[] = {
>>> + /* Mixer pre-gains */
>>> + SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
>>> + SUN8I_ADDA_LINEIN_GCTRL_LINEING,
>>> + 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
>>> + SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL,
>>> + SUN8I_ADDA_MICIN_GCTRL_MIC1G,
>>> + 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
>>> + SOC_SINGLE_TLV("Mic2 Playback Volume",
>>> + SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
>>> + 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
>>> +
>>> + /* Microphone Amp boost gains */
>>> + SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
>>> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0,
>>> + sun8i_codec_mic_gain_scale),
>>> + SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
>>> + SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
>>> + sun8i_codec_mic_gain_scale),
>>> +
>>> + /* ADC */
>>> + SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN,
>>> + SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0,
>>> + sun8i_codec_out_mixer_pregain_scale),
>>> +};
>>> +
>>> +static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
>>> + /* ADC */
>>> + SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
>>> + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0),
>>> + SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
>>> + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0),
>>> +
>>> + /* DAC */
>>> + SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
>>> + SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0),
>>> + SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
>>> + SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0),
>>> + /*
>>> + * Due to this component and the codec belonging to separate DAPM
>>> + * contexts, we need to manually link the above widgets to their
>>> + * stream widgets at the card level.
>>> + */
>>> +
>>> + /* Line In */
>>> + SND_SOC_DAPM_INPUT("LINEIN"),
>>> +
>>> + /* Microphone inputs */
>>> + SND_SOC_DAPM_INPUT("MIC1"),
>>> + SND_SOC_DAPM_INPUT("MIC2"),
>>> +
>>> + /* Microphone Bias */
>>> + SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
>>> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN,
>>> + 0, NULL, 0),
>>> +
>>> + /* Mic input path */
>>> + SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
>>> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),
>>> + SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
>>> + SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
>>> +
>>> + /* Mixers */
>>> + SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
>>> + SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
>>> + sun8i_codec_mixer_controls,
>>> + ARRAY_SIZE(sun8i_codec_mixer_controls)),
>>> + SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,
>>> + SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,
>>> + sun8i_codec_mixer_controls,
>>> + ARRAY_SIZE(sun8i_codec_mixer_controls)),
>>> + SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
>>> + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,
>>> + sun8i_codec_adc_mixer_controls,
>>> + ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
>>> + SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
>>> + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,
>>> + sun8i_codec_adc_mixer_controls,
>>> + ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
>>> +};
>>> +
>>> +static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {
>>> + /* Microphone Routes */
>>> + { "Mic1 Amplifier", NULL, "MIC1"},
>>> + { "Mic2 Amplifier", NULL, "MIC2"},
>>> +
>>> + /* Left Mixer Routes */
>>> + { "Left Mixer", "DAC Playback Switch", "Left DAC" },
>>> + { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
>>> + { "Left Mixer", "Line In Playback Switch", "LINEIN" },
>>> + { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
>>> + { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
>>> +
>>> + /* Right Mixer Routes */
>>> + { "Right Mixer", "DAC Playback Switch", "Right DAC" },
>>> + { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
>>> + { "Right Mixer", "Line In Playback Switch", "LINEIN" },
>>> + { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
>>> + { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
>>> +
>>> + /* Left ADC Mixer Routes */
>>> + { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
>>> + { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
>>> + { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
>>> + { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
>>> + { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
>>> +
>>> + /* Right ADC Mixer Routes */
>>> + { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
>>> + { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
>>> + { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
>>> + { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
>>> + { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
>>> +
>>> + /* ADC Routes */
>>> + { "Left ADC", NULL, "Left ADC Mixer" },
>>> + { "Right ADC", NULL, "Right ADC Mixer" },
>>> +};
>>> +
>>> +/* headphone specific controls, widgets, and routes */
>>> +static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1);
>>> +static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = {
>>> + SOC_SINGLE_TLV("Headphone Playback Volume",
>>> + SUN8I_ADDA_HP_VOLC,
>>> + SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0,
>>> + sun8i_codec_hp_vol_scale),
>>> + SOC_DOUBLE("Headphone Playback Switch",
>>> + SUN8I_ADDA_DAC_PA_SRC,
>>> + SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE,
>>> + SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0),
>>> +};
>>> +
>>> +static const char * const sun8i_codec_hp_src_enum_text[] = {
>>> + "DAC", "Mixer",
>>> +};
>>> +
>>> +static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum,
>>> + SUN8I_ADDA_DAC_PA_SRC,
>>> + SUN8I_ADDA_DAC_PA_SRC_LHPIS,
>>> + SUN8I_ADDA_DAC_PA_SRC_RHPIS,
>>> + sun8i_codec_hp_src_enum_text);
>>> +
>>> +static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {
>>> + SOC_DAPM_ENUM("Headphone Source Playback Route",
>>> + sun8i_codec_hp_src_enum),
>>> +};
>>> +
>>> +static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {
>>> + SND_SOC_DAPM_MUX("Headphone Source Playback Route",
>>> + SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),
>>> + SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
>>> + SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0),
>>> + SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,
>>> + SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),
>>> + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,
>>> + SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0),
>>> + SND_SOC_DAPM_OUTPUT("HP"),
>>> +};
>>> +
>>> +static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = {
>>> + { "Headphone Source Playback Route", "DAC", "Left DAC" },
>>> + { "Headphone Source Playback Route", "DAC", "Right DAC" },
>>> + { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
>>> + { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
>>> + { "Headphone Amp", NULL, "Headphone Source Playback Route" },
>>> + { "HPCOM", NULL, "HPCOM Protection" },
>>> + { "HP", NULL, "Headphone Amp" },
>>> +};
>>> +
>>> +static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt)
>>> +{
>>> + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
>>> + struct device *dev = cmpnt->dev;
>>> + int ret;
>>> +
>>> + ret = snd_soc_add_component_controls(cmpnt,
>>> + sun8i_codec_headphone_controls,
>>> + ARRAY_SIZE(sun8i_codec_headphone_controls));
>>> + if (ret) {
>>> + dev_err(dev, "Failed to add Headphone controls: %d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets,
>>> + ARRAY_SIZE(sun8i_codec_headphone_widgets));
>>> + if (ret) {
>>> + dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes,
>>> + ARRAY_SIZE(sun8i_codec_headphone_routes));
>>> + if (ret) {
>>> + dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/* hmic specific widget */
>>> +static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = {
>>> + SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
>>> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN,
>>> + 0, NULL, 0),
>>> +};
>>> +
>>> +static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt)
>>> +{
>>> + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
>>> + struct device *dev = cmpnt->dev;
>>> + int ret;
>>> +
>>> + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets,
>>> + ARRAY_SIZE(sun8i_codec_hmic_widgets));
>>> + if (ret)
>>> + dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +/* line out specific controls, widgets and routes */
>>> +static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale,
>>> + 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
>>> + 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
>>> +);
>>> +static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = {
>>> + SOC_SINGLE_TLV("Line Out Playback Volume",
>>> + SUN8I_ADDA_PHONE_GAIN_CTRL,
>>> + SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0,
>>> + sun8i_codec_lineout_vol_scale),
>>> + SOC_DOUBLE("Line Out Playback Switch",
>>> + SUN8I_ADDA_MIC2G_CTRL,
>>> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN,
>>> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0),
>>> +};
>>> +
>>> +static const char * const sun8i_codec_lineout_src_enum_text[] = {
>>> + "Stereo", "Mono Differential",
>>> +};
>>> +
>>> +static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum,
>>> + SUN8I_ADDA_MIC2G_CTRL,
>>> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC,
>>> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC,
>>> + sun8i_codec_lineout_src_enum_text);
>>> +
>>> +static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = {
>>> + SOC_DAPM_ENUM("Line Out Source Playback Route",
>>> + sun8i_codec_lineout_src_enum),
>>> +};
>>> +
>>> +static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = {
>>> + SND_SOC_DAPM_MUX("Line Out Source Playback Route",
>>> + SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src),
>>> + /* It is unclear if this is a buffer or gate, model it as a supply */
>>> + SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL,
>>> + SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0),
>>> + SND_SOC_DAPM_OUTPUT("LINEOUT"),
>>> +};
>>> +
>>> +static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = {
>>> + { "Line Out Source Playback Route", "Stereo", "Left Mixer" },
>>> + { "Line Out Source Playback Route", "Stereo", "Right Mixer" },
>>> + { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
>>> + { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
>>> + { "LINEOUT", NULL, "Line Out Source Playback Route" },
>>> + { "LINEOUT", NULL, "Line Out Enable", },
>>> +};
>>> +
>>> +static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt)
>>> +{
>>> + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
>>> + struct device *dev = cmpnt->dev;
>>> + int ret;
>>> +
>>> + ret = snd_soc_add_component_controls(cmpnt,
>>> + sun8i_codec_lineout_controls,
>>> + ARRAY_SIZE(sun8i_codec_lineout_controls));
>>> + if (ret) {
>>> + dev_err(dev, "Failed to add Line Out controls: %d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets,
>>> + ARRAY_SIZE(sun8i_codec_lineout_widgets));
>>> + if (ret) {
>>> + dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes,
>>> + ARRAY_SIZE(sun8i_codec_lineout_routes));
>>> + if (ret) {
>>> + dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +struct sun8i_codec_analog_quirks {
>>> + bool has_headphone;
>>> + bool has_hmic;
>>> + bool has_lineout;
>>> +};
>>> +
>>> +static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
>>> + .has_headphone = true,
>>> + .has_hmic = true,
>>> +};
>>> +
>>> +static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {
>>> + .has_lineout = true,
>>> +};
>>> +
>>> +static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
>>> +{
>>> + struct device *dev = cmpnt->dev;
>>> + const struct sun8i_codec_analog_quirks *quirks;
>>> + int ret;
>>> +
>>> + /*
>>> + * This would never return NULL unless someone directly registers a
>>> + * platform device matching this driver's name, without specifying a
>>> + * device tree node.
>>> + */
>>> + quirks = of_device_get_match_data(dev);
>>> +
>>> + /* Add controls, widgets, and routes for individual features */
>>> +
>>> + if (quirks->has_headphone) {
>>> + ret = sun8i_codec_add_headphone(cmpnt);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + if (quirks->has_hmic) {
>>> + sun8i_codec_add_hmic(cmpnt);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + if (quirks->has_lineout) {
>>> + ret = sun8i_codec_add_lineout(cmpnt);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = {
>>> + .controls = sun8i_codec_common_controls,
>>> + .num_controls = ARRAY_SIZE(sun8i_codec_common_controls),
>>> + .dapm_widgets = sun8i_codec_common_widgets,
>>> + .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_common_widgets),
>>> + .dapm_routes = sun8i_codec_common_routes,
>>> + .num_dapm_routes = ARRAY_SIZE(sun8i_codec_common_routes),
>>> + .probe = sun8i_codec_analog_cmpnt_probe,
>>> +};
>>> +
>>> +static const struct of_device_id sun8i_codec_analog_of_match[] = {
>>> + {
>>> + .compatible = "allwinner,sun8i-a23-codec-analog",
>>> + .data = &sun8i_a23_quirks,
>>> + },
>>> + {
>>> + .compatible = "allwinner,sun8i-h3-codec-analog",
>>> + .data = &sun8i_h3_quirks,
>>> + },
>>> + {}
>>> +};
>>> +MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);
>>> +
>>> +static int sun8i_codec_analog_probe(struct platform_device *pdev)
>>> +{
>>> + struct resource *res;
>>> + struct regmap *regmap;
>>> + void __iomem *base;
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + base = devm_ioremap_resource(&pdev->dev, res);
>>> + if (IS_ERR(base)) {
>>> + dev_err(&pdev->dev, "Failed to map the registers\n");
>>> + return PTR_ERR(base);
>>> + }
>>> +
>>> + regmap = devm_regmap_init(&pdev->dev, NULL, base, &adda_pr_regmap_cfg);
>>> + if (IS_ERR(regmap)) {
>>> + dev_err(&pdev->dev, "Failed to create regmap\n");
>>> + return PTR_ERR(regmap);
>>> + }
>>> +
>>> + return devm_snd_soc_register_component(&pdev->dev,
>>> + &sun8i_codec_analog_cmpnt_drv,
>>> + NULL, 0);
>>> +}
>>> +
>>> +static struct platform_driver sun8i_codec_analog_driver = {
>>> + .driver = {
>>> + .name = "sun8i-codec-analog",
>>> + .of_match_table = sun8i_codec_analog_of_match,
>>> + },
>>> + .probe = sun8i_codec_analog_probe,
>>> +};
>>> +module_platform_driver(sun8i_codec_analog_driver);
>>> +
>>> +MODULE_DESCRIPTION("Allwinner internal codec analog controls driver");
>>> +MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_ALIAS("platform:sun8i-codec-analog");
>>> --
>>> 2.10.2
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> linux-arm-kernel@lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 02/10] ASoC: sunxi: Add support for A23/A33/H3 codec's analog path controls
From: Chen-Yu Tsai @ 2016-11-25 5:45 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Mark Rutland, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, Mark Brown, Liam Girdwood,
linux-kernel@vger.kernel.org, Chen-Yu Tsai, Rob Herring,
Maxime Ripard, Mylene Josserand, Lee Jones,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <901011480052591@web1j.yandex.ru>
On Fri, Nov 25, 2016 at 1:43 PM, Icenowy Zheng <icenowy@aosc.xyz> wrote:
>
>
> 12.11.2016, 14:57, "Chen-Yu Tsai" <wens@csie.org>:
>> The internal codec on A23/A33/H3 is split into 2 parts. The
>> analog path controls are routed through an embedded custom register
>> bus accessed through the PRCM block.
>>
>> The SoCs share a common set of inputs, outputs, and audio paths.
>> The following table lists the differences.
>>
>> ----------------------------------------
>> | Feature \ SoC | A23 | A33 | H3 |
>> ----------------------------------------
>> | Headphone | v | v | |
>> ----------------------------------------
>> | Line Out | | | v |
>> ----------------------------------------
>> | Phone In/Out | v | v | |
>> ----------------------------------------
>>
>> Add an ASoC component driver for it. This should be tied to the codec
>> audio card as an auxiliary device. This patch adds the commont paths
>> and controls, and variant specific headphone out and line out.
>>
>> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
>> ---
>> sound/soc/sunxi/Kconfig | 8 +
>> sound/soc/sunxi/Makefile | 1 +
>> sound/soc/sunxi/sun8i-codec-analog.c | 665 +++++++++++++++++++++++++++++++++++
>> 3 files changed, 674 insertions(+)
>> create mode 100644 sound/soc/sunxi/sun8i-codec-analog.c
>>
>> diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
>> index dd2368297fd3..6c344e16aca4 100644
>> --- a/sound/soc/sunxi/Kconfig
>> +++ b/sound/soc/sunxi/Kconfig
>> @@ -9,6 +9,14 @@ config SND_SUN4I_CODEC
>> Select Y or M to add support for the Codec embedded in the Allwinner
>> A10 and affiliated SoCs.
>>
>> +config SND_SUN8I_CODEC_ANALOG
>> + tristate "Allwinner sun8i Codec Analog Controls Support"
>> + depends on MACH_SUN8I || COMPILE_TEST
>
> sun50i-a64 has a similar (or the same?) codec to A33.
>
I think the register offsets/fields were moved around again.
Why does Allwinner always do that... :/
ChenYu
>> + select REGMAP
>> + help
>> + Say Y or M if you want to add support for the analog controls for
>> + the codec embedded in newer Allwinner SoCs.
>> +
>> config SND_SUN4I_I2S
>> tristate "Allwinner A10 I2S Support"
>> select SND_SOC_GENERIC_DMAENGINE_PCM
>> diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
>> index 604c7b842837..241c0df9ca0c 100644
>> --- a/sound/soc/sunxi/Makefile
>> +++ b/sound/soc/sunxi/Makefile
>> @@ -1,3 +1,4 @@
>> obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
>> obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
>> obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
>> +obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o
>> diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c
>> new file mode 100644
>> index 000000000000..222bbd440b1e
>> --- /dev/null
>> +++ b/sound/soc/sunxi/sun8i-codec-analog.c
>> @@ -0,0 +1,665 @@
>> +/*
>> + * This driver supports the analog controls for the internal codec
>> + * found in Allwinner's A31s, A23, A33 and H3 SoCs.
>> + *
>> + * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +
>> +#include <sound/soc.h>
>> +#include <sound/soc-dapm.h>
>> +#include <sound/tlv.h>
>> +
>> +/* Codec analog control register offsets and bit fields */
>> +#define SUN8I_ADDA_HP_VOLC 0x00
>> +#define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE 7
>> +#define SUN8I_ADDA_HP_VOLC_HP_VOL 0
>> +#define SUN8I_ADDA_LOMIXSC 0x01
>> +#define SUN8I_ADDA_LOMIXSC_MIC1 6
>> +#define SUN8I_ADDA_LOMIXSC_MIC2 5
>> +#define SUN8I_ADDA_LOMIXSC_PHONE 4
>> +#define SUN8I_ADDA_LOMIXSC_PHONEN 3
>> +#define SUN8I_ADDA_LOMIXSC_LINEINL 2
>> +#define SUN8I_ADDA_LOMIXSC_DACL 1
>> +#define SUN8I_ADDA_LOMIXSC_DACR 0
>> +#define SUN8I_ADDA_ROMIXSC 0x02
>> +#define SUN8I_ADDA_ROMIXSC_MIC1 6
>> +#define SUN8I_ADDA_ROMIXSC_MIC2 5
>> +#define SUN8I_ADDA_ROMIXSC_PHONE 4
>> +#define SUN8I_ADDA_ROMIXSC_PHONEP 3
>> +#define SUN8I_ADDA_ROMIXSC_LINEINR 2
>> +#define SUN8I_ADDA_ROMIXSC_DACR 1
>> +#define SUN8I_ADDA_ROMIXSC_DACL 0
>> +#define SUN8I_ADDA_DAC_PA_SRC 0x03
>> +#define SUN8I_ADDA_DAC_PA_SRC_DACAREN 7
>> +#define SUN8I_ADDA_DAC_PA_SRC_DACALEN 6
>> +#define SUN8I_ADDA_DAC_PA_SRC_RMIXEN 5
>> +#define SUN8I_ADDA_DAC_PA_SRC_LMIXEN 4
>> +#define SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE 3
>> +#define SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE 2
>> +#define SUN8I_ADDA_DAC_PA_SRC_RHPIS 1
>> +#define SUN8I_ADDA_DAC_PA_SRC_LHPIS 0
>> +#define SUN8I_ADDA_PHONEIN_GCTRL 0x04
>> +#define SUN8I_ADDA_PHONEIN_GCTRL_PHONEPG 4
>> +#define SUN8I_ADDA_PHONEIN_GCTRL_PHONENG 0
>> +#define SUN8I_ADDA_LINEIN_GCTRL 0x05
>> +#define SUN8I_ADDA_LINEIN_GCTRL_LINEING 4
>> +#define SUN8I_ADDA_LINEIN_GCTRL_PHONEG 0
>> +#define SUN8I_ADDA_MICIN_GCTRL 0x06
>> +#define SUN8I_ADDA_MICIN_GCTRL_MIC1G 4
>> +#define SUN8I_ADDA_MICIN_GCTRL_MIC2G 0
>> +#define SUN8I_ADDA_PAEN_HP_CTRL 0x07
>> +#define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN 7
>> +#define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN 7 /* H3 specific */
>> +#define SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC 5
>> +#define SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN 4
>> +#define SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL 2
>> +#define SUN8I_ADDA_PAEN_HP_CTRL_LTRNMUTE 1
>> +#define SUN8I_ADDA_PAEN_HP_CTRL_RTLNMUTE 0
>> +#define SUN8I_ADDA_PHONEOUT_CTRL 0x08
>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTG 5
>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTEN 4
>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC1 3
>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC2 2
>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_RMIX 1
>> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_LMIX 0
>> +#define SUN8I_ADDA_PHONE_GAIN_CTRL 0x09
>> +#define SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL 3
>> +#define SUN8I_ADDA_PHONE_GAIN_CTRL_PHONEPREG 0
>> +#define SUN8I_ADDA_MIC2G_CTRL 0x0a
>> +#define SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN 7
>> +#define SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST 4
>> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN 3
>> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN 2
>> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC 1
>> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC 0
>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL 0x0b
>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN 7
>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN 6
>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE 5
>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN 3
>> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST 0
>> +#define SUN8I_ADDA_LADCMIXSC 0x0c
>> +#define SUN8I_ADDA_LADCMIXSC_MIC1 6
>> +#define SUN8I_ADDA_LADCMIXSC_MIC2 5
>> +#define SUN8I_ADDA_LADCMIXSC_PHONE 4
>> +#define SUN8I_ADDA_LADCMIXSC_PHONEN 3
>> +#define SUN8I_ADDA_LADCMIXSC_LINEINL 2
>> +#define SUN8I_ADDA_LADCMIXSC_OMIXRL 1
>> +#define SUN8I_ADDA_LADCMIXSC_OMIXRR 0
>> +#define SUN8I_ADDA_RADCMIXSC 0x0d
>> +#define SUN8I_ADDA_RADCMIXSC_MIC1 6
>> +#define SUN8I_ADDA_RADCMIXSC_MIC2 5
>> +#define SUN8I_ADDA_RADCMIXSC_PHONE 4
>> +#define SUN8I_ADDA_RADCMIXSC_PHONEP 3
>> +#define SUN8I_ADDA_RADCMIXSC_LINEINR 2
>> +#define SUN8I_ADDA_RADCMIXSC_OMIXR 1
>> +#define SUN8I_ADDA_RADCMIXSC_OMIXL 0
>> +#define SUN8I_ADDA_RES 0x0e
>> +#define SUN8I_ADDA_RES_MMICBIAS_SEL 4
>> +#define SUN8I_ADDA_RES_PA_ANTI_POP_CTRL 0
>> +#define SUN8I_ADDA_ADC_AP_EN 0x0f
>> +#define SUN8I_ADDA_ADC_AP_EN_ADCREN 7
>> +#define SUN8I_ADDA_ADC_AP_EN_ADCLEN 6
>> +#define SUN8I_ADDA_ADC_AP_EN_ADCG 0
>> +
>> +/* Analog control register access bits */
>> +#define ADDA_PR 0x0 /* PRCM base + 0x1c0 */
>> +#define ADDA_PR_RESET BIT(28)
>> +#define ADDA_PR_WRITE BIT(24)
>> +#define ADDA_PR_ADDR_SHIFT 16
>> +#define ADDA_PR_ADDR_MASK GENMASK(4, 0)
>> +#define ADDA_PR_DATA_IN_SHIFT 8
>> +#define ADDA_PR_DATA_IN_MASK GENMASK(7, 0)
>> +#define ADDA_PR_DATA_OUT_SHIFT 0
>> +#define ADDA_PR_DATA_OUT_MASK GENMASK(7, 0)
>> +
>> +/* regmap access bits */
>> +static int adda_reg_read(void *context, unsigned int reg, unsigned int *val)
>> +{
>> + void __iomem *base = (void __iomem *)context;
>> + u32 tmp;
>> +
>> + /* De-assert reset */
>> + writel(readl(base) | ADDA_PR_RESET, base);
>> +
>> + /* Clear write bit */
>> + writel(readl(base) & ~ADDA_PR_WRITE, base);
>> +
>> + /* Set register address */
>> + tmp = readl(base);
>> + tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
>> + tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
>> + writel(tmp, base);
>> +
>> + /* Read back value */
>> + *val = readl(base) & ADDA_PR_DATA_OUT_MASK;
>> +
>> + return 0;
>> +}
>> +
>> +static int adda_reg_write(void *context, unsigned int reg, unsigned int val)
>> +{
>> + void __iomem *base = (void __iomem *)context;
>> + u32 tmp;
>> +
>> + /* De-assert reset */
>> + writel(readl(base) | ADDA_PR_RESET, base);
>> +
>> + /* Set register address */
>> + tmp = readl(base);
>> + tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
>> + tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
>> + writel(tmp, base);
>> +
>> + /* Set data to write */
>> + tmp = readl(base);
>> + tmp &= ~(ADDA_PR_DATA_IN_MASK << ADDA_PR_DATA_IN_SHIFT);
>> + tmp |= (val & ADDA_PR_DATA_IN_MASK) << ADDA_PR_DATA_IN_SHIFT;
>> + writel(tmp, base);
>> +
>> + /* Set write bit to signal a write */
>> + writel(readl(base) | ADDA_PR_WRITE, base);
>> +
>> + /* Clear write bit */
>> + writel(readl(base) & ~ADDA_PR_WRITE, base);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct regmap_config adda_pr_regmap_cfg = {
>> + .name = "adda-pr",
>> + .reg_bits = 5,
>> + .reg_stride = 1,
>> + .val_bits = 8,
>> + .reg_read = adda_reg_read,
>> + .reg_write = adda_reg_write,
>> + .fast_io = true,
>> + .max_register = 24,
>> +};
>> +
>> +/* mixer controls */
>> +static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = {
>> + SOC_DAPM_DOUBLE_R("DAC Playback Switch",
>> + SUN8I_ADDA_LOMIXSC,
>> + SUN8I_ADDA_ROMIXSC,
>> + SUN8I_ADDA_LOMIXSC_DACL, 1, 0),
>> + SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
>> + SUN8I_ADDA_LOMIXSC,
>> + SUN8I_ADDA_ROMIXSC,
>> + SUN8I_ADDA_LOMIXSC_DACR, 1, 0),
>> + SOC_DAPM_DOUBLE_R("Line In Playback Switch",
>> + SUN8I_ADDA_LOMIXSC,
>> + SUN8I_ADDA_ROMIXSC,
>> + SUN8I_ADDA_LOMIXSC_LINEINL, 1, 0),
>> + SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
>> + SUN8I_ADDA_LOMIXSC,
>> + SUN8I_ADDA_ROMIXSC,
>> + SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),
>> + SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
>> + SUN8I_ADDA_LOMIXSC,
>> + SUN8I_ADDA_ROMIXSC,
>> + SUN8I_ADDA_LOMIXSC_MIC2, 1, 0),
>> +};
>> +
>> +/* ADC mixer controls */
>> +static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = {
>> + SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
>> + SUN8I_ADDA_LADCMIXSC,
>> + SUN8I_ADDA_RADCMIXSC,
>> + SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),
>> + SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
>> + SUN8I_ADDA_LADCMIXSC,
>> + SUN8I_ADDA_RADCMIXSC,
>> + SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),
>> + SOC_DAPM_DOUBLE_R("Line In Capture Switch",
>> + SUN8I_ADDA_LADCMIXSC,
>> + SUN8I_ADDA_RADCMIXSC,
>> + SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0),
>> + SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
>> + SUN8I_ADDA_LADCMIXSC,
>> + SUN8I_ADDA_RADCMIXSC,
>> + SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),
>> + SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
>> + SUN8I_ADDA_LADCMIXSC,
>> + SUN8I_ADDA_RADCMIXSC,
>> + SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0),
>> +};
>> +
>> +/* volume / mute controls */
>> +static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale,
>> + -450, 150, 0);
>> +static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale,
>> + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
>> + 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
>> +);
>> +
>> +static const struct snd_kcontrol_new sun8i_codec_common_controls[] = {
>> + /* Mixer pre-gains */
>> + SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
>> + SUN8I_ADDA_LINEIN_GCTRL_LINEING,
>> + 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
>> + SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL,
>> + SUN8I_ADDA_MICIN_GCTRL_MIC1G,
>> + 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
>> + SOC_SINGLE_TLV("Mic2 Playback Volume",
>> + SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
>> + 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
>> +
>> + /* Microphone Amp boost gains */
>> + SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
>> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0,
>> + sun8i_codec_mic_gain_scale),
>> + SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
>> + SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
>> + sun8i_codec_mic_gain_scale),
>> +
>> + /* ADC */
>> + SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN,
>> + SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0,
>> + sun8i_codec_out_mixer_pregain_scale),
>> +};
>> +
>> +static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
>> + /* ADC */
>> + SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
>> + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0),
>> + SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
>> + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0),
>> +
>> + /* DAC */
>> + SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
>> + SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0),
>> + SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
>> + SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0),
>> + /*
>> + * Due to this component and the codec belonging to separate DAPM
>> + * contexts, we need to manually link the above widgets to their
>> + * stream widgets at the card level.
>> + */
>> +
>> + /* Line In */
>> + SND_SOC_DAPM_INPUT("LINEIN"),
>> +
>> + /* Microphone inputs */
>> + SND_SOC_DAPM_INPUT("MIC1"),
>> + SND_SOC_DAPM_INPUT("MIC2"),
>> +
>> + /* Microphone Bias */
>> + SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
>> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN,
>> + 0, NULL, 0),
>> +
>> + /* Mic input path */
>> + SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
>> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),
>> + SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
>> + SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
>> +
>> + /* Mixers */
>> + SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
>> + SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
>> + sun8i_codec_mixer_controls,
>> + ARRAY_SIZE(sun8i_codec_mixer_controls)),
>> + SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,
>> + SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,
>> + sun8i_codec_mixer_controls,
>> + ARRAY_SIZE(sun8i_codec_mixer_controls)),
>> + SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
>> + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,
>> + sun8i_codec_adc_mixer_controls,
>> + ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
>> + SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
>> + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,
>> + sun8i_codec_adc_mixer_controls,
>> + ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
>> +};
>> +
>> +static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {
>> + /* Microphone Routes */
>> + { "Mic1 Amplifier", NULL, "MIC1"},
>> + { "Mic2 Amplifier", NULL, "MIC2"},
>> +
>> + /* Left Mixer Routes */
>> + { "Left Mixer", "DAC Playback Switch", "Left DAC" },
>> + { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
>> + { "Left Mixer", "Line In Playback Switch", "LINEIN" },
>> + { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
>> + { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
>> +
>> + /* Right Mixer Routes */
>> + { "Right Mixer", "DAC Playback Switch", "Right DAC" },
>> + { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
>> + { "Right Mixer", "Line In Playback Switch", "LINEIN" },
>> + { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
>> + { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
>> +
>> + /* Left ADC Mixer Routes */
>> + { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
>> + { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
>> + { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
>> + { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
>> + { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
>> +
>> + /* Right ADC Mixer Routes */
>> + { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
>> + { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
>> + { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
>> + { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
>> + { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
>> +
>> + /* ADC Routes */
>> + { "Left ADC", NULL, "Left ADC Mixer" },
>> + { "Right ADC", NULL, "Right ADC Mixer" },
>> +};
>> +
>> +/* headphone specific controls, widgets, and routes */
>> +static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1);
>> +static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = {
>> + SOC_SINGLE_TLV("Headphone Playback Volume",
>> + SUN8I_ADDA_HP_VOLC,
>> + SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0,
>> + sun8i_codec_hp_vol_scale),
>> + SOC_DOUBLE("Headphone Playback Switch",
>> + SUN8I_ADDA_DAC_PA_SRC,
>> + SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE,
>> + SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0),
>> +};
>> +
>> +static const char * const sun8i_codec_hp_src_enum_text[] = {
>> + "DAC", "Mixer",
>> +};
>> +
>> +static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum,
>> + SUN8I_ADDA_DAC_PA_SRC,
>> + SUN8I_ADDA_DAC_PA_SRC_LHPIS,
>> + SUN8I_ADDA_DAC_PA_SRC_RHPIS,
>> + sun8i_codec_hp_src_enum_text);
>> +
>> +static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {
>> + SOC_DAPM_ENUM("Headphone Source Playback Route",
>> + sun8i_codec_hp_src_enum),
>> +};
>> +
>> +static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {
>> + SND_SOC_DAPM_MUX("Headphone Source Playback Route",
>> + SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),
>> + SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
>> + SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0),
>> + SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,
>> + SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),
>> + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,
>> + SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0),
>> + SND_SOC_DAPM_OUTPUT("HP"),
>> +};
>> +
>> +static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = {
>> + { "Headphone Source Playback Route", "DAC", "Left DAC" },
>> + { "Headphone Source Playback Route", "DAC", "Right DAC" },
>> + { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
>> + { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
>> + { "Headphone Amp", NULL, "Headphone Source Playback Route" },
>> + { "HPCOM", NULL, "HPCOM Protection" },
>> + { "HP", NULL, "Headphone Amp" },
>> +};
>> +
>> +static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt)
>> +{
>> + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
>> + struct device *dev = cmpnt->dev;
>> + int ret;
>> +
>> + ret = snd_soc_add_component_controls(cmpnt,
>> + sun8i_codec_headphone_controls,
>> + ARRAY_SIZE(sun8i_codec_headphone_controls));
>> + if (ret) {
>> + dev_err(dev, "Failed to add Headphone controls: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets,
>> + ARRAY_SIZE(sun8i_codec_headphone_widgets));
>> + if (ret) {
>> + dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes,
>> + ARRAY_SIZE(sun8i_codec_headphone_routes));
>> + if (ret) {
>> + dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/* hmic specific widget */
>> +static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = {
>> + SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
>> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN,
>> + 0, NULL, 0),
>> +};
>> +
>> +static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt)
>> +{
>> + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
>> + struct device *dev = cmpnt->dev;
>> + int ret;
>> +
>> + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets,
>> + ARRAY_SIZE(sun8i_codec_hmic_widgets));
>> + if (ret)
>> + dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret);
>> +
>> + return ret;
>> +}
>> +
>> +/* line out specific controls, widgets and routes */
>> +static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale,
>> + 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
>> + 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
>> +);
>> +static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = {
>> + SOC_SINGLE_TLV("Line Out Playback Volume",
>> + SUN8I_ADDA_PHONE_GAIN_CTRL,
>> + SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0,
>> + sun8i_codec_lineout_vol_scale),
>> + SOC_DOUBLE("Line Out Playback Switch",
>> + SUN8I_ADDA_MIC2G_CTRL,
>> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN,
>> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0),
>> +};
>> +
>> +static const char * const sun8i_codec_lineout_src_enum_text[] = {
>> + "Stereo", "Mono Differential",
>> +};
>> +
>> +static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum,
>> + SUN8I_ADDA_MIC2G_CTRL,
>> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC,
>> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC,
>> + sun8i_codec_lineout_src_enum_text);
>> +
>> +static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = {
>> + SOC_DAPM_ENUM("Line Out Source Playback Route",
>> + sun8i_codec_lineout_src_enum),
>> +};
>> +
>> +static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = {
>> + SND_SOC_DAPM_MUX("Line Out Source Playback Route",
>> + SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src),
>> + /* It is unclear if this is a buffer or gate, model it as a supply */
>> + SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL,
>> + SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0),
>> + SND_SOC_DAPM_OUTPUT("LINEOUT"),
>> +};
>> +
>> +static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = {
>> + { "Line Out Source Playback Route", "Stereo", "Left Mixer" },
>> + { "Line Out Source Playback Route", "Stereo", "Right Mixer" },
>> + { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
>> + { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
>> + { "LINEOUT", NULL, "Line Out Source Playback Route" },
>> + { "LINEOUT", NULL, "Line Out Enable", },
>> +};
>> +
>> +static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt)
>> +{
>> + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
>> + struct device *dev = cmpnt->dev;
>> + int ret;
>> +
>> + ret = snd_soc_add_component_controls(cmpnt,
>> + sun8i_codec_lineout_controls,
>> + ARRAY_SIZE(sun8i_codec_lineout_controls));
>> + if (ret) {
>> + dev_err(dev, "Failed to add Line Out controls: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets,
>> + ARRAY_SIZE(sun8i_codec_lineout_widgets));
>> + if (ret) {
>> + dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes,
>> + ARRAY_SIZE(sun8i_codec_lineout_routes));
>> + if (ret) {
>> + dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +struct sun8i_codec_analog_quirks {
>> + bool has_headphone;
>> + bool has_hmic;
>> + bool has_lineout;
>> +};
>> +
>> +static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
>> + .has_headphone = true,
>> + .has_hmic = true,
>> +};
>> +
>> +static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {
>> + .has_lineout = true,
>> +};
>> +
>> +static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
>> +{
>> + struct device *dev = cmpnt->dev;
>> + const struct sun8i_codec_analog_quirks *quirks;
>> + int ret;
>> +
>> + /*
>> + * This would never return NULL unless someone directly registers a
>> + * platform device matching this driver's name, without specifying a
>> + * device tree node.
>> + */
>> + quirks = of_device_get_match_data(dev);
>> +
>> + /* Add controls, widgets, and routes for individual features */
>> +
>> + if (quirks->has_headphone) {
>> + ret = sun8i_codec_add_headphone(cmpnt);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + if (quirks->has_hmic) {
>> + sun8i_codec_add_hmic(cmpnt);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + if (quirks->has_lineout) {
>> + ret = sun8i_codec_add_lineout(cmpnt);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = {
>> + .controls = sun8i_codec_common_controls,
>> + .num_controls = ARRAY_SIZE(sun8i_codec_common_controls),
>> + .dapm_widgets = sun8i_codec_common_widgets,
>> + .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_common_widgets),
>> + .dapm_routes = sun8i_codec_common_routes,
>> + .num_dapm_routes = ARRAY_SIZE(sun8i_codec_common_routes),
>> + .probe = sun8i_codec_analog_cmpnt_probe,
>> +};
>> +
>> +static const struct of_device_id sun8i_codec_analog_of_match[] = {
>> + {
>> + .compatible = "allwinner,sun8i-a23-codec-analog",
>> + .data = &sun8i_a23_quirks,
>> + },
>> + {
>> + .compatible = "allwinner,sun8i-h3-codec-analog",
>> + .data = &sun8i_h3_quirks,
>> + },
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);
>> +
>> +static int sun8i_codec_analog_probe(struct platform_device *pdev)
>> +{
>> + struct resource *res;
>> + struct regmap *regmap;
>> + void __iomem *base;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + base = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(base)) {
>> + dev_err(&pdev->dev, "Failed to map the registers\n");
>> + return PTR_ERR(base);
>> + }
>> +
>> + regmap = devm_regmap_init(&pdev->dev, NULL, base, &adda_pr_regmap_cfg);
>> + if (IS_ERR(regmap)) {
>> + dev_err(&pdev->dev, "Failed to create regmap\n");
>> + return PTR_ERR(regmap);
>> + }
>> +
>> + return devm_snd_soc_register_component(&pdev->dev,
>> + &sun8i_codec_analog_cmpnt_drv,
>> + NULL, 0);
>> +}
>> +
>> +static struct platform_driver sun8i_codec_analog_driver = {
>> + .driver = {
>> + .name = "sun8i-codec-analog",
>> + .of_match_table = sun8i_codec_analog_of_match,
>> + },
>> + .probe = sun8i_codec_analog_probe,
>> +};
>> +module_platform_driver(sun8i_codec_analog_driver);
>> +
>> +MODULE_DESCRIPTION("Allwinner internal codec analog controls driver");
>> +MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:sun8i-codec-analog");
>> --
>> 2.10.2
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 02/10] ASoC: sunxi: Add support for A23/A33/H3 codec's analog path controls
From: Icenowy Zheng @ 2016-11-25 5:43 UTC (permalink / raw)
To: Chen-Yu Tsai, Liam Girdwood, Mark Brown, Maxime Ripard, Lee Jones,
Rob Herring, Mark Rutland
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
Mylene Josserand, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <20161112064648.26779-3-wens@csie.org>
12.11.2016, 14:57, "Chen-Yu Tsai" <wens@csie.org>:
> The internal codec on A23/A33/H3 is split into 2 parts. The
> analog path controls are routed through an embedded custom register
> bus accessed through the PRCM block.
>
> The SoCs share a common set of inputs, outputs, and audio paths.
> The following table lists the differences.
>
> ----------------------------------------
> | Feature \ SoC | A23 | A33 | H3 |
> ----------------------------------------
> | Headphone | v | v | |
> ----------------------------------------
> | Line Out | | | v |
> ----------------------------------------
> | Phone In/Out | v | v | |
> ----------------------------------------
>
> Add an ASoC component driver for it. This should be tied to the codec
> audio card as an auxiliary device. This patch adds the commont paths
> and controls, and variant specific headphone out and line out.
>
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> ---
> sound/soc/sunxi/Kconfig | 8 +
> sound/soc/sunxi/Makefile | 1 +
> sound/soc/sunxi/sun8i-codec-analog.c | 665 +++++++++++++++++++++++++++++++++++
> 3 files changed, 674 insertions(+)
> create mode 100644 sound/soc/sunxi/sun8i-codec-analog.c
>
> diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
> index dd2368297fd3..6c344e16aca4 100644
> --- a/sound/soc/sunxi/Kconfig
> +++ b/sound/soc/sunxi/Kconfig
> @@ -9,6 +9,14 @@ config SND_SUN4I_CODEC
> Select Y or M to add support for the Codec embedded in the Allwinner
> A10 and affiliated SoCs.
>
> +config SND_SUN8I_CODEC_ANALOG
> + tristate "Allwinner sun8i Codec Analog Controls Support"
> + depends on MACH_SUN8I || COMPILE_TEST
sun50i-a64 has a similar (or the same?) codec to A33.
> + select REGMAP
> + help
> + Say Y or M if you want to add support for the analog controls for
> + the codec embedded in newer Allwinner SoCs.
> +
> config SND_SUN4I_I2S
> tristate "Allwinner A10 I2S Support"
> select SND_SOC_GENERIC_DMAENGINE_PCM
> diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
> index 604c7b842837..241c0df9ca0c 100644
> --- a/sound/soc/sunxi/Makefile
> +++ b/sound/soc/sunxi/Makefile
> @@ -1,3 +1,4 @@
> obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
> obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
> obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
> +obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o
> diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c
> new file mode 100644
> index 000000000000..222bbd440b1e
> --- /dev/null
> +++ b/sound/soc/sunxi/sun8i-codec-analog.c
> @@ -0,0 +1,665 @@
> +/*
> + * This driver supports the analog controls for the internal codec
> + * found in Allwinner's A31s, A23, A33 and H3 SoCs.
> + *
> + * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
> + *
> + * 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.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/tlv.h>
> +
> +/* Codec analog control register offsets and bit fields */
> +#define SUN8I_ADDA_HP_VOLC 0x00
> +#define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE 7
> +#define SUN8I_ADDA_HP_VOLC_HP_VOL 0
> +#define SUN8I_ADDA_LOMIXSC 0x01
> +#define SUN8I_ADDA_LOMIXSC_MIC1 6
> +#define SUN8I_ADDA_LOMIXSC_MIC2 5
> +#define SUN8I_ADDA_LOMIXSC_PHONE 4
> +#define SUN8I_ADDA_LOMIXSC_PHONEN 3
> +#define SUN8I_ADDA_LOMIXSC_LINEINL 2
> +#define SUN8I_ADDA_LOMIXSC_DACL 1
> +#define SUN8I_ADDA_LOMIXSC_DACR 0
> +#define SUN8I_ADDA_ROMIXSC 0x02
> +#define SUN8I_ADDA_ROMIXSC_MIC1 6
> +#define SUN8I_ADDA_ROMIXSC_MIC2 5
> +#define SUN8I_ADDA_ROMIXSC_PHONE 4
> +#define SUN8I_ADDA_ROMIXSC_PHONEP 3
> +#define SUN8I_ADDA_ROMIXSC_LINEINR 2
> +#define SUN8I_ADDA_ROMIXSC_DACR 1
> +#define SUN8I_ADDA_ROMIXSC_DACL 0
> +#define SUN8I_ADDA_DAC_PA_SRC 0x03
> +#define SUN8I_ADDA_DAC_PA_SRC_DACAREN 7
> +#define SUN8I_ADDA_DAC_PA_SRC_DACALEN 6
> +#define SUN8I_ADDA_DAC_PA_SRC_RMIXEN 5
> +#define SUN8I_ADDA_DAC_PA_SRC_LMIXEN 4
> +#define SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE 3
> +#define SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE 2
> +#define SUN8I_ADDA_DAC_PA_SRC_RHPIS 1
> +#define SUN8I_ADDA_DAC_PA_SRC_LHPIS 0
> +#define SUN8I_ADDA_PHONEIN_GCTRL 0x04
> +#define SUN8I_ADDA_PHONEIN_GCTRL_PHONEPG 4
> +#define SUN8I_ADDA_PHONEIN_GCTRL_PHONENG 0
> +#define SUN8I_ADDA_LINEIN_GCTRL 0x05
> +#define SUN8I_ADDA_LINEIN_GCTRL_LINEING 4
> +#define SUN8I_ADDA_LINEIN_GCTRL_PHONEG 0
> +#define SUN8I_ADDA_MICIN_GCTRL 0x06
> +#define SUN8I_ADDA_MICIN_GCTRL_MIC1G 4
> +#define SUN8I_ADDA_MICIN_GCTRL_MIC2G 0
> +#define SUN8I_ADDA_PAEN_HP_CTRL 0x07
> +#define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN 7
> +#define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN 7 /* H3 specific */
> +#define SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC 5
> +#define SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN 4
> +#define SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL 2
> +#define SUN8I_ADDA_PAEN_HP_CTRL_LTRNMUTE 1
> +#define SUN8I_ADDA_PAEN_HP_CTRL_RTLNMUTE 0
> +#define SUN8I_ADDA_PHONEOUT_CTRL 0x08
> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTG 5
> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTEN 4
> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC1 3
> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC2 2
> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_RMIX 1
> +#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_LMIX 0
> +#define SUN8I_ADDA_PHONE_GAIN_CTRL 0x09
> +#define SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL 3
> +#define SUN8I_ADDA_PHONE_GAIN_CTRL_PHONEPREG 0
> +#define SUN8I_ADDA_MIC2G_CTRL 0x0a
> +#define SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN 7
> +#define SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST 4
> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN 3
> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN 2
> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC 1
> +#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC 0
> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL 0x0b
> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN 7
> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN 6
> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE 5
> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN 3
> +#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST 0
> +#define SUN8I_ADDA_LADCMIXSC 0x0c
> +#define SUN8I_ADDA_LADCMIXSC_MIC1 6
> +#define SUN8I_ADDA_LADCMIXSC_MIC2 5
> +#define SUN8I_ADDA_LADCMIXSC_PHONE 4
> +#define SUN8I_ADDA_LADCMIXSC_PHONEN 3
> +#define SUN8I_ADDA_LADCMIXSC_LINEINL 2
> +#define SUN8I_ADDA_LADCMIXSC_OMIXRL 1
> +#define SUN8I_ADDA_LADCMIXSC_OMIXRR 0
> +#define SUN8I_ADDA_RADCMIXSC 0x0d
> +#define SUN8I_ADDA_RADCMIXSC_MIC1 6
> +#define SUN8I_ADDA_RADCMIXSC_MIC2 5
> +#define SUN8I_ADDA_RADCMIXSC_PHONE 4
> +#define SUN8I_ADDA_RADCMIXSC_PHONEP 3
> +#define SUN8I_ADDA_RADCMIXSC_LINEINR 2
> +#define SUN8I_ADDA_RADCMIXSC_OMIXR 1
> +#define SUN8I_ADDA_RADCMIXSC_OMIXL 0
> +#define SUN8I_ADDA_RES 0x0e
> +#define SUN8I_ADDA_RES_MMICBIAS_SEL 4
> +#define SUN8I_ADDA_RES_PA_ANTI_POP_CTRL 0
> +#define SUN8I_ADDA_ADC_AP_EN 0x0f
> +#define SUN8I_ADDA_ADC_AP_EN_ADCREN 7
> +#define SUN8I_ADDA_ADC_AP_EN_ADCLEN 6
> +#define SUN8I_ADDA_ADC_AP_EN_ADCG 0
> +
> +/* Analog control register access bits */
> +#define ADDA_PR 0x0 /* PRCM base + 0x1c0 */
> +#define ADDA_PR_RESET BIT(28)
> +#define ADDA_PR_WRITE BIT(24)
> +#define ADDA_PR_ADDR_SHIFT 16
> +#define ADDA_PR_ADDR_MASK GENMASK(4, 0)
> +#define ADDA_PR_DATA_IN_SHIFT 8
> +#define ADDA_PR_DATA_IN_MASK GENMASK(7, 0)
> +#define ADDA_PR_DATA_OUT_SHIFT 0
> +#define ADDA_PR_DATA_OUT_MASK GENMASK(7, 0)
> +
> +/* regmap access bits */
> +static int adda_reg_read(void *context, unsigned int reg, unsigned int *val)
> +{
> + void __iomem *base = (void __iomem *)context;
> + u32 tmp;
> +
> + /* De-assert reset */
> + writel(readl(base) | ADDA_PR_RESET, base);
> +
> + /* Clear write bit */
> + writel(readl(base) & ~ADDA_PR_WRITE, base);
> +
> + /* Set register address */
> + tmp = readl(base);
> + tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
> + tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
> + writel(tmp, base);
> +
> + /* Read back value */
> + *val = readl(base) & ADDA_PR_DATA_OUT_MASK;
> +
> + return 0;
> +}
> +
> +static int adda_reg_write(void *context, unsigned int reg, unsigned int val)
> +{
> + void __iomem *base = (void __iomem *)context;
> + u32 tmp;
> +
> + /* De-assert reset */
> + writel(readl(base) | ADDA_PR_RESET, base);
> +
> + /* Set register address */
> + tmp = readl(base);
> + tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
> + tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
> + writel(tmp, base);
> +
> + /* Set data to write */
> + tmp = readl(base);
> + tmp &= ~(ADDA_PR_DATA_IN_MASK << ADDA_PR_DATA_IN_SHIFT);
> + tmp |= (val & ADDA_PR_DATA_IN_MASK) << ADDA_PR_DATA_IN_SHIFT;
> + writel(tmp, base);
> +
> + /* Set write bit to signal a write */
> + writel(readl(base) | ADDA_PR_WRITE, base);
> +
> + /* Clear write bit */
> + writel(readl(base) & ~ADDA_PR_WRITE, base);
> +
> + return 0;
> +}
> +
> +static const struct regmap_config adda_pr_regmap_cfg = {
> + .name = "adda-pr",
> + .reg_bits = 5,
> + .reg_stride = 1,
> + .val_bits = 8,
> + .reg_read = adda_reg_read,
> + .reg_write = adda_reg_write,
> + .fast_io = true,
> + .max_register = 24,
> +};
> +
> +/* mixer controls */
> +static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = {
> + SOC_DAPM_DOUBLE_R("DAC Playback Switch",
> + SUN8I_ADDA_LOMIXSC,
> + SUN8I_ADDA_ROMIXSC,
> + SUN8I_ADDA_LOMIXSC_DACL, 1, 0),
> + SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
> + SUN8I_ADDA_LOMIXSC,
> + SUN8I_ADDA_ROMIXSC,
> + SUN8I_ADDA_LOMIXSC_DACR, 1, 0),
> + SOC_DAPM_DOUBLE_R("Line In Playback Switch",
> + SUN8I_ADDA_LOMIXSC,
> + SUN8I_ADDA_ROMIXSC,
> + SUN8I_ADDA_LOMIXSC_LINEINL, 1, 0),
> + SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
> + SUN8I_ADDA_LOMIXSC,
> + SUN8I_ADDA_ROMIXSC,
> + SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),
> + SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
> + SUN8I_ADDA_LOMIXSC,
> + SUN8I_ADDA_ROMIXSC,
> + SUN8I_ADDA_LOMIXSC_MIC2, 1, 0),
> +};
> +
> +/* ADC mixer controls */
> +static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = {
> + SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
> + SUN8I_ADDA_LADCMIXSC,
> + SUN8I_ADDA_RADCMIXSC,
> + SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),
> + SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
> + SUN8I_ADDA_LADCMIXSC,
> + SUN8I_ADDA_RADCMIXSC,
> + SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),
> + SOC_DAPM_DOUBLE_R("Line In Capture Switch",
> + SUN8I_ADDA_LADCMIXSC,
> + SUN8I_ADDA_RADCMIXSC,
> + SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0),
> + SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
> + SUN8I_ADDA_LADCMIXSC,
> + SUN8I_ADDA_RADCMIXSC,
> + SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),
> + SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
> + SUN8I_ADDA_LADCMIXSC,
> + SUN8I_ADDA_RADCMIXSC,
> + SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0),
> +};
> +
> +/* volume / mute controls */
> +static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale,
> + -450, 150, 0);
> +static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale,
> + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
> + 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
> +);
> +
> +static const struct snd_kcontrol_new sun8i_codec_common_controls[] = {
> + /* Mixer pre-gains */
> + SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
> + SUN8I_ADDA_LINEIN_GCTRL_LINEING,
> + 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
> + SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL,
> + SUN8I_ADDA_MICIN_GCTRL_MIC1G,
> + 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
> + SOC_SINGLE_TLV("Mic2 Playback Volume",
> + SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
> + 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
> +
> + /* Microphone Amp boost gains */
> + SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0,
> + sun8i_codec_mic_gain_scale),
> + SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
> + SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
> + sun8i_codec_mic_gain_scale),
> +
> + /* ADC */
> + SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN,
> + SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0,
> + sun8i_codec_out_mixer_pregain_scale),
> +};
> +
> +static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
> + /* ADC */
> + SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
> + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0),
> + SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
> + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0),
> +
> + /* DAC */
> + SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
> + SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0),
> + SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
> + SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0),
> + /*
> + * Due to this component and the codec belonging to separate DAPM
> + * contexts, we need to manually link the above widgets to their
> + * stream widgets at the card level.
> + */
> +
> + /* Line In */
> + SND_SOC_DAPM_INPUT("LINEIN"),
> +
> + /* Microphone inputs */
> + SND_SOC_DAPM_INPUT("MIC1"),
> + SND_SOC_DAPM_INPUT("MIC2"),
> +
> + /* Microphone Bias */
> + SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN,
> + 0, NULL, 0),
> +
> + /* Mic input path */
> + SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),
> + SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
> + SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
> +
> + /* Mixers */
> + SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
> + SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
> + sun8i_codec_mixer_controls,
> + ARRAY_SIZE(sun8i_codec_mixer_controls)),
> + SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,
> + SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,
> + sun8i_codec_mixer_controls,
> + ARRAY_SIZE(sun8i_codec_mixer_controls)),
> + SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
> + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,
> + sun8i_codec_adc_mixer_controls,
> + ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
> + SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
> + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,
> + sun8i_codec_adc_mixer_controls,
> + ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
> +};
> +
> +static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {
> + /* Microphone Routes */
> + { "Mic1 Amplifier", NULL, "MIC1"},
> + { "Mic2 Amplifier", NULL, "MIC2"},
> +
> + /* Left Mixer Routes */
> + { "Left Mixer", "DAC Playback Switch", "Left DAC" },
> + { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
> + { "Left Mixer", "Line In Playback Switch", "LINEIN" },
> + { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
> + { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
> +
> + /* Right Mixer Routes */
> + { "Right Mixer", "DAC Playback Switch", "Right DAC" },
> + { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
> + { "Right Mixer", "Line In Playback Switch", "LINEIN" },
> + { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
> + { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
> +
> + /* Left ADC Mixer Routes */
> + { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
> + { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
> + { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
> + { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
> + { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
> +
> + /* Right ADC Mixer Routes */
> + { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
> + { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
> + { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
> + { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
> + { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
> +
> + /* ADC Routes */
> + { "Left ADC", NULL, "Left ADC Mixer" },
> + { "Right ADC", NULL, "Right ADC Mixer" },
> +};
> +
> +/* headphone specific controls, widgets, and routes */
> +static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1);
> +static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = {
> + SOC_SINGLE_TLV("Headphone Playback Volume",
> + SUN8I_ADDA_HP_VOLC,
> + SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0,
> + sun8i_codec_hp_vol_scale),
> + SOC_DOUBLE("Headphone Playback Switch",
> + SUN8I_ADDA_DAC_PA_SRC,
> + SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE,
> + SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0),
> +};
> +
> +static const char * const sun8i_codec_hp_src_enum_text[] = {
> + "DAC", "Mixer",
> +};
> +
> +static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum,
> + SUN8I_ADDA_DAC_PA_SRC,
> + SUN8I_ADDA_DAC_PA_SRC_LHPIS,
> + SUN8I_ADDA_DAC_PA_SRC_RHPIS,
> + sun8i_codec_hp_src_enum_text);
> +
> +static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {
> + SOC_DAPM_ENUM("Headphone Source Playback Route",
> + sun8i_codec_hp_src_enum),
> +};
> +
> +static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {
> + SND_SOC_DAPM_MUX("Headphone Source Playback Route",
> + SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),
> + SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
> + SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0),
> + SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,
> + SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),
> + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,
> + SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0),
> + SND_SOC_DAPM_OUTPUT("HP"),
> +};
> +
> +static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = {
> + { "Headphone Source Playback Route", "DAC", "Left DAC" },
> + { "Headphone Source Playback Route", "DAC", "Right DAC" },
> + { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
> + { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
> + { "Headphone Amp", NULL, "Headphone Source Playback Route" },
> + { "HPCOM", NULL, "HPCOM Protection" },
> + { "HP", NULL, "Headphone Amp" },
> +};
> +
> +static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt)
> +{
> + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
> + struct device *dev = cmpnt->dev;
> + int ret;
> +
> + ret = snd_soc_add_component_controls(cmpnt,
> + sun8i_codec_headphone_controls,
> + ARRAY_SIZE(sun8i_codec_headphone_controls));
> + if (ret) {
> + dev_err(dev, "Failed to add Headphone controls: %d\n", ret);
> + return ret;
> + }
> +
> + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets,
> + ARRAY_SIZE(sun8i_codec_headphone_widgets));
> + if (ret) {
> + dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret);
> + return ret;
> + }
> +
> + ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes,
> + ARRAY_SIZE(sun8i_codec_headphone_routes));
> + if (ret) {
> + dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/* hmic specific widget */
> +static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = {
> + SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
> + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN,
> + 0, NULL, 0),
> +};
> +
> +static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt)
> +{
> + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
> + struct device *dev = cmpnt->dev;
> + int ret;
> +
> + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets,
> + ARRAY_SIZE(sun8i_codec_hmic_widgets));
> + if (ret)
> + dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret);
> +
> + return ret;
> +}
> +
> +/* line out specific controls, widgets and routes */
> +static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale,
> + 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
> + 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
> +);
> +static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = {
> + SOC_SINGLE_TLV("Line Out Playback Volume",
> + SUN8I_ADDA_PHONE_GAIN_CTRL,
> + SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0,
> + sun8i_codec_lineout_vol_scale),
> + SOC_DOUBLE("Line Out Playback Switch",
> + SUN8I_ADDA_MIC2G_CTRL,
> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN,
> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0),
> +};
> +
> +static const char * const sun8i_codec_lineout_src_enum_text[] = {
> + "Stereo", "Mono Differential",
> +};
> +
> +static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum,
> + SUN8I_ADDA_MIC2G_CTRL,
> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC,
> + SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC,
> + sun8i_codec_lineout_src_enum_text);
> +
> +static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = {
> + SOC_DAPM_ENUM("Line Out Source Playback Route",
> + sun8i_codec_lineout_src_enum),
> +};
> +
> +static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = {
> + SND_SOC_DAPM_MUX("Line Out Source Playback Route",
> + SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src),
> + /* It is unclear if this is a buffer or gate, model it as a supply */
> + SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL,
> + SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0),
> + SND_SOC_DAPM_OUTPUT("LINEOUT"),
> +};
> +
> +static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = {
> + { "Line Out Source Playback Route", "Stereo", "Left Mixer" },
> + { "Line Out Source Playback Route", "Stereo", "Right Mixer" },
> + { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
> + { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
> + { "LINEOUT", NULL, "Line Out Source Playback Route" },
> + { "LINEOUT", NULL, "Line Out Enable", },
> +};
> +
> +static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt)
> +{
> + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
> + struct device *dev = cmpnt->dev;
> + int ret;
> +
> + ret = snd_soc_add_component_controls(cmpnt,
> + sun8i_codec_lineout_controls,
> + ARRAY_SIZE(sun8i_codec_lineout_controls));
> + if (ret) {
> + dev_err(dev, "Failed to add Line Out controls: %d\n", ret);
> + return ret;
> + }
> +
> + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets,
> + ARRAY_SIZE(sun8i_codec_lineout_widgets));
> + if (ret) {
> + dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret);
> + return ret;
> + }
> +
> + ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes,
> + ARRAY_SIZE(sun8i_codec_lineout_routes));
> + if (ret) {
> + dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +struct sun8i_codec_analog_quirks {
> + bool has_headphone;
> + bool has_hmic;
> + bool has_lineout;
> +};
> +
> +static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
> + .has_headphone = true,
> + .has_hmic = true,
> +};
> +
> +static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {
> + .has_lineout = true,
> +};
> +
> +static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
> +{
> + struct device *dev = cmpnt->dev;
> + const struct sun8i_codec_analog_quirks *quirks;
> + int ret;
> +
> + /*
> + * This would never return NULL unless someone directly registers a
> + * platform device matching this driver's name, without specifying a
> + * device tree node.
> + */
> + quirks = of_device_get_match_data(dev);
> +
> + /* Add controls, widgets, and routes for individual features */
> +
> + if (quirks->has_headphone) {
> + ret = sun8i_codec_add_headphone(cmpnt);
> + if (ret)
> + return ret;
> + }
> +
> + if (quirks->has_hmic) {
> + sun8i_codec_add_hmic(cmpnt);
> + if (ret)
> + return ret;
> + }
> +
> + if (quirks->has_lineout) {
> + ret = sun8i_codec_add_lineout(cmpnt);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = {
> + .controls = sun8i_codec_common_controls,
> + .num_controls = ARRAY_SIZE(sun8i_codec_common_controls),
> + .dapm_widgets = sun8i_codec_common_widgets,
> + .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_common_widgets),
> + .dapm_routes = sun8i_codec_common_routes,
> + .num_dapm_routes = ARRAY_SIZE(sun8i_codec_common_routes),
> + .probe = sun8i_codec_analog_cmpnt_probe,
> +};
> +
> +static const struct of_device_id sun8i_codec_analog_of_match[] = {
> + {
> + .compatible = "allwinner,sun8i-a23-codec-analog",
> + .data = &sun8i_a23_quirks,
> + },
> + {
> + .compatible = "allwinner,sun8i-h3-codec-analog",
> + .data = &sun8i_h3_quirks,
> + },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);
> +
> +static int sun8i_codec_analog_probe(struct platform_device *pdev)
> +{
> + struct resource *res;
> + struct regmap *regmap;
> + void __iomem *base;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(base)) {
> + dev_err(&pdev->dev, "Failed to map the registers\n");
> + return PTR_ERR(base);
> + }
> +
> + regmap = devm_regmap_init(&pdev->dev, NULL, base, &adda_pr_regmap_cfg);
> + if (IS_ERR(regmap)) {
> + dev_err(&pdev->dev, "Failed to create regmap\n");
> + return PTR_ERR(regmap);
> + }
> +
> + return devm_snd_soc_register_component(&pdev->dev,
> + &sun8i_codec_analog_cmpnt_drv,
> + NULL, 0);
> +}
> +
> +static struct platform_driver sun8i_codec_analog_driver = {
> + .driver = {
> + .name = "sun8i-codec-analog",
> + .of_match_table = sun8i_codec_analog_of_match,
> + },
> + .probe = sun8i_codec_analog_probe,
> +};
> +module_platform_driver(sun8i_codec_analog_driver);
> +
> +MODULE_DESCRIPTION("Allwinner internal codec analog controls driver");
> +MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:sun8i-codec-analog");
> --
> 2.10.2
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v6 3/5] ARM: dts: sun8i-h3: add HDMI video nodes
From: Icenowy Zheng @ 2016-11-25 5:27 UTC (permalink / raw)
To: moinejf-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org, Dave Airlie,
Maxime Ripard, Rob Herring
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
In-Reply-To: <cdf50b8433e2d9a053e35f8788bfcd5d41504312.1479641523.git.moinejf-GANU6spQydw@public.gmane.org>
20.11.2016, 20:07, "Jean-Francois Moine" <moinejf@free.fr>:
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
> arch/arm/boot/dts/sun8i-h3.dtsi | 51 +++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 51 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
> index 416b825..7c6b1d5 100644
> --- a/arch/arm/boot/dts/sun8i-h3.dtsi
> +++ b/arch/arm/boot/dts/sun8i-h3.dtsi
> @@ -140,6 +140,16 @@
> #size-cells = <1>;
> ranges;
>
> + de: de-controller@01000000 {
> + compatible = "allwinner,sun8i-h3-display-engine";
> + reg = <0x01000000 0x400000>;
> + clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
> + clock-names = "bus", "clock";
> + resets = <&ccu RST_BUS_DE>;
> + ports = <&lcd0_p>;
> + status = "disabled";
> + };
> +
> dma: dma-controller@01c02000 {
> compatible = "allwinner,sun8i-h3-dma";
> reg = <0x01c02000 0x1000>;
> @@ -149,6 +159,23 @@
> #dma-cells = <1>;
> };
>
> + lcd0: lcd-controller@01c0c000 {
> + compatible = "allwinner,sun8i-a83t-tcon";
> + reg = <0x01c0c000 0x400>;
> + clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
> + clock-names = "bus", "clock";
> + resets = <&ccu RST_BUS_TCON0>;
> + interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
> + status = "disabled";
> + #address-cells = <1>;
> + #size-cells = <0>;
> + lcd0_p: port {
> + lcd0_hdmi: endpoint {
> + remote-endpoint = <&hdmi_lcd0>;
> + };
> + };
> + };
> +
> mmc0: mmc@01c0f000 {
> compatible = "allwinner,sun7i-a20-mmc";
> reg = <0x01c0f000 0x1000>;
> @@ -314,6 +341,11 @@
> clock-names = "hosc", "losc";
> #clock-cells = <1>;
> #reset-cells = <1>;
> +
> + assigned-clocks = <&ccu CLK_PLL_DE>,
Cannot get the patch built on 4.9-rc, as CLK_PLL_DE is not an exported clock.
Only CLK_DE is exported.
> + <&ccu CLK_DE>;
> + assigned-clock-rates = <864000000>,
> + <432000000>;
> };
>
> pio: pinctrl@01c20800 {
> @@ -564,6 +596,25 @@
> interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
> };
>
> + hdmi: hdmi@01ee0000 {
> + compatible = "allwinner,sun8i-h3-hdmi";
> + reg = <0x01ee0000 0x20000>;
> + clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
> + <&ccu CLK_HDMI_DDC>;
> + clock-names = "bus", "clock", "ddc-clock";
> + resets = <&ccu RST_BUS_HDMI0>, <&ccu RST_BUS_HDMI1>;
> + reset-names = "hdmi0", "hdmi1";
> + status = "disabled";
> + #address-cells = <1>;
> + #size-cells = <0>;
> + port@0 { /* video */
> + reg = <0>;
> + hdmi_lcd0: endpoint {
> + remote-endpoint = <&lcd0_hdmi>;
> + };
> + };
> + };
> +
> rtc: rtc@01f00000 {
> compatible = "allwinner,sun6i-a31-rtc";
> reg = <0x01f00000 0x54>;
> --
> 2.10.2
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox