* Re: [PATCH 1/2] usb: xhci: add relaxed timing quirk bit
From: Mathias Nyman @ 2017-11-22 15:24 UTC (permalink / raw)
To: Adam Wallis, Rob Herring
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Greg Kroah-Hartman, Mathias Nyman, Linux USB List,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Mark Rutland,
Timur Tabi, Chunfeng Yun
In-Reply-To: <32f8dc7e-9fde-5e45-1570-a9ec372579fa-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
On 22.11.2017 02:07, Adam Wallis wrote:
> On 11/21/2017 3:06 PM, Rob Herring wrote:
>
> [..]
>
>>> I like where you are going with this. Are you saying that I could read for a
>>> device property read from firmware (for DTB or ACPI) like DWC3 does for
>>> "snps,hird-threshold"?
>>
>> Is that for the same thing? If so, drop the vendor prefix and use
>> that. Otherwise, a separate property should really be something that
>> is per board rather than per SoC.
>
> I don't think that's exactly the same property, but it's the same idea I would
> prefer to go with. That way, an integer can be passed in via the firmware tables.
>
>>
>>> If you mean this, where do you recommend I store the
>>> desired IRQ_CONTROL value - in struct xhci_hcd ?
>>
>> No idea.
>>
>>> Or by "compatible" strings, did you mean storing hard-coded values in the
>>> of_device_id usb_xhci_of_match[] array? This would still be hard-coding (which I
>>> would like to avoid) and also would not work for the ACPI case.
>>
>> ACPI has match tables too?
>>
>
> Yes, you can use DSD in a way that is similar to OF properties
>
>> It would only be hardcoded per compatible which should be per SoC. Do
>> you need per board/device tuning? If so, use a property.
>>
>
> The reason why I think it should dynamic via firmware tables is that
>
> * It's much less invasive for vendors to update their DT tables if they need to
> adjust on a per device/controller/family/etc basis then to adjust a properties
> table in xhci-plat
> * This would lead to less polluting in xhci-plat code
>
>> Rob
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
>
> I will provide an updated proposed patch sometime this week. I also hope to get
> some feedback from Mathias to see what he prefers.
We know have at least two hosts/platforms that need custom interrupt moderation values
How about adding a u32 device property for xhci with the interrupt moderation interval in
nanoseconds? And also add a u32 imod_interval variable to struct xhci_hcd?
imod_interval can be set to the current default 40000ns (160*250ns) and overwritten if
device_property_read_u32() returns something else.
XHCI_MTK_HOST could then use whatever preferred device propery interval value,
and we can get rid of using XHCI_MTK_HOST quirk flag when setting up the IMODI
-Mathias
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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: [v3,10/12] hwmon (occ): Add non-hwmon attributes
From: Guenter Roeck @ 2017-11-22 15:23 UTC (permalink / raw)
To: eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-hwmon-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA, jdelvare-IBi9RG/b67k,
corbet-T1hC0tSOHrs, mark.rutland-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, joel-U3u1mxZcP9KHXe+LvDLADg,
Edward A. James
In-Reply-To: <1511222021-562-11-git-send-email-eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
On Mon, Nov 20, 2017 at 05:53:39PM -0600, eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org wrote:
> From: "Edward A. James" <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
>
> Create device attributes for additional OCC properties that do not
> belong as hwmon sensors. These provide additional information as to the
> state of the processor and system.
>
... and those attributes should really not be part of the hwmon driver.
> Signed-off-by: Edward A. James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
> ---
> drivers/hwmon/occ/common.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/hwmon/occ/common.h | 1 +
> drivers/hwmon/occ/p8_i2c.c | 10 +++++
> drivers/hwmon/occ/p9_sbe.c | 2 +
> 4 files changed, 106 insertions(+)
>
> diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
> index 337f286b..53e3592 100644
> --- a/drivers/hwmon/occ/common.c
> +++ b/drivers/hwmon/occ/common.c
> @@ -24,6 +24,14 @@
>
> #define OCC_FRU_TYPE_VRM 0x3
>
> +/* OCC status bits */
> +#define OCC_STAT_MASTER 0x80
> +#define OCC_STAT_ACTIVE 0x01
> +#define OCC_EXT_STAT_DVFS_OT 0x80
> +#define OCC_EXT_STAT_DVFS_POWER 0x40
> +#define OCC_EXT_STAT_MEM_THROTTLE 0x20
> +#define OCC_EXT_STAT_QUICK_DROP 0x10
> +
> /* OCC sensor type and version definitions */
>
> struct temp_sensor_1 {
> @@ -1108,6 +1116,81 @@ static int occ_setup_sensor_attrs(struct occ *occ)
> return 0;
> }
>
> +static ssize_t occ_show_status(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int rc;
> + int val = 0;
> + struct occ *occ = dev_get_drvdata(dev);
> + struct occ_poll_response_header *header;
> + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
> +
> + rc = occ_update_response(occ);
> + if (rc)
> + return rc;
> +
> + header = (struct occ_poll_response_header *)occ->resp.data;
> +
> + switch (sattr->index) {
> + case 0:
> + val = (header->status & OCC_STAT_MASTER) ? 1 : 0;
> + break;
> + case 1:
> + val = (header->status & OCC_STAT_ACTIVE) ? 1 : 0;
> + break;
> + case 2:
> + val = (header->ext_status & OCC_EXT_STAT_DVFS_OT) ? 1 : 0;
> + break;
> + case 3:
> + val = (header->ext_status & OCC_EXT_STAT_DVFS_POWER) ? 1 : 0;
> + break;
> + case 4:
> + val = (header->ext_status & OCC_EXT_STAT_MEM_THROTTLE) ? 1 : 0;
> + break;
> + case 5:
> + val = (header->ext_status & OCC_EXT_STAT_QUICK_DROP) ? 1 : 0;
> + break;
> + case 6:
> + val = header->occ_state;
> + break;
> + case 7:
> + if (header->status & OCC_STAT_MASTER)
> + val = hweight8(header->occs_present);
> + else
> + val = 1;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
> +}
> +
> +static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_show_status, NULL, 0);
> +static SENSOR_DEVICE_ATTR(occ_active, 0444, occ_show_status, NULL, 1);
> +static SENSOR_DEVICE_ATTR(occ_dvfs_ot, 0444, occ_show_status, NULL, 2);
> +static SENSOR_DEVICE_ATTR(occ_dvfs_power, 0444, occ_show_status, NULL, 3);
> +static SENSOR_DEVICE_ATTR(occ_mem_throttle, 0444, occ_show_status, NULL, 4);
> +static SENSOR_DEVICE_ATTR(occ_quick_drop, 0444, occ_show_status, NULL, 5);
> +static SENSOR_DEVICE_ATTR(occ_status, 0444, occ_show_status, NULL, 6);
> +static SENSOR_DEVICE_ATTR(occs_present, 0444, occ_show_status, NULL, 7);
> +
> +static struct attribute *occ_attributes[] = {
> + &sensor_dev_attr_occ_master.dev_attr.attr,
> + &sensor_dev_attr_occ_active.dev_attr.attr,
> + &sensor_dev_attr_occ_dvfs_ot.dev_attr.attr,
> + &sensor_dev_attr_occ_dvfs_power.dev_attr.attr,
> + &sensor_dev_attr_occ_mem_throttle.dev_attr.attr,
> + &sensor_dev_attr_occ_quick_drop.dev_attr.attr,
> + &sensor_dev_attr_occ_status.dev_attr.attr,
> + &sensor_dev_attr_occs_present.dev_attr.attr,
> + NULL
> +};
> +
> +static const struct attribute_group occ_attr_group = {
> + .attrs = occ_attributes,
> +};
> +
> /* only need to do this once at startup, as OCC won't change sensors on us */
> static void occ_parse_poll_response(struct occ *occ)
> {
> @@ -1188,5 +1271,15 @@ int occ_setup(struct occ *occ, const char *name)
> return rc;
> }
>
> + rc = sysfs_create_group(&occ->bus_dev->kobj, &occ_attr_group);
> + if (rc)
> + dev_warn(occ->bus_dev, "failed to create status attrs: %d\n",
> + rc);
> +
> return 0;
> }
> +
> +void occ_shutdown(struct occ *occ)
> +{
> + sysfs_remove_group(&occ->bus_dev->kobj, &occ_attr_group);
> +}
> diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
> index 049c3b4..dc9e06d 100644
> --- a/drivers/hwmon/occ/common.h
> +++ b/drivers/hwmon/occ/common.h
> @@ -110,5 +110,6 @@ struct occ {
> };
>
> int occ_setup(struct occ *occ, const char *name);
> +void occ_shutdown(struct occ *occ);
>
> #endif /* OCC_COMMON_H */
> diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
> index 8032c0b..d719632 100644
> --- a/drivers/hwmon/occ/p8_i2c.c
> +++ b/drivers/hwmon/occ/p8_i2c.c
> @@ -230,6 +230,15 @@ static int p8_i2c_occ_probe(struct i2c_client *client,
> return occ_setup(occ, "p8_occ");
> }
>
> +static int p8_i2c_occ_remove(struct i2c_client *client)
> +{
> + struct occ *occ = dev_get_drvdata(&client->dev);
> +
> + occ_shutdown(occ);
> +
> + return 0;
> +}
> +
> static const struct of_device_id p8_i2c_occ_of_match[] = {
> { .compatible = "ibm,p8-occ-hwmon" },
> {}
> @@ -243,6 +252,7 @@ static int p8_i2c_occ_probe(struct i2c_client *client,
> .of_match_table = p8_i2c_occ_of_match,
> },
> .probe = p8_i2c_occ_probe,
> + .remove = p8_i2c_occ_remove,
> };
>
> module_i2c_driver(p8_i2c_occ_driver);
> diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
> index be3a469..7dbe4d5 100644
> --- a/drivers/hwmon/occ/p9_sbe.c
> +++ b/drivers/hwmon/occ/p9_sbe.c
> @@ -135,6 +135,8 @@ static int p9_sbe_occ_remove(struct platform_device *pdev)
> p9_sbe_occ->sbe = NULL;
> p9_sbe_occ_close_client(p9_sbe_occ);
>
> + occ_shutdown(occ);
> +
> return 0;
> }
>
--
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: [v3, 09/12] hwmon (occ): Add sensor attributes and register hwmon device
From: Guenter Roeck @ 2017-11-22 15:22 UTC (permalink / raw)
To: eajames
Cc: linux-kernel, linux-hwmon, devicetree, linux-doc, jdelvare,
corbet, mark.rutland, robh+dt, joel, Edward A. James
In-Reply-To: <1511222021-562-10-git-send-email-eajames@linux.vnet.ibm.com>
On Mon, Nov 20, 2017 at 05:53:38PM -0600, eajames@linux.vnet.ibm.com wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Setup the sensor attributes for every OCC sensor found by the first poll
> response. Register the attributes with hwmon. Add hwmon documentation
> for the driver.
>
> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> ---
> drivers/hwmon/occ/common.c | 450 +++++++++++++++++++++++++++++++++++++++++++++
> drivers/hwmon/occ/common.h | 15 ++
I seem to be missing the documentation.
Documentation/hwmon/submitting-patches:
* Document the driver in Documentation/hwmon/<driver_name>.
* Do not create non-standard attributes unless really needed. If you have to use
non-standard attributes, or you believe you do, discuss it on the mailing list
first. Either case, provide a detailed explanation why you need the
non-standard attribute(s).
Standard attributes are specified in Documentation/hwmon/sysfs-interface.
I really would expect a detailed explanation and, even more so,
documentation for all the non-standard attributes you are adding.
power%d_update_time
power%d_update_tag
power%d_accumulator
really need to be documented, you really need to explain the need for those
attributes. To me they don't mean anything.
Note that you'd probably be much better off using
devm_register_hwmon_with_info() nowadays, but i'll leave that for you to
decide.
Guenter
^ permalink raw reply
* Re: [v3, 02/12] Documentation: ABI: Add occ-hwmon driver sysfs documentation
From: Guenter Roeck @ 2017-11-22 15:15 UTC (permalink / raw)
To: eajames
Cc: linux-kernel, linux-hwmon, devicetree, linux-doc, jdelvare,
corbet, mark.rutland, robh+dt, joel, Edward A. James
In-Reply-To: <1511222021-562-3-git-send-email-eajames@linux.vnet.ibm.com>
On Mon, Nov 20, 2017 at 05:53:31PM -0600, eajames@linux.vnet.ibm.com wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Detail the sysfs attributes provided by the occ-hwmon driver.
>
This describes my problem with this driver: All the sysfs attributes
described here are not really hardware monitoring related. A later patch
describes this in more detail:
"The OCC can provide the raw sensor data as well as perform thermal
and power management on the system."
Is it possible to extract the non-hwmon functionality (maybe into an
mfd driver) and limit the hwmon part to just hardware monitoring ?
Guenter
> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> ---
> Documentation/ABI/testing/sysfs-driver-occ-hwmon | 85 ++++++++++++++++++++++++
> 1 file changed, 85 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-driver-occ-hwmon
>
> diff --git a/Documentation/ABI/testing/sysfs-driver-occ-hwmon b/Documentation/ABI/testing/sysfs-driver-occ-hwmon
> new file mode 100644
> index 0000000..8873cc3
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-driver-occ-hwmon
> @@ -0,0 +1,85 @@
> +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_active
> +Date: November 2017
> +KernelVersion: 4.14
> +Contact: eajames@us.ibm.com
> +Description:
> + A read-only attribute that indicates (with a "1" or a "0",
> + respectively) whether or not this OCC is in the "active" state.
> +
> +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_dvfs_ot
> +Date: November 2017
> +KernelVersion: 4.14
> +Contact: eajames@us.ibm.com
> +Description:
> + A read-only attribute that indicates (with a "1" or a "0",
> + respectively) whether or not this OCC has limited the processor
> + frequency due to over-temperature.
> +
> +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_dvfs_power
> +Date: November 2017
> +KernelVersion: 4.14
> +Contact: eajames@us.ibm.com
> +Description:
> + A read-only attribute that indicates (with a "1" or a "0",
> + respectively) whether or not this OCC has limited the processor
> + frequency due to power usage.
> +
> +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_error
> +Date: November 2017
> +KernelVersion: 4.14
> +Contact: eajames@us.ibm.com
> +Description:
> + A read-only attribute that indicates any error condition
> + observed by the OCC or detected by the driver. Reading the
> + attribute will return an integer. A negative integer indicates
> + either an error response from the OCC or bus error or other
> + error condition detected by the driver. A "0" indicates no
> + error.
> +
> +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_master
> +Date: November 2017
> +KernelVersion: 4.14
> +Contact: eajames@us.ibm.com
> +Description:
> + A read-only attribute that indicates (with a "1" or a "0",
> + respectively) whether or not this OCC is the "master" OCC.
> +
> +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_mem_throttle
> +Date: November 2017
> +KernelVersion: 4.14
> +Contact: eajames@us.ibm.com
> +Description:
> + A read-only attribute that indicates (with a "1" or a "0",
> + respectively) whether or not the OCC has throttled memory due
> + to over-temperature.
> +
> +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occs_present
> +Date: November 2017
> +KernelVersion: 4.14
> +Contact: eajames@us.ibm.com
> +Description:
> + A read-only attribute that indicates the number of OCCs present
> + on the system.
> +
> +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_quick_drop
> +Date: November 2017
> +KernelVersion: 4.14
> +Contact: eajames@us.ibm.com
> +Description:
> + A read-only attribute that indicates (with a "1" or a "0",
> + respectively) whether or not this OCC has asserted the "quick
> + power drop" signal.
> +
> +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_status
> +Date: November 2017
> +KernelVersion: 4.14
> +Contact: eajames@us.ibm.com
> +Description:
> + A read-only attribute that indicates the current OCC state. The
> + value of the attribute will be one of the following states:
> + 0: Reserved
> + 1: Standby
> + 2: Observation
> + 3: Active
> + 4: Safe
> + 5: Characterization
^ permalink raw reply
* [PATCH 3/3] ASoC: stm32: sai: use devm_of_platform_populate()
From: Olivier Moysan @ 2017-11-22 15:02 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, mcoquelin.stm32,
alexandre.torgue, alsa-devel, robh, mark.rutland, devicetree,
linux-arm-kernel, kernel, linux-kernel, olivier.moysan
Cc: arnaud.pouliquen, benjamin.gaignard, Benjamin Gaignard
In-Reply-To: <1511362947-14747-1-git-send-email-olivier.moysan@st.com>
Use devm_of_platform_populate() instead of of_platform_depopulate()
to simplify driver code.
Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
Signed-off-by: Olivier Moysan <olivier.moysan@st.com>
---
sound/soc/stm/stm32_sai.c | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
index 0a1f064..d743b7d 100644
--- a/sound/soc/stm/stm32_sai.c
+++ b/sound/soc/stm/stm32_sai.c
@@ -124,7 +124,6 @@ static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
static int stm32_sai_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
struct stm32_sai_data *sai;
struct reset_control *rst;
struct resource *res;
@@ -184,14 +183,7 @@ static int stm32_sai_probe(struct platform_device *pdev)
sai->set_sync = &stm32_sai_set_sync;
platform_set_drvdata(pdev, sai);
- return of_platform_populate(np, NULL, NULL, &pdev->dev);
-}
-
-static int stm32_sai_remove(struct platform_device *pdev)
-{
- of_platform_depopulate(&pdev->dev);
-
- return 0;
+ return devm_of_platform_populate(&pdev->dev);
}
MODULE_DEVICE_TABLE(of, stm32_sai_ids);
@@ -202,7 +194,6 @@ static int stm32_sai_remove(struct platform_device *pdev)
.of_match_table = stm32_sai_ids,
},
.probe = stm32_sai_probe,
- .remove = stm32_sai_remove,
};
module_platform_driver(stm32_sai_driver);
--
1.9.1
^ permalink raw reply related
* [PATCH 2/3] ASoC: stm32: sai: simplify sync modes management
From: Olivier Moysan @ 2017-11-22 15:02 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, mcoquelin.stm32,
alexandre.torgue, alsa-devel, robh, mark.rutland, devicetree,
linux-arm-kernel, kernel, linux-kernel, olivier.moysan
Cc: arnaud.pouliquen, benjamin.gaignard
In-Reply-To: <1511362947-14747-1-git-send-email-olivier.moysan@st.com>
Use function of_find_device_by_node() to retrieve SAI
synchro provider device and private data.
This allows to remove registration of probed SAI
in a linked list.
Signed-off-by: Olivier Moysan <olivier.moysan@st.com>
---
sound/soc/stm/stm32_sai.c | 105 ++++++++++------------------------------------
1 file changed, 22 insertions(+), 83 deletions(-)
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
index d6f71a3..0a1f064 100644
--- a/sound/soc/stm/stm32_sai.c
+++ b/sound/soc/stm/stm32_sai.c
@@ -28,16 +28,6 @@
#include "stm32_sai.h"
-static LIST_HEAD(sync_providers);
-static DEFINE_MUTEX(sync_mutex);
-
-struct sync_provider {
- struct list_head link;
- struct device_node *node;
- int (*sync_conf)(void *data, int synco);
- void *data;
-};
-
static const struct stm32_sai_conf stm32_sai_conf_f4 = {
.version = SAI_STM32F4,
};
@@ -70,9 +60,8 @@ static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci)
return 0;
}
-static int stm32_sai_sync_conf_provider(void *data, int synco)
+static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
{
- struct stm32_sai_data *sai = (struct stm32_sai_data *)data;
u32 prev_synco;
int ret;
@@ -103,73 +92,34 @@ static int stm32_sai_sync_conf_provider(void *data, int synco)
return 0;
}
-static int stm32_sai_set_sync_provider(struct device_node *np, int synco)
+static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
+ struct device_node *np_provider,
+ int synco, int synci)
{
- struct sync_provider *provider;
+ struct platform_device *pdev = of_find_device_by_node(np_provider);
+ struct stm32_sai_data *sai_provider;
int ret;
- mutex_lock(&sync_mutex);
- list_for_each_entry(provider, &sync_providers, link) {
- if (provider->node == np) {
- ret = provider->sync_conf(provider->data, synco);
- mutex_unlock(&sync_mutex);
- return ret;
- }
+ if (!pdev) {
+ dev_err(&sai_client->pdev->dev,
+ "Device not found for node %s\n", np_provider->name);
+ return -ENODEV;
}
- mutex_unlock(&sync_mutex);
- /* SAI sync provider not found */
- return -ENODEV;
-}
-
-static int stm32_sai_set_sync(struct stm32_sai_data *sai,
- struct device_node *np_provider,
- int synco, int synci)
-{
- int ret;
+ sai_provider = platform_get_drvdata(pdev);
+ if (!sai_provider) {
+ dev_err(&sai_client->pdev->dev,
+ "SAI sync provider data not found\n");
+ return -EINVAL;
+ }
/* Configure sync client */
- stm32_sai_sync_conf_client(sai, synci);
+ ret = stm32_sai_sync_conf_client(sai_client, synci);
+ if (ret < 0)
+ return ret;
/* Configure sync provider */
- ret = stm32_sai_set_sync_provider(np_provider, synco);
-
- return ret;
-}
-
-static int stm32_sai_sync_add_provider(struct platform_device *pdev,
- void *data)
-{
- struct sync_provider *sp;
-
- sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL);
- if (!sp)
- return -ENOMEM;
-
- sp->node = of_node_get(pdev->dev.of_node);
- sp->data = data;
- sp->sync_conf = &stm32_sai_sync_conf_provider;
-
- mutex_lock(&sync_mutex);
- list_add(&sp->link, &sync_providers);
- mutex_unlock(&sync_mutex);
-
- return 0;
-}
-
-static void stm32_sai_sync_del_provider(struct device_node *np)
-{
- struct sync_provider *sp;
-
- mutex_lock(&sync_mutex);
- list_for_each_entry(sp, &sync_providers, link) {
- if (sp->node == np) {
- list_del(&sp->link);
- of_node_put(sp->node);
- break;
- }
- }
- mutex_unlock(&sync_mutex);
+ return stm32_sai_sync_conf_provider(sai_provider, synco);
}
static int stm32_sai_probe(struct platform_device *pdev)
@@ -179,7 +129,6 @@ static int stm32_sai_probe(struct platform_device *pdev)
struct reset_control *rst;
struct resource *res;
const struct of_device_id *of_id;
- int ret;
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
if (!sai)
@@ -231,27 +180,17 @@ static int stm32_sai_probe(struct platform_device *pdev)
reset_control_deassert(rst);
}
- ret = stm32_sai_sync_add_provider(pdev, sai);
- if (ret < 0)
- return ret;
- sai->set_sync = &stm32_sai_set_sync;
-
sai->pdev = pdev;
+ sai->set_sync = &stm32_sai_set_sync;
platform_set_drvdata(pdev, sai);
- ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
- if (ret < 0)
- stm32_sai_sync_del_provider(np);
-
- return ret;
+ return of_platform_populate(np, NULL, NULL, &pdev->dev);
}
static int stm32_sai_remove(struct platform_device *pdev)
{
of_platform_depopulate(&pdev->dev);
- stm32_sai_sync_del_provider(pdev->dev.of_node);
-
return 0;
}
--
1.9.1
^ permalink raw reply related
* [PATCH 1/3] ASoC: stm32: fix sync property description in SAI bindings
From: Olivier Moysan @ 2017-11-22 15:02 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, mcoquelin.stm32,
alexandre.torgue, alsa-devel, robh, mark.rutland, devicetree,
linux-arm-kernel, kernel, linux-kernel, olivier.moysan
Cc: arnaud.pouliquen, benjamin.gaignard
In-Reply-To: <1511362947-14747-1-git-send-email-olivier.moysan@st.com>
SAI sync property must be described in SAI subnodes
section, as it is a property of child node.
This patch fixes commit 14f0e5f8d97e632695d92f41f2e91d10d8005d47
"ASoC: stm32: Add synchronization to SAI bindings".
Signed-off-by: Olivier Moysan <olivier.moysan@st.com>
---
Documentation/devicetree/bindings/sound/st,stm32-sai.txt | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt
index 1f9cd70..b1acc1a 100644
--- a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt
+++ b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt
@@ -20,11 +20,6 @@ Required properties:
Optional properties:
- resets: Reference to a reset controller asserting the SAI
- - st,sync: specify synchronization mode.
- By default SAI sub-block is in asynchronous mode.
- This property sets SAI sub-block as slave of another SAI sub-block.
- Must contain the phandle and index of the sai sub-block providing
- the synchronization.
SAI subnodes:
Two subnodes corresponding to SAI sub-block instances A et B can be defined.
@@ -44,6 +39,13 @@ SAI subnodes required properties:
- pinctrl-names: should contain only value "default"
- pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
+SAI subnodes Optional properties:
+ - st,sync: specify synchronization mode.
+ By default SAI sub-block is in asynchronous mode.
+ This property sets SAI sub-block as slave of another SAI sub-block.
+ Must contain the phandle and index of the sai sub-block providing
+ the synchronization.
+
The device node should contain one 'port' child node with one child 'endpoint'
node, according to the bindings defined in Documentation/devicetree/bindings/
graph.txt.
--
1.9.1
^ permalink raw reply related
* [PATCH 0/3] ASoC: stm32: sai: fixes related to synchro feature
From: Olivier Moysan @ 2017-11-22 15:02 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, mcoquelin.stm32,
alexandre.torgue, alsa-devel, robh, mark.rutland, devicetree,
linux-arm-kernel, kernel, linux-kernel, olivier.moysan
Cc: arnaud.pouliquen, benjamin.gaignard
Fix sai binding and simplify code related to synchro feature.
This change allows to remove stm32_sai_remove() function
and use devm_of_platform_populate().
Olivier Moysan (3):
ASoC: stm32: fix sync property description in SAI bindings
ASoC: stm32: sai: simplify sync modes management
ASoC: stm32: sai: use devm_of_platform_populate()
.../devicetree/bindings/sound/st,stm32-sai.txt | 12 ++-
sound/soc/stm/stm32_sai.c | 114 ++++-----------------
2 files changed, 29 insertions(+), 97 deletions(-)
--
1.9.1
^ permalink raw reply
* Re: [PATCH v2 4/4] drm/tinydrm: add driver for ILI9225 panels
From: Noralf Trønnes @ 2017-11-22 14:22 UTC (permalink / raw)
To: David Lechner, dri-devel, devicetree
Cc: Mark Rutland, Rob Herring, linux-kernel
In-Reply-To: <1511122328-31133-5-git-send-email-david@lechnology.com>
Den 19.11.2017 21.12, skrev David Lechner:
> This adds a new driver for display panels based on the Ilitek ILI9225
> controller.
>
> This was developed for a no-name panel with a red PCB that is commonly
> marketed for Arduino. See <https://github.com/Nkawu/TFT_22_ILI9225>.
>
> Signed-off-by: David Lechner <david@lechnology.com>
> ---
Reviewed-by: Noralf Trønnes <noralf@tronnes.org>
> v2 changes:
> * use exported mipi_dbi_* functions from patch 3/4
> * new ili9225_command function
>
> MAINTAINERS | 6 +
> drivers/gpu/drm/tinydrm/Kconfig | 10 +
> drivers/gpu/drm/tinydrm/Makefile | 1 +
> drivers/gpu/drm/tinydrm/ili9225.c | 468 ++++++++++++++++++++++++++++++++++++++
> 4 files changed, 485 insertions(+)
> create mode 100644 drivers/gpu/drm/tinydrm/ili9225.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0d77f22..72404f3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4372,6 +4372,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
> S: Maintained
> F: drivers/gpu/drm/tve200/
>
> +DRM DRIVER FOR ILITEK ILI9225 PANELS
> +M: David Lechner <david@lechnology.com>
> +S: Maintained
> +F: drivers/gpu/drm/tinydrm/ili9225.c
> +F: Documentation/devicetree/bindings/display/ili9225.txt
> +
> DRM DRIVER FOR INTEL I810 VIDEO CARDS
> S: Orphan / Obsolete
> F: drivers/gpu/drm/i810/
> diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig
> index 2e790e7..90c5bd5 100644
> --- a/drivers/gpu/drm/tinydrm/Kconfig
> +++ b/drivers/gpu/drm/tinydrm/Kconfig
> @@ -12,6 +12,16 @@ menuconfig DRM_TINYDRM
> config TINYDRM_MIPI_DBI
> tristate
>
> +config TINYDRM_ILI9225
> + tristate "DRM support for ILI9225 display panels"
> + depends on DRM_TINYDRM && SPI
> + select TINYDRM_MIPI_DBI
> + help
> + DRM driver for the following Ilitek ILI9225 panels:
> + * No-name 2.2" color screen module
> +
> + If M is selected the module will be called ili9225.
> +
> config TINYDRM_MI0283QT
> tristate "DRM support for MI0283QT"
> depends on DRM_TINYDRM && SPI
> diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile
> index 0c184bd..8aeee53 100644
> --- a/drivers/gpu/drm/tinydrm/Makefile
> +++ b/drivers/gpu/drm/tinydrm/Makefile
> @@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_TINYDRM) += core/
> obj-$(CONFIG_TINYDRM_MIPI_DBI) += mipi-dbi.o
>
> # Displays
> +obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o
> obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o
> obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o
> obj-$(CONFIG_TINYDRM_ST7586) += st7586.o
> diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c
> new file mode 100644
> index 0000000..3b766a2
> --- /dev/null
> +++ b/drivers/gpu/drm/tinydrm/ili9225.c
> @@ -0,0 +1,468 @@
> +/*
> + * DRM driver for Ilitek ILI9225 panels
> + *
> + * Copyright 2017 David Lechner <david@lechnology.com>
> + *
> + * Some code copied from mipi-dbi.c
> + * Copyright 2016 Noralf Trønnes
> + *
> + * 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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/dma-buf.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/property.h>
> +#include <linux/spi/spi.h>
> +#include <video/mipi_display.h>
> +
> +#include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/tinydrm/mipi-dbi.h>
> +#include <drm/tinydrm/tinydrm-helpers.h>
> +
> +#define ILI9225_DRIVER_READ_CODE 0x00
> +#define ILI9225_DRIVER_OUTPUT_CONTROL 0x01
> +#define ILI9225_LCD_AC_DRIVING_CONTROL 0x02
> +#define ILI9225_ENTRY_MODE 0x03
> +#define ILI9225_DISPLAY_CONTROL_1 0x07
> +#define ILI9225_BLANK_PERIOD_CONTROL_1 0x08
> +#define ILI9225_FRAME_CYCLE_CONTROL 0x0b
> +#define ILI9225_INTERFACE_CONTROL 0x0c
> +#define ILI9225_OSCILLATION_CONTROL 0x0f
> +#define ILI9225_POWER_CONTROL_1 0x10
> +#define ILI9225_POWER_CONTROL_2 0x11
> +#define ILI9225_POWER_CONTROL_3 0x12
> +#define ILI9225_POWER_CONTROL_4 0x13
> +#define ILI9225_POWER_CONTROL_5 0x14
> +#define ILI9225_VCI_RECYCLING 0x15
> +#define ILI9225_RAM_ADDRESS_SET_1 0x20
> +#define ILI9225_RAM_ADDRESS_SET_2 0x21
> +#define ILI9225_WRITE_DATA_TO_GRAM 0x22
> +#define ILI9225_SOFTWARE_RESET 0x28
> +#define ILI9225_GATE_SCAN_CONTROL 0x30
> +#define ILI9225_VERTICAL_SCROLL_1 0x31
> +#define ILI9225_VERTICAL_SCROLL_2 0x32
> +#define ILI9225_VERTICAL_SCROLL_3 0x33
> +#define ILI9225_PARTIAL_DRIVING_POS_1 0x34
> +#define ILI9225_PARTIAL_DRIVING_POS_2 0x35
> +#define ILI9225_HORIZ_WINDOW_ADDR_1 0x36
> +#define ILI9225_HORIZ_WINDOW_ADDR_2 0x37
> +#define ILI9225_VERT_WINDOW_ADDR_1 0x38
> +#define ILI9225_VERT_WINDOW_ADDR_2 0x39
> +#define ILI9225_GAMMA_CONTROL_1 0x50
> +#define ILI9225_GAMMA_CONTROL_2 0x51
> +#define ILI9225_GAMMA_CONTROL_3 0x52
> +#define ILI9225_GAMMA_CONTROL_4 0x53
> +#define ILI9225_GAMMA_CONTROL_5 0x54
> +#define ILI9225_GAMMA_CONTROL_6 0x55
> +#define ILI9225_GAMMA_CONTROL_7 0x56
> +#define ILI9225_GAMMA_CONTROL_8 0x57
> +#define ILI9225_GAMMA_CONTROL_9 0x58
> +#define ILI9225_GAMMA_CONTROL_10 0x59
> +
> +static inline int ili9225_command(struct mipi_dbi *mipi, u8 cmd, u16 data)
> +{
> + u8 par[2] = { data >> 8, data & 0xff };
> +
> + return mipi_dbi_command_buf(mipi, cmd, par, 2);
> +}
> +
> +static int ili9225_fb_dirty(struct drm_framebuffer *fb,
> + struct drm_file *file_priv, unsigned int flags,
> + unsigned int color, struct drm_clip_rect *clips,
> + unsigned int num_clips)
> +{
> + struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
> + struct tinydrm_device *tdev = fb->dev->dev_private;
> + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
> + bool swap = mipi->swap_bytes;
> + struct drm_clip_rect clip;
> + u16 x_start, y_start;
> + u16 x1, x2, y1, y2;
> + int ret = 0;
> + bool full;
> + void *tr;
> +
> + mutex_lock(&tdev->dirty_lock);
> +
> + if (!mipi->enabled)
> + goto out_unlock;
> +
> + /* fbdev can flush even when we're not interested */
> + if (tdev->pipe.plane.fb != fb)
> + goto out_unlock;
> +
> + full = tinydrm_merge_clips(&clip, clips, num_clips, flags,
> + fb->width, fb->height);
> +
> + DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id,
> + clip.x1, clip.x2, clip.y1, clip.y2);
> +
> + if (!mipi->dc || !full || swap ||
> + fb->format->format == DRM_FORMAT_XRGB8888) {
> + tr = mipi->tx_buf;
> + ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap);
> + if (ret)
> + goto out_unlock;
> + } else {
> + tr = cma_obj->vaddr;
> + }
> +
> + switch (mipi->rotation) {
> + default:
> + x1 = clip.x1;
> + x2 = clip.x2 - 1;
> + y1 = clip.y1;
> + y2 = clip.y2 - 1;
> + x_start = x1;
> + y_start = y1;
> + break;
> + case 90:
> + x1 = clip.y1;
> + x2 = clip.y2 - 1;
> + y1 = fb->width - clip.x2;
> + y2 = fb->width - clip.x1 - 1;
> + x_start = x1;
> + y_start = y2;
> + break;
> + case 180:
> + x1 = fb->width - clip.x2;
> + x2 = fb->width - clip.x1 - 1;
> + y1 = fb->height - clip.y2;
> + y2 = fb->height - clip.y1 - 1;
> + x_start = x2;
> + y_start = y2;
> + break;
> + case 270:
> + x1 = fb->height - clip.y2;
> + x2 = fb->height - clip.y1 - 1;
> + y1 = clip.x1;
> + y2 = clip.x2 - 1;
> + x_start = x2;
> + y_start = y1;
> + break;
> + }
> +
> + ili9225_command(mipi, ILI9225_HORIZ_WINDOW_ADDR_1, x2);
> + ili9225_command(mipi, ILI9225_HORIZ_WINDOW_ADDR_2, x1);
> + ili9225_command(mipi, ILI9225_VERT_WINDOW_ADDR_1, y2);
> + ili9225_command(mipi, ILI9225_VERT_WINDOW_ADDR_2, y1);
> +
> + ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_1, x_start);
> + ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, y_start);
> +
> + ret = mipi_dbi_command_buf(mipi, ILI9225_WRITE_DATA_TO_GRAM, tr,
> + (clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2);
> +
> +out_unlock:
> + mutex_unlock(&tdev->dirty_lock);
> +
> + if (ret)
> + dev_err_once(fb->dev->dev, "Failed to update display %d\n",
> + ret);
> +
> + return ret;
> +}
> +
> +static const struct drm_framebuffer_funcs ili9225_fb_funcs = {
> + .destroy = drm_gem_fb_destroy,
> + .create_handle = drm_gem_fb_create_handle,
> + .dirty = ili9225_fb_dirty,
> +};
> +
> +static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
> + struct drm_crtc_state *crtc_state)
> +{
> + struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
> + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
> + struct drm_framebuffer *fb = pipe->plane.fb;
> + struct device *dev = tdev->drm->dev;
> + int ret;
> + u8 am_id;
> +
> + DRM_DEBUG_KMS("\n");
> +
> + mipi_dbi_hw_reset(mipi);
> +
> + /*
> + * There don't seem to be two example init sequences that match, so
> + * using the one from the popular Arduino library for this display.
> + * https://github.com/Nkawu/TFT_22_ILI9225/blob/master/src/TFT_22_ILI9225.cpp
> + */
> +
> + ret = ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0000);
> + if (ret) {
> + DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
> + return;
> + }
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0000);
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x0000);
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_4, 0x0000);
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_5, 0x0000);
> +
> + msleep(40);
> +
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0018);
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x6121);
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_4, 0x006f);
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_5, 0x495f);
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0800);
> +
> + msleep(10);
> +
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x103b);
> +
> + msleep(50);
> +
> + switch (mipi->rotation) {
> + default:
> + am_id = 0x30;
> + break;
> + case 90:
> + am_id = 0x18;
> + break;
> + case 180:
> + am_id = 0x00;
> + break;
> + case 270:
> + am_id = 0x28;
> + break;
> + }
> + ili9225_command(mipi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c);
> + ili9225_command(mipi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100);
> + ili9225_command(mipi, ILI9225_ENTRY_MODE, 0x1000 | am_id);
> + ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
> + ili9225_command(mipi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808);
> + ili9225_command(mipi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100);
> + ili9225_command(mipi, ILI9225_INTERFACE_CONTROL, 0x0000);
> + ili9225_command(mipi, ILI9225_OSCILLATION_CONTROL, 0x0d01);
> + ili9225_command(mipi, ILI9225_VCI_RECYCLING, 0x0020);
> + ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_1, 0x0000);
> + ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, 0x0000);
> +
> + ili9225_command(mipi, ILI9225_GATE_SCAN_CONTROL, 0x0000);
> + ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_1, 0x00db);
> + ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_2, 0x0000);
> + ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_3, 0x0000);
> + ili9225_command(mipi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db);
> + ili9225_command(mipi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000);
> +
> + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_1, 0x0000);
> + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_2, 0x0808);
> + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_3, 0x080a);
> + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_4, 0x000a);
> + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_5, 0x0a08);
> + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_6, 0x0808);
> + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_7, 0x0000);
> + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_8, 0x0a00);
> + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_9, 0x0710);
> + ili9225_command(mipi, ILI9225_GAMMA_CONTROL_10, 0x0710);
> +
> + ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0012);
> +
> + msleep(50);
> +
> + ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x1017);
> +
> + mipi->enabled = true;
> +
> + if (fb)
> + fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0);
> +}
> +
> +static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
> +{
> + struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
> + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
> +
> + DRM_DEBUG_KMS("\n");
> +
> + if (!mipi->enabled)
> + return;
> +
> + ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
> + msleep(50);
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0007);
> + msleep(50);
> + ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0a02);
> +
> + mipi->enabled = false;
> +}
> +
> +static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par,
> + size_t num)
> +{
> + struct spi_device *spi = mipi->spi;
> + unsigned int bpw = 8;
> + u32 speed_hz;
> + int ret;
> +
> + gpiod_set_value_cansleep(mipi->dc, 0);
> + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
> + ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, &cmd, 1);
> + if (ret || !num)
> + return ret;
> +
> + if (cmd == ILI9225_WRITE_DATA_TO_GRAM && !mipi->swap_bytes)
> + bpw = 16;
> +
> + gpiod_set_value_cansleep(mipi->dc, 1);
> + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
> +
> + return tinydrm_spi_transfer(spi, speed_hz, NULL, bpw, par, num);
> +}
> +
> +static const u32 ili9225_formats[] = {
> + DRM_FORMAT_RGB565,
> + DRM_FORMAT_XRGB8888,
> +};
> +
> +static int ili9225_init(struct device *dev, struct mipi_dbi *mipi,
> + const struct drm_simple_display_pipe_funcs *pipe_funcs,
> + struct drm_driver *driver,
> + const struct drm_display_mode *mode,
> + unsigned int rotation)
> +{
> + size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
> + struct tinydrm_device *tdev = &mipi->tinydrm;
> + int ret;
> +
> + if (!mipi->command)
> + return -EINVAL;
> +
> + mutex_init(&mipi->cmdlock);
> +
> + mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
> + if (!mipi->tx_buf)
> + return -ENOMEM;
> +
> + ret = devm_tinydrm_init(dev, tdev, &ili9225_fb_funcs, driver);
> + if (ret)
> + return ret;
> +
> + ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
> + DRM_MODE_CONNECTOR_VIRTUAL,
> + ili9225_formats,
> + ARRAY_SIZE(ili9225_formats), mode,
> + rotation);
> + if (ret)
> + return ret;
> +
> + tdev->drm->mode_config.preferred_depth = 16;
> + mipi->rotation = rotation;
> +
> + drm_mode_config_reset(tdev->drm);
> +
> + DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
> + tdev->drm->mode_config.preferred_depth, rotation);
> +
> + return 0;
> +}
> +
> +static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
> + .enable = ili9225_pipe_enable,
> + .disable = ili9225_pipe_disable,
> + .update = tinydrm_display_pipe_update,
> + .prepare_fb = tinydrm_display_pipe_prepare_fb,
> +};
> +
> +static const struct drm_display_mode ili9225_mode = {
> + TINYDRM_MODE(176, 220, 35, 44),
> +};
> +
> +DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops);
> +
> +static struct drm_driver ili9225_driver = {
> + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
> + DRIVER_ATOMIC,
> + .fops = &ili9225_fops,
> + TINYDRM_GEM_DRIVER_OPS,
> + .lastclose = tinydrm_lastclose,
> + .name = "ili9225",
> + .desc = "Ilitek ILI9225",
> + .date = "20171106",
> + .major = 1,
> + .minor = 0,
> +};
> +
> +static const struct of_device_id ili9225_of_match[] = {
> + { .compatible = "ilitek,ili9225-2.2in-176x220" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, ili9225_of_match);
> +
> +static const struct spi_device_id ili9225_id[] = {
> + { "ili9225-2.2in-176x220", 0 },
> + { },
> +};
> +MODULE_DEVICE_TABLE(spi, ili9225_id);
> +
> +static int ili9225_probe(struct spi_device *spi)
> +{
> + struct device *dev = &spi->dev;
> + struct mipi_dbi *mipi;
> + struct gpio_desc *rs;
> + u32 rotation = 0;
> + int ret;
> +
> + mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
> + if (!mipi)
> + return -ENOMEM;
> +
> + mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(mipi->reset)) {
> + DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
> + return PTR_ERR(mipi->reset);
> + }
> +
> + rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW);
> + if (IS_ERR(rs)) {
> + DRM_DEV_ERROR(dev, "Failed to get gpio 'rs'\n");
> + return PTR_ERR(rs);
> + }
> +
> + device_property_read_u32(dev, "rotation", &rotation);
> +
> + ret = mipi_dbi_spi_init(spi, mipi, rs);
> + if (ret)
> + return ret;
> +
> + /* override the command function set in mipi_dbi_spi_init() */
> + mipi->command = ili9225_dbi_command;
> +
> + ret = ili9225_init(&spi->dev, mipi, &ili9225_pipe_funcs,
> + &ili9225_driver, &ili9225_mode, rotation);
> + if (ret)
> + return ret;
> +
> + spi_set_drvdata(spi, mipi);
> +
> + return devm_tinydrm_register(&mipi->tinydrm);
> +}
> +
> +static void ili9225_shutdown(struct spi_device *spi)
> +{
> + struct mipi_dbi *mipi = spi_get_drvdata(spi);
> +
> + tinydrm_shutdown(&mipi->tinydrm);
> +}
> +
> +static struct spi_driver ili9225_spi_driver = {
> + .driver = {
> + .name = "ili9225",
> + .owner = THIS_MODULE,
> + .of_match_table = ili9225_of_match,
> + },
> + .id_table = ili9225_id,
> + .probe = ili9225_probe,
> + .shutdown = ili9225_shutdown,
> +};
> +module_spi_driver(ili9225_spi_driver);
> +
> +MODULE_DESCRIPTION("Ilitek ILI9225 DRM driver");
> +MODULE_AUTHOR("David Lechner <david@lechnology.com>");
> +MODULE_LICENSE("GPL");
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [PATCH v2 3/4] drm/tinydrm: export mipi_dbi_buf_copy and mipi_dbi_spi_cmd_max_speed
From: Noralf Trønnes @ 2017-11-22 14:21 UTC (permalink / raw)
To: David Lechner, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Rob Herring, Mark Rutland, linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1511122328-31133-4-git-send-email-david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
Den 19.11.2017 21.12, skrev David Lechner:
> This exports the mipi_dbi_buf_copy() and mipi_dbi_spi_cmd_max_speed()
> functions so that they can be shared with other drivers.
>
> Signed-off-by: David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
> ---
Reviewed-by: Noralf Trønnes <noralf-L59+Z2yzLopAfugRpC6u6w@public.gmane.org>
> v2 changes:
> * new patch in v2
>
> drivers/gpu/drm/tinydrm/mipi-dbi.c | 24 ++++++++++++++++++++----
> include/drm/tinydrm/mipi-dbi.h | 4 +++-
> 2 files changed, 23 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
> index 347f9b2..aa6b6ce 100644
> --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c
> +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c
> @@ -154,8 +154,18 @@ int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len)
> }
> EXPORT_SYMBOL(mipi_dbi_command_buf);
>
> -static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
> - struct drm_clip_rect *clip, bool swap)
> +/**
> + * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary
> + * @dst: The destination buffer
> + * @fb: The source framebuffer
> + * @clip: Clipping rectangle of the area to be copied
> + * @swap: When true, swap MSB/LSB of 16-bit values
> + *
> + * Returns:
> + * Zero on success, negative error code on failure.
> + */
> +int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
> + struct drm_clip_rect *clip, bool swap)
> {
> struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
> struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
> @@ -192,6 +202,7 @@ static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
> DMA_FROM_DEVICE);
> return ret;
> }
> +EXPORT_SYMBOL(mipi_dbi_buf_copy);
>
> static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb,
> struct drm_file *file_priv,
> @@ -444,18 +455,23 @@ EXPORT_SYMBOL(mipi_dbi_display_is_on);
>
> #if IS_ENABLED(CONFIG_SPI)
>
> -/*
> +/**
> + * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed
> + * @spi: SPI device
> + * @len: The transfer buffer length.
> + *
> * Many controllers have a max speed of 10MHz, but can be pushed way beyond
> * that. Increase reliability by running pixel data at max speed and the rest
> * at 10MHz, preventing transfer glitches from messing up the init settings.
> */
> -static u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
> +u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
> {
> if (len > 64)
> return 0; /* use default */
>
> return min_t(u32, 10000000, spi->max_speed_hz);
> }
> +EXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed);
>
> /*
> * MIPI DBI Type C Option 1
> diff --git a/include/drm/tinydrm/mipi-dbi.h b/include/drm/tinydrm/mipi-dbi.h
> index 83346dd..5d0e82b 100644
> --- a/include/drm/tinydrm/mipi-dbi.h
> +++ b/include/drm/tinydrm/mipi-dbi.h
> @@ -72,10 +72,12 @@ void mipi_dbi_pipe_enable(struct drm_simple_display_pipe *pipe,
> void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe);
> void mipi_dbi_hw_reset(struct mipi_dbi *mipi);
> bool mipi_dbi_display_is_on(struct mipi_dbi *mipi);
> +u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len);
>
> int mipi_dbi_command_read(struct mipi_dbi *mipi, u8 cmd, u8 *val);
> int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len);
> -
> +int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
> + struct drm_clip_rect *clip, bool swap);
> /**
> * mipi_dbi_command - MIPI DCS command with optional parameter(s)
> * @mipi: MIPI structure
--
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 7/7] can: rcar_canfd: document r8a77995 (R-Car D3) compatibility strings
From: Geert Uytterhoeven @ 2017-11-22 13:20 UTC (permalink / raw)
To: Ulrich Hecht
Cc: Linux-Renesas, linux-can, devicetree@vger.kernel.org,
Wolfram Sang, Simon Horman, Magnus Damm, Chris Paterson,
Ramesh Shanmugasundaram
In-Reply-To: <1510915289-15059-8-git-send-email-ulrich.hecht+renesas@gmail.com>
Hi Ulrich,
On Fri, Nov 17, 2017 at 11:41 AM, Ulrich Hecht
<ulrich.hecht+renesas@gmail.com> wrote:
> Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
Thanks for your patch!
With the below fixed:
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> --- a/Documentation/devicetree/bindings/net/can/rcar_canfd.txt
> +++ b/Documentation/devicetree/bindings/net/can/rcar_canfd.txt
> @@ -24,12 +25,12 @@ The name of the child nodes are "channel0" and "channel1" respectively. Each
> child node supports the "status" property only, which is used to
> enable/disable the respective channel.
>
> -Required properties for "renesas,r8a7795-canfd" and "renesas,r8a7796-canfd"
> -compatible:
> -In R8A7795 and R8A7796 SoCs, canfd clock is a div6 clock and can be used by both
> -CAN and CAN FD controller at the same time. It needs to be scaled to maximum
> -frequency if any of these controllers use it. This is done using the below
> -properties:
> +Required properties for "renesas,r8a7795-canfd", "renesas,r8a7796-canfd" and
> +"renesas,r8a77995" compatible:
"renesas,r8a77995-canfd"
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.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
^ permalink raw reply
* Re: [PATCH 6/7] can: rcar_can: document r8a77995 (R-Car D3) compatibility strings
From: Geert Uytterhoeven @ 2017-11-22 13:18 UTC (permalink / raw)
To: Ulrich Hecht
Cc: Linux-Renesas, linux-can, devicetree@vger.kernel.org,
Wolfram Sang, Simon Horman, Magnus Damm, Chris Paterson,
Ramesh Shanmugasundaram
In-Reply-To: <1510915289-15059-7-git-send-email-ulrich.hecht+renesas@gmail.com>
On Fri, Nov 17, 2017 at 11:41 AM, Ulrich Hecht
<ulrich.hecht+renesas@gmail.com> wrote:
> Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.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
^ permalink raw reply
* Re: [PATCH 5/7] arm64: dts: r8a77995: Add CAN FD support
From: Geert Uytterhoeven @ 2017-11-22 13:18 UTC (permalink / raw)
To: Ulrich Hecht
Cc: Linux-Renesas, linux-can, devicetree@vger.kernel.org,
Wolfram Sang, Simon Horman, Magnus Damm, Chris Paterson,
Ramesh Shanmugasundaram
In-Reply-To: <1510915289-15059-6-git-send-email-ulrich.hecht+renesas@gmail.com>
On Fri, Nov 17, 2017 at 11:41 AM, Ulrich Hecht
<ulrich.hecht+renesas@gmail.com> wrote:
> Adds CAN FD controller node for r8a77995.
>
> Based on a patch for r8a7796 by Chris Paterson.
>
> Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.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
^ permalink raw reply
* Re: [PATCH 4/7] arm64: dts: r8a77995: Add CAN support
From: Geert Uytterhoeven @ 2017-11-22 13:16 UTC (permalink / raw)
To: Ulrich Hecht
Cc: Linux-Renesas, linux-can-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Wolfram Sang,
Simon Horman, Magnus Damm, Chris Paterson,
Ramesh Shanmugasundaram
In-Reply-To: <1510915289-15059-5-git-send-email-ulrich.hecht+renesas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Fri, Nov 17, 2017 at 11:41 AM, Ulrich Hecht
<ulrich.hecht+renesas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Adds CAN controller nodes for r8a77995.
>
> Based on a patch for r8a7796 by Chris Paterson.
>
> Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Reviewed-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 3/7] arm64: dts: r8a77995: Add CAN external clock support
From: Geert Uytterhoeven @ 2017-11-22 13:11 UTC (permalink / raw)
To: Ulrich Hecht
Cc: Linux-Renesas, linux-can-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Wolfram Sang,
Simon Horman, Magnus Damm, Chris Paterson,
Ramesh Shanmugasundaram
In-Reply-To: <1510915289-15059-4-git-send-email-ulrich.hecht+renesas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Fri, Nov 17, 2017 at 11:41 AM, Ulrich Hecht
<ulrich.hecht+renesas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Adds external CAN clock node for r8a77995. This clock can be used as
> fCAN clock of CAN and CAN FD controller.
>
> Based on a patch for r8a7796 by Chris Paterson.
>
> Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Reviewed-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 2/2] arm64: dts: renesas: eagle: add I2C0 support
From: Geert Uytterhoeven @ 2017-11-22 13:11 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Simon Horman, Rob Herring, Catalin Marinas, Will Deacon,
Linux-Renesas, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Magnus Damm, Mark Rutland,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Vladimir Barinov
In-Reply-To: <20171116210801.215014557-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
On Thu, Nov 16, 2017 at 10:06 PM, Sergei Shtylyov
<sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org> wrote:
> Define the Eagle board dependent part of the I2C0 device node.
>
> The I2C0 bus is populated by ON Semiconductor PCA9653 I/O expander and
> Analog Devices ADV7511W HDMI transmitter (but we're only describing the
> former chip now).
>
> Based on the original (and large) patch by Vladimir Barinov.
>
> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
Reviewed-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 1/2] arm64: dts: renesas: r8a77970: add I2C support
From: Geert Uytterhoeven @ 2017-11-22 13:06 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Simon Horman, Rob Herring, Catalin Marinas, Will Deacon,
Linux-Renesas, devicetree@vger.kernel.org, Magnus Damm,
Mark Rutland, linux-arm-kernel@lists.infradead.org,
Vladimir Barinov
In-Reply-To: <20171116210757.729290426@cogentembedded.com>
Hi Sergei,
On Thu, Nov 16, 2017 at 10:06 PM, Sergei Shtylyov
<sergei.shtylyov@cogentembedded.com> wrote:
> Define the generic R8A77970 parts of the I2C[0-4] device node.
>
> Based on the original (and large) patch by Daisuke Matsushita
> <daisuke.matsushita.ns@hitachi.com>.
>
> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Thanks for your patch!
> --- renesas.orig/arch/arm64/boot/dts/renesas/r8a77970.dtsi
> +++ renesas/arch/arm64/boot/dts/renesas/r8a77970.dtsi
> @@ -483,5 +491,85 @@
> #address-cells = <1>;
> #size-cells = <0>;
> };
> +
> + i2c0: i2c@e6500000 {
> + compatible = "renesas,i2c-r8a77970",
> + "renesas,rcar-gen3-i2c";
> + reg = <0 0xe6500000 0 0x40>;
> + interrupts = <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&cpg CPG_MOD 931>;
> + power-domains = <&sysc 32>;
> + resets = <&cpg 931>;
> + dmas = <&dmac1 0x91>, <&dmac1 0x90>;
Can't all five i2c interfaces be used with both dmac1 and dmac2?
With that fixed:
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.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
^ permalink raw reply
* Re: [RFCv2 PATCH 01/36] iommu: Keep track of processes and PASIDs
From: Jean-Philippe Brucker @ 2017-11-22 13:04 UTC (permalink / raw)
To: Bob Liu, linux-arm-kernel@lists.infradead.org,
linux-pci@vger.kernel.org, linux-acpi@vger.kernel.org,
devicetree@vger.kernel.org, iommu@lists.linux-foundation.org
Cc: joro@8bytes.org, robh+dt@kernel.org, Mark Rutland,
Catalin Marinas, Will Deacon, Lorenzo Pieralisi,
hanjun.guo@linaro.org, Sudeep Holla, rjw@rjwysocki.net,
lenb@kernel.org, Robin Murphy, bhelgaas@google.com,
alex.williamson@redhat.com, tn@semihalf.com,
thunder.leizhen@huawei.com, xieyisheng1@huawei.com,
gabriele.paoloni@huawei.com, nwat
In-Reply-To: <588ad6f0-e78a-63de-e27a-3da4de4ab2ab@huawei.com>
On 22/11/17 03:15, Bob Liu wrote:
> Hey Jean,
>
> On 2017/10/6 21:31, Jean-Philippe Brucker wrote:
>> IOMMU drivers need a way to bind Linux processes to devices. This is used
>> for Shared Virtual Memory (SVM), where devices support paging. In that
>> mode, DMA can directly target virtual addresses of a process.
>>
>> Introduce boilerplate code for allocating process structures and binding
>> them to devices. Four operations are added to IOMMU drivers:
>>
>> * process_alloc, process_free: to create an iommu_process structure and
>> perform architecture-specific operations required to grab the process
>> (for instance on ARM SMMU, pin down the CPU ASID). There is a single
>> iommu_process structure per Linux process.
>>
>
> I'm a bit confused here.
> The original meaning of iommu_domain is a virtual addrspace defined by a set of io page table.
> (fix me if I misunderstood).
iommu_domain can also be seen as a logical partition of devices that share
the same address spaces (the concept comes from AMD and Intel IOMMU
domains, I believe). Without PASIDs it was a single address space, with
PASIDs it can have multiple address spaces.
> Then what's the meaning of iommu_domain and iommu_process after introducing iommu_process?
> Could you consider document these concepts?
iommu_process is used to keep track of Linux process address spaces. I'll
rename it to io_mm in next version, to make it clear that it doesn't
represent a Linux task but an mm_struct instead. However the
implementation stays pretty much identical. A domain can be associated to
multiple io_mm, and an io_mm can be associated to multiple domains.
In the IOMMU architectures I know, PASID is implemented like this. You
have the device tables (stream tables on SMMU), pointing to PASID tables
(context descriptor tables on SMMU). In the following diagram,
.->+--------+
/ 0 | |------ io_pgtable
/ +--------+
/ 1 | |------ io_mm->mm X
+--------+ / +--------+
0 | A |-' 2 | |-.
+--------+ +--------+ \
1 | | 3 | | \
+--------+ +--------+ -- io_mm->mm Y
2 | B |--. PASID tables /
+--------+ \ |
3 | B |----+--->+--------+ |
+--------+ / 0 | |- | -- io_pgtable
4 | B |--' +--------+ |
+--------+ 1 | | |
Device tables +--------+ |
2 | |--'
+--------+
3 | |------ io_mm->priv io_pgtable
+--------+
PASID tables
* Device 0 (e.g. PCI 0000:00:00.0) is in domain A.
* Devices 2, 3 and 4 are in domain B.
* Domain A has the top set of PASID tables.
* Domain B has the bottom set of PASID tables.
* Domain A is bound to process address space X.
-> Device 0 can access X with PASID 1.
* Both domains A and B are bound to process address space Y.
-> Devices 0, 2, 3 and 4 can access Y with PASID 2
* PASID 0 is special on Arm SMMU (with S1DSS=0b10). It will always be
reserved for classic DMA map/unmap. Even for hypothetical devices that
don't support non-pasid transactions, I'd like to keep this convention.
It should be quite useful for device drivers to have PASID 0 available
with DMA map/unmap.
* When introducing "private" PASID address spaces (that many are asking
for), which are backed by a set of io-pgtable and map/unmap ops, I
suppose they would reuse the io_mm structure. In this example PASID 3 is
associated to a private address space and not backed by an mm. Since the
PASID space is global, PASID 3 won't be available for any other domain.
Does this clarify the current design, or is it just more confusing?
Thanks,
Jean
^ permalink raw reply
* Re: [PATCH v2 2/4] dt-bindings: media: rcar_vin: add device tree support for r8a774[35]
From: Geert Uytterhoeven @ 2017-11-22 12:53 UTC (permalink / raw)
To: Fabrizio Castro
Cc: Mauro Carvalho Chehab, Mark Rutland, Rob Herring,
Niklas Söderlund, Linux Media Mailing List, Linux-Renesas,
devicetree@vger.kernel.org, Simon Horman, Chris Paterson,
Biju Das
In-Reply-To: <1510856571-30281-3-git-send-email-fabrizio.castro@bp.renesas.com>
On Thu, Nov 16, 2017 at 7:22 PM, Fabrizio Castro
<fabrizio.castro@bp.renesas.com> wrote:
> Add compatible strings for r8a7743 and r8a7745. No driver change
> is needed as "renesas,rcar-gen2-vin" will activate the right code.
> However, it is good practice to document compatible strings for the
> specific SoC as this allows SoC specific changes to the driver if
> needed, in addition to document SoC support and therefore allow
> checkpatch.pl to validate compatible string values.
>
> Signed-off-by: Fabrizio Castro <fabrizio.castro@bp.renesas.com>
> Reviewed-by: Biju Das <biju.das@bp.renesas.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.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
^ permalink raw reply
* [PATCH 8/8] arm64: dts: uniphier: add sound node for UniPhier
From: Katsuhiro Suzuki @ 2017-11-22 11:43 UTC (permalink / raw)
To: Mark Brown, alsa-devel, Rob Herring, devicetree, Masahiro Yamada
Cc: Masami Hiramatsu, Jassi Brar, linux-arm-kernel, linux-kernel,
Katsuhiro Suzuki
In-Reply-To: <20171122114321.29196-1-suzuki.katsuhiro@socionext.com>
This patch adds audio controller, codec and simple card node of
UniPhier AIO sound system for LD11/20 SoCs.
Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
---
.../boot/dts/socionext/uniphier-ld11-global.dts | 72 ++++++++++++++++++++++
arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi | 31 ++++++++++
.../boot/dts/socionext/uniphier-ld20-global.dts | 72 ++++++++++++++++++++++
arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi | 35 +++++++++++
4 files changed, 210 insertions(+)
diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld11-global.dts b/arch/arm64/boot/dts/socionext/uniphier-ld11-global.dts
index 2452b2243f42..4eb8d00a8c02 100644
--- a/arch/arm64/boot/dts/socionext/uniphier-ld11-global.dts
+++ b/arch/arm64/boot/dts/socionext/uniphier-ld11-global.dts
@@ -37,6 +37,29 @@
device_type = "memory";
reg = <0 0x80000000 0 0x40000000>;
};
+
+ soc@0 {
+ sound {
+ compatible = "audio-graph-card";
+ label = "UniPhier LD11";
+ widgets = "Headphone", "Headphone Jack";
+
+ dais = <&i2s_port2
+ &i2s_port4
+ &spdif_port0>;
+ };
+
+ spdif-out {
+ compatible = "linux,spdif-dit";
+ #sound-dai-cells = <0>;
+
+ port@0 {
+ spdif_tx: endpoint {
+ remote-endpoint = <&spdif_hiecout1>;
+ };
+ };
+ };
+ };
};
&serial0 {
@@ -72,3 +95,52 @@
&nand {
status = "okay";
};
+
+&audio {
+ i2s_port0: port@0 {
+ i2s_hdmi: endpoint {
+ };
+ };
+
+ i2s_port1: port@1 {
+ i2s_pcmin2: endpoint {
+ };
+ };
+
+ i2s_port2: port@2 {
+ i2s_line: endpoint {
+ remote-endpoint = <&evea_line>;
+ };
+ };
+
+ i2s_port3: port@3 {
+ i2s_hpcmout1: endpoint {
+ };
+ };
+
+ i2s_port4: port@4 {
+ i2s_pcmout3: endpoint {
+ remote-endpoint = <&evea_hp>;
+ };
+ };
+
+ spdif_port0: port@5 {
+ spdif_hiecout1: endpoint {
+ remote-endpoint = <&spdif_tx>;
+ };
+ };
+};
+
+&evea {
+ port@0 {
+ evea_line: endpoint {
+ remote-endpoint = <&i2s_line>;
+ };
+ };
+
+ port@1 {
+ evea_hp: endpoint {
+ remote-endpoint = <&i2s_pcmout3>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi b/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi
index 1c63d0ab8a58..875e3d832090 100644
--- a/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi
+++ b/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi
@@ -186,6 +186,29 @@
<21 217 3>;
};
+ audio: audio@56000000 {
+ compatible = "socionext,uniphier-ld11-aio";
+ reg = <0x56000000 0x80000>;
+ interrupts = <0 144 4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_aout>;
+ clock-names = "aio";
+ clocks = <&sys_clk 40>;
+ reset-names = "aio";
+ resets = <&sys_rst 40>;
+ #sound-dai-cells = <1>;
+ };
+
+ evea: codec@57900000 {
+ compatible = "socionext,uniphier-evea";
+ reg = <0x57900000 0x1000>;
+ clock-names = "evea", "exiv";
+ clocks = <&sys_clk 41>, <&sys_clk 42>;
+ reset-names = "evea", "exiv", "adamv";
+ resets = <&sys_rst 41>, <&sys_rst 42>, <&adamv_rst 0>;
+ #sound-dai-cells = <1>;
+ };
+
adamv@57920000 {
compatible = "socionext,uniphier-ld11-adamv",
"simple-mfd", "syscon";
@@ -471,3 +494,11 @@
};
#include "uniphier-pinctrl.dtsi"
+
+&pinctrl_aout {
+ drive-strength = <4>; /* default: 4mA */
+ group_1 {
+ pins = "AO1ARC";
+ drive-strength = <8>; /* 8mA */
+ };
+};
diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld20-global.dts b/arch/arm64/boot/dts/socionext/uniphier-ld20-global.dts
index fc2bc9d75d35..ebcd65631bca 100644
--- a/arch/arm64/boot/dts/socionext/uniphier-ld20-global.dts
+++ b/arch/arm64/boot/dts/socionext/uniphier-ld20-global.dts
@@ -37,6 +37,29 @@
device_type = "memory";
reg = <0 0x80000000 0 0xc0000000>;
};
+
+ soc@0 {
+ sound {
+ compatible = "audio-graph-card";
+ label = "UniPhier LD20";
+ widgets = "Headphone", "Headphone Jack";
+
+ dais = <&i2s_port2
+ &i2s_port4
+ &spdif_port0>;
+ };
+
+ spdif-out {
+ compatible = "linux,spdif-dit";
+ #sound-dai-cells = <0>;
+
+ port@0 {
+ spdif_tx: endpoint {
+ remote-endpoint = <&spdif_hiecout1>;
+ };
+ };
+ };
+ };
};
&serial0 {
@@ -54,3 +77,52 @@
&nand {
status = "okay";
};
+
+&audio {
+ i2s_port0: port@0 {
+ i2s_hdmi: endpoint {
+ };
+ };
+
+ i2s_port1: port@1 {
+ i2s_pcmin2: endpoint {
+ };
+ };
+
+ i2s_port2: port@2 {
+ i2s_line: endpoint {
+ remote-endpoint = <&evea_line>;
+ };
+ };
+
+ i2s_port3: port@3 {
+ i2s_hpcmout1: endpoint {
+ };
+ };
+
+ i2s_port4: port@4 {
+ i2s_pcmout3: endpoint {
+ remote-endpoint = <&evea_hp>;
+ };
+ };
+
+ spdif_port0: port@5 {
+ spdif_hiecout1: endpoint {
+ remote-endpoint = <&spdif_tx>;
+ };
+ };
+};
+
+&evea {
+ port@0 {
+ evea_line: endpoint {
+ remote-endpoint = <&i2s_line>;
+ };
+ };
+
+ port@1 {
+ evea_hp: endpoint {
+ remote-endpoint = <&i2s_pcmout3>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi b/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi
index 5c81070944cc..d42bb1be1137 100644
--- a/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi
+++ b/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi
@@ -286,6 +286,29 @@
<21 217 3>;
};
+ audio: audio@56000000 {
+ compatible = "socionext,uniphier-ld20-aio";
+ reg = <0x56000000 0x80000>;
+ interrupts = <0 144 4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_aout>;
+ clock-names = "aio";
+ clocks = <&sys_clk 40>;
+ reset-names = "aio";
+ resets = <&sys_rst 40>;
+ #sound-dai-cells = <1>;
+ };
+
+ evea: codec@57900000 {
+ compatible = "socionext,uniphier-evea";
+ reg = <0x57900000 0x1000>;
+ clock-names = "evea", "exiv";
+ clocks = <&sys_clk 41>, <&sys_clk 42>;
+ reset-names = "evea", "exiv", "adamv";
+ resets = <&sys_rst 41>, <&sys_rst 42>, <&adamv_rst 0>;
+ #sound-dai-cells = <1>;
+ };
+
adamv@57920000 {
compatible = "socionext,uniphier-ld20-adamv",
"simple-mfd", "syscon";
@@ -527,3 +550,15 @@
};
#include "uniphier-pinctrl.dtsi"
+
+&pinctrl_aout {
+ drive-strength = <4>; /* default: 3.5mA */
+ group_1 {
+ pins = "AO1DACCK";
+ drive-strength = <5>; /* 5mA */
+ };
+ group_2 {
+ pins = "AO1ARC";
+ drive-strength = <11>; /* 11mA */
+ };
+};
--
2.15.0
^ permalink raw reply related
* [PATCH 7/8] MAINTAINERS: add entries for UniPhier ASoC sound drivers
From: Katsuhiro Suzuki @ 2017-11-22 11:43 UTC (permalink / raw)
To: Mark Brown, alsa-devel, Rob Herring, devicetree, Masahiro Yamada
Cc: Masami Hiramatsu, Jassi Brar, linux-arm-kernel, linux-kernel,
Katsuhiro Suzuki
In-Reply-To: <20171122114321.29196-1-suzuki.katsuhiro@socionext.com>
Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
---
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index b05bc2c5e85c..0959fd951f80 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12579,6 +12579,12 @@ F: include/media/soc*
F: drivers/media/i2c/soc_camera/
F: drivers/media/platform/soc_camera/
+SOCIONEXT UNIPHIER SOUND DRIVER
+M: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: sound/soc/uniphier/
+
SOEKRIS NET48XX LED SUPPORT
M: Chris Boot <bootc@bootc.net>
S: Maintained
--
2.15.0
^ permalink raw reply related
* [PATCH 6/8] ASoC: uniphier: add support for UniPhier LD11/LD20 AIO driver
From: Katsuhiro Suzuki @ 2017-11-22 11:43 UTC (permalink / raw)
To: Mark Brown, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA, Masahiro Yamada
Cc: Masami Hiramatsu, Jassi Brar,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Katsuhiro Suzuki
In-Reply-To: <20171122114321.29196-1-suzuki.katsuhiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
This patch adds support for UniPhier AIO sound driver
which is included in UniPhier LD11/LD20 SoCs.
Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
---
sound/soc/uniphier/Kconfig | 10 ++
sound/soc/uniphier/Makefile | 1 +
sound/soc/uniphier/aio-ld11.c | 343 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 354 insertions(+)
create mode 100644 sound/soc/uniphier/aio-ld11.c
diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig
index 38a02879b9d5..8da628a46a9b 100644
--- a/sound/soc/uniphier/Kconfig
+++ b/sound/soc/uniphier/Kconfig
@@ -13,6 +13,16 @@ config SND_SOC_UNIPHIER_AIO
select REGMAP_MMIO
depends on SND_SOC_UNIPHIER
+config SND_SOC_UNIPHIER_LD11
+ tristate "UniPhier LD11/LD20 Device Driver"
+ depends on SND_SOC_UNIPHIER
+ select SND_SOC_UNIPHIER_AIO
+ help
+ This adds ASoC driver for Socionext UniPhier LD11/LD20
+ input and output that can be used with other codecs.
+ Select Y if you use such device.
+ If unsure select "N".
+
config SND_SOC_UNIPHIER_EVEA_CODEC
tristate "UniPhier SoC internal audio codec"
depends on SND_SOC_UNIPHIER
diff --git a/sound/soc/uniphier/Makefile b/sound/soc/uniphier/Makefile
index 705a60cb2aa5..55a77ad1192e 100644
--- a/sound/soc/uniphier/Makefile
+++ b/sound/soc/uniphier/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
snd-soc-uniphier-aio-objs := aio-regctrl.o aio-dma.o aio-core.o
+snd-soc-uniphier-aio-$(CONFIG_SND_SOC_UNIPHIER_LD11) += aio-ld11.o
obj-$(CONFIG_SND_SOC_UNIPHIER_AIO) += snd-soc-uniphier-aio.o
diff --git a/sound/soc/uniphier/aio-ld11.c b/sound/soc/uniphier/aio-ld11.c
new file mode 100644
index 000000000000..b6e3bd11f23a
--- /dev/null
+++ b/sound/soc/uniphier/aio-ld11.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Socionext UniPhier AIO ALSA driver for LD11/LD20.
+ *
+ * Copyright (c) 2016-2017 Socionext Inc.
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * 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 "aio.h"
+
+static const struct uniphier_aio_spec uniphier_aio_ld11[] = {
+ /* for HDMI PCM In, Pin:AI1Dx */
+ {
+ .name = AUD_NAME_PCMIN1,
+ .gname = AUD_GNAME_HDMI,
+ .swm = {
+ .type = PORT_TYPE_I2S,
+ .dir = PORT_DIR_INPUT,
+ .rb = { 21, 14, },
+ .ch = { 21, 14, },
+ .iif = { 5, 3, },
+ .iport = { 0, AUD_HW_PCMIN1, },
+ },
+ },
+
+ /* for SIF In, Pin:AI2Dx */
+ {
+ .name = AUD_NAME_PCMIN2,
+ .swm = {
+ .type = PORT_TYPE_I2S,
+ .dir = PORT_DIR_INPUT,
+ .rb = { 22, 15, },
+ .ch = { 22, 15, },
+ .iif = { 6, 4, },
+ .iport = { 1, AUD_HW_PCMIN2, },
+ },
+ },
+
+ /* for Line In, Pin:AI3Dx */
+ {
+ .name = AUD_NAME_PCMIN3,
+ .gname = AUD_GNAME_LINE,
+ .swm = {
+ .type = PORT_TYPE_EVE,
+ .dir = PORT_DIR_INPUT,
+ .rb = { 23, 16, },
+ .ch = { 23, 16, },
+ .iif = { 7, 5, },
+ .iport = { 2, AUD_HW_PCMIN3, },
+ },
+ },
+
+ /* for S/PDIF In, Pin:AI1IEC */
+ {
+ .name = AUD_NAME_IECIN1,
+ .gname = AUD_GNAME_IEC,
+ .swm = {
+ .type = PORT_TYPE_SPDIF,
+ .dir = PORT_DIR_INPUT,
+ .rb = { 26, 17, },
+ .ch = { 26, 17, },
+ .iif = { 10, 6, },
+ .iport = { 3, AUD_HW_IECIN1, },
+ },
+ },
+
+ /* for Speaker, Pin:AO1Dx */
+ {
+ .name = AUD_NAME_HPCMOUT1,
+ .swm = {
+ .type = PORT_TYPE_I2S,
+ .dir = PORT_DIR_OUTPUT,
+ .rb = { 0, 0, },
+ .ch = { 0, 0, },
+ .oif = { 0, 0, },
+ .oport = { 0, AUD_HW_HPCMOUT1, },
+ },
+ },
+
+ /* for HDMI PCM, Pin:AO2Dx */
+ {
+ .name = AUD_NAME_PCMOUT1,
+ .gname = AUD_GNAME_HDMI,
+ .swm = {
+ .type = PORT_TYPE_I2S,
+ .dir = PORT_DIR_OUTPUT,
+ .rb = { 0, 0, },
+ .ch = { 0, 0, },
+ .oif = { 0, 0, },
+ .oport = { 3, AUD_HW_PCMOUT1, },
+ },
+ },
+
+ /* for Line Out, Pin:LO2_x */
+ {
+ .name = AUD_NAME_PCMOUT2,
+ .gname = AUD_GNAME_LINE,
+ .swm = {
+ .type = PORT_TYPE_EVE,
+ .dir = PORT_DIR_OUTPUT,
+ .rb = { 2, 2, },
+ .ch = { 2, 2, },
+ .oif = { 2, 2, },
+ .oport = { 1, AUD_HW_PCMOUT2, },
+ },
+ },
+
+ /* for Headphone, Pin:HP1_x */
+ {
+ .name = AUD_NAME_PCMOUT3,
+ .swm = {
+ .type = PORT_TYPE_EVE,
+ .dir = PORT_DIR_OUTPUT,
+ .rb = { 3, 3, },
+ .ch = { 3, 3, },
+ .oif = { 3, 3, },
+ .oport = { 2, AUD_HW_PCMOUT3, },
+ },
+ },
+
+ /* for HW Sampling Rate Converter */
+ {
+ .name = AUD_NAME_EPCMOUT2,
+ .swm = {
+ .type = PORT_TYPE_CONV,
+ .dir = PORT_DIR_OUTPUT,
+ .rb = { 7, 5, },
+ .ch = { 7, 5, },
+ .oif = { 7, 5, },
+ .oport = { 6, AUD_HW_EPCMOUT2, },
+ .och = { 17, 12, },
+ .iif = { 1, 1, },
+ },
+ },
+
+ /* for HW Sampling Rate Converter 2 */
+ {
+ .name = AUD_NAME_EPCMOUT3,
+ .swm = {
+ .type = PORT_TYPE_CONV,
+ .dir = PORT_DIR_OUTPUT,
+ .rb = { 8, 6, },
+ .ch = { 8, 6, },
+ .oif = { 8, 6, },
+ .oport = { 7, AUD_HW_EPCMOUT3, },
+ .och = { 18, 13, },
+ .iif = { 2, 2, },
+ },
+ },
+
+ /* for S/PDIF Out, Pin:AO1IEC */
+ {
+ .name = AUD_NAME_HIECOUT1,
+ .gname = AUD_GNAME_IEC,
+ .swm = {
+ .type = PORT_TYPE_SPDIF,
+ .dir = PORT_DIR_OUTPUT,
+ .rb = { 1, 1, },
+ .ch = { 1, 1, },
+ .oif = { 1, 1, },
+ .oport = { 12, AUD_HW_HIECOUT1, },
+ },
+ },
+};
+
+static const struct snd_soc_dai_ops uniphier_aio_ops = {
+ .startup = uniphier_aio_startup,
+ .shutdown = uniphier_aio_shutdown,
+ .hw_params = uniphier_aio_hw_params,
+ .hw_free = uniphier_aio_hw_free,
+ .prepare = uniphier_aio_prepare,
+};
+
+static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = {
+ {
+ .name = AUD_GNAME_HDMI,
+ .probe = uniphier_aio_dai_probe,
+ .remove = uniphier_aio_dai_remove,
+ .suspend = uniphier_aio_dai_suspend,
+ .resume = uniphier_aio_dai_resume,
+ .playback = {
+ .stream_name = AUD_NAME_PCMOUT1,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = AUD_NAME_PCMIN1,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_32000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &uniphier_aio_ops,
+ },
+ {
+ .name = AUD_NAME_PCMIN2,
+ .probe = uniphier_aio_dai_probe,
+ .remove = uniphier_aio_dai_remove,
+ .suspend = uniphier_aio_dai_suspend,
+ .resume = uniphier_aio_dai_resume,
+ .capture = {
+ .stream_name = AUD_NAME_PCMIN2,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &uniphier_aio_ops,
+ },
+ {
+ .name = AUD_GNAME_LINE,
+ .probe = uniphier_aio_dai_probe,
+ .remove = uniphier_aio_dai_remove,
+ .suspend = uniphier_aio_dai_suspend,
+ .resume = uniphier_aio_dai_resume,
+ .playback = {
+ .stream_name = AUD_NAME_PCMOUT2,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = AUD_NAME_PCMIN3,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &uniphier_aio_ops,
+ },
+ {
+ .name = AUD_NAME_HPCMOUT1,
+ .probe = uniphier_aio_dai_probe,
+ .remove = uniphier_aio_dai_remove,
+ .suspend = uniphier_aio_dai_suspend,
+ .resume = uniphier_aio_dai_resume,
+ .playback = {
+ .stream_name = AUD_NAME_HPCMOUT1,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &uniphier_aio_ops,
+ },
+ {
+ .name = AUD_NAME_PCMOUT3,
+ .probe = uniphier_aio_dai_probe,
+ .remove = uniphier_aio_dai_remove,
+ .suspend = uniphier_aio_dai_suspend,
+ .resume = uniphier_aio_dai_resume,
+ .playback = {
+ .stream_name = AUD_NAME_PCMOUT3,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &uniphier_aio_ops,
+ },
+ {
+ .name = AUD_NAME_HIECOUT1,
+ .probe = uniphier_aio_dai_probe,
+ .remove = uniphier_aio_dai_remove,
+ .suspend = uniphier_aio_dai_suspend,
+ .resume = uniphier_aio_dai_resume,
+ .playback = {
+ .stream_name = AUD_NAME_HIECOUT1,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &uniphier_aio_ops,
+ },
+ {
+ .name = AUD_NAME_EPCMOUT2,
+ .probe = uniphier_aio_dai_probe,
+ .remove = uniphier_aio_dai_remove,
+ .suspend = uniphier_aio_dai_suspend,
+ .resume = uniphier_aio_dai_resume,
+ .playback = {
+ .stream_name = AUD_NAME_EPCMOUT2,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_32000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &uniphier_aio_ops,
+ },
+ {
+ .name = AUD_NAME_EPCMOUT3,
+ .probe = uniphier_aio_dai_probe,
+ .remove = uniphier_aio_dai_remove,
+ .suspend = uniphier_aio_dai_suspend,
+ .resume = uniphier_aio_dai_resume,
+ .playback = {
+ .stream_name = AUD_NAME_EPCMOUT3,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_32000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &uniphier_aio_ops,
+ },
+};
+
+const struct uniphier_aio_chip_spec uniphier_aio_ld11_spec = {
+ .aio_specs = uniphier_aio_ld11,
+ .num_aio_specs = ARRAY_SIZE(uniphier_aio_ld11),
+ .dais = uniphier_aio_dai_ld11,
+ .num_dais = ARRAY_SIZE(uniphier_aio_dai_ld11),
+ .addr_ext = 0,
+};
+
+const struct uniphier_aio_chip_spec uniphier_aio_ld20_spec = {
+ .aio_specs = uniphier_aio_ld11,
+ .num_aio_specs = ARRAY_SIZE(uniphier_aio_ld11),
+ .dais = uniphier_aio_dai_ld11,
+ .num_dais = ARRAY_SIZE(uniphier_aio_dai_ld11),
+ .addr_ext = 1,
+};
--
2.15.0
--
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 5/8] ASoC: uniphier: add support for UniPhier AIO driver
From: Katsuhiro Suzuki @ 2017-11-22 11:43 UTC (permalink / raw)
To: Mark Brown, alsa-devel, Rob Herring, devicetree, Masahiro Yamada
Cc: Masami Hiramatsu, Jassi Brar, linux-arm-kernel, linux-kernel,
Katsuhiro Suzuki
In-Reply-To: <20171122114321.29196-1-suzuki.katsuhiro@socionext.com>
This patch adds for UniPhier AIO audio sound system.
This provides commonly used APIs for input/output
control registers and DMA interfaces for UniPhier AIO.
This module provides all sound devices for I2S, S/PDIF and so on.
Since the AIO has mixed register map for those I/Os, it is hard
to split register areas for each sound devices.
Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
---
sound/soc/uniphier/Kconfig | 5 +
sound/soc/uniphier/Makefile | 4 +
sound/soc/uniphier/aio-core.c | 368 +++++++++++++++++++++
sound/soc/uniphier/aio-dma.c | 266 +++++++++++++++
sound/soc/uniphier/aio-regctrl.c | 699 +++++++++++++++++++++++++++++++++++++++
sound/soc/uniphier/aio-regctrl.h | 495 +++++++++++++++++++++++++++
sound/soc/uniphier/aio.h | 261 +++++++++++++++
7 files changed, 2098 insertions(+)
create mode 100644 sound/soc/uniphier/aio-core.c
create mode 100644 sound/soc/uniphier/aio-dma.c
create mode 100644 sound/soc/uniphier/aio-regctrl.c
create mode 100644 sound/soc/uniphier/aio-regctrl.h
create mode 100644 sound/soc/uniphier/aio.h
diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig
index 02886a457eaf..38a02879b9d5 100644
--- a/sound/soc/uniphier/Kconfig
+++ b/sound/soc/uniphier/Kconfig
@@ -8,6 +8,11 @@ config SND_SOC_UNIPHIER
audio interfaces to support below.
If unsure select "N".
+config SND_SOC_UNIPHIER_AIO
+ tristate
+ select REGMAP_MMIO
+ depends on SND_SOC_UNIPHIER
+
config SND_SOC_UNIPHIER_EVEA_CODEC
tristate "UniPhier SoC internal audio codec"
depends on SND_SOC_UNIPHIER
diff --git a/sound/soc/uniphier/Makefile b/sound/soc/uniphier/Makefile
index 3be00d72f5e5..705a60cb2aa5 100644
--- a/sound/soc/uniphier/Makefile
+++ b/sound/soc/uniphier/Makefile
@@ -1,3 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
+snd-soc-uniphier-aio-objs := aio-regctrl.o aio-dma.o aio-core.o
+
+obj-$(CONFIG_SND_SOC_UNIPHIER_AIO) += snd-soc-uniphier-aio.o
+
snd-soc-uniphier-evea-objs := evea.o
obj-$(CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC) += snd-soc-uniphier-evea.o
diff --git a/sound/soc/uniphier/aio-core.c b/sound/soc/uniphier/aio-core.c
new file mode 100644
index 000000000000..08f33f2c7c99
--- /dev/null
+++ b/sound/soc/uniphier/aio-core.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Socionext UniPhier AIO ALSA driver.
+ *
+ * Copyright (c) 2016-2017 Socionext Inc.
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * 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/err.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include "aio.h"
+#include "aio-regctrl.h"
+
+static int match_name(const struct uniphier_aio_spec *spec,
+ const char *name, int direction)
+{
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
+ spec->swm.dir != PORT_DIR_OUTPUT) {
+ return 0;
+ }
+
+ if (direction == SNDRV_PCM_STREAM_CAPTURE &&
+ spec->swm.dir != PORT_DIR_INPUT) {
+ return 0;
+ }
+
+ if (spec->name && strcmp(spec->name, name) == 0)
+ return 1;
+
+ if (spec->gname && strcmp(spec->gname, name) == 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * uniphier_aio_find_spec - find HW specification info by name
+ * @aio: the AIO device pointer
+ * @name: name of device
+ * @direction: the direction of substream, SNDRV_PCM_STREAM_*
+ *
+ * Find hardware specification information from list by device name. This
+ * information is used for telling the difference of SoCs to driver.
+ *
+ * Specification list is array of 'struct uniphier_aio_spec' which is defined
+ * in each drivers (see: aio-i2s.c).
+ *
+ * Return: The pointer of hardware specification of AIO if successful,
+ * otherwise NULL on error.
+ */
+const struct uniphier_aio_spec *find_spec(struct uniphier_aio *aio,
+ const char *name, int direction)
+{
+ const struct uniphier_aio_chip_spec *chip_spec = aio->chip->chip_spec;
+ int i;
+
+ for (i = 0; i < chip_spec->num_aio_specs; i++) {
+ const struct uniphier_aio_spec *spec =
+ &chip_spec->aio_specs[i];
+
+ if (!match_name(spec, name, direction))
+ continue;
+
+ return spec;
+ }
+
+ return NULL;
+}
+
+int uniphier_aio_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct uniphier_aio *aio = uniphier_priv(dai);
+ struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
+
+ sub->substream = substream;
+ aio->dai = dai;
+
+ uniphier_aio_init(sub);
+
+ return 0;
+}
+
+void uniphier_aio_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct uniphier_aio *aio = uniphier_priv(dai);
+ struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
+
+ sub->substream = NULL;
+ aio->dai = NULL;
+}
+
+int uniphier_aio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct uniphier_aio *aio = uniphier_priv(dai);
+ struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
+
+ sub->params = *params;
+ sub->setting = 1;
+
+ uniphier_aio_port_reset(sub);
+ uniphier_aio_srcport_reset(sub);
+
+ return 0;
+}
+
+int uniphier_aio_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct uniphier_aio *aio = uniphier_priv(dai);
+ struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
+
+ sub->setting = 0;
+
+ return 0;
+}
+
+int uniphier_aio_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct uniphier_aio *aio = uniphier_priv(dai);
+ struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
+
+ uniphier_aio_port_set_param(sub, aio->pass_through, &sub->params);
+ uniphier_aio_srcport_set_param(sub, &sub->params);
+ if (sub->swm->type == PORT_TYPE_SPDIF && aio->pass_through)
+ uniphier_aio_oport_set_stream_type(sub, IEC61937_PC_AC3);
+ uniphier_aio_port_set_enable(sub, 1);
+
+ uniphier_aio_if_set_param(sub, aio->pass_through);
+ if (sub->swm->type == PORT_TYPE_CONV) {
+ uniphier_aio_srcif_set_param(sub);
+ uniphier_aio_srcch_set_param(sub);
+ uniphier_aio_srcch_set_enable(sub, 1);
+ }
+
+ return 0;
+}
+
+int uniphier_aio_dai_probe(struct snd_soc_dai *dai)
+{
+ struct uniphier_aio *aio = uniphier_priv(dai);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
+ struct uniphier_aio_sub *sub = &aio->sub[i];
+ const struct uniphier_aio_spec *spec;
+
+ spec = find_spec(aio, dai->name, i);
+ if (!spec)
+ continue;
+
+ sub->swm = &spec->swm;
+ sub->spec = spec;
+ }
+
+ uniphier_aio_chip_init(aio->chip);
+ aio->chip->active = 1;
+
+ return 0;
+}
+
+int uniphier_aio_dai_remove(struct snd_soc_dai *dai)
+{
+ struct uniphier_aio *aio = uniphier_priv(dai);
+
+ aio->chip->active = 0;
+
+ return 0;
+}
+
+int uniphier_aio_dai_suspend(struct snd_soc_dai *dai)
+{
+ struct uniphier_aio *aio = uniphier_priv(dai);
+
+ reset_control_assert(aio->chip->rst);
+ clk_disable_unprepare(aio->chip->clk);
+
+ return 0;
+}
+
+int uniphier_aio_dai_resume(struct snd_soc_dai *dai)
+{
+ struct uniphier_aio *aio = uniphier_priv(dai);
+ int ret, i;
+
+ if (!aio->chip->active)
+ return 0;
+
+ ret = clk_prepare_enable(aio->chip->clk);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(aio->chip->rst);
+ if (ret)
+ goto err_out_clock;
+
+ uniphier_aio_chip_init(aio->chip);
+
+ for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
+ struct uniphier_aio_sub *sub = &aio->sub[i];
+
+ if (!sub->spec || !sub->substream)
+ continue;
+
+ uniphier_aio_init(sub);
+
+ if (!sub->setting)
+ continue;
+
+ uniphier_aio_port_reset(sub);
+ uniphier_aio_srcport_reset(sub);
+ }
+
+ return 0;
+
+err_out_clock:
+ clk_disable_unprepare(aio->chip->clk);
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver uniphier_aio_component = {
+ .name = "uniphier-aio",
+};
+
+static int uniphier_aio_probe(struct platform_device *pdev)
+{
+ struct uniphier_aio_chip *chip;
+ struct device *dev = &pdev->dev;
+ int ret, i, j;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->chip_spec = of_device_get_match_data(dev);
+ if (!chip->chip_spec)
+ return -EINVAL;
+
+ chip->clk = devm_clk_get(dev, "aio");
+ if (IS_ERR(chip->clk))
+ return PTR_ERR(chip->clk);
+
+ chip->rst = devm_reset_control_get_shared(dev, "aio");
+ if (IS_ERR(chip->rst))
+ return PTR_ERR(chip->rst);
+
+ chip->num_aios = chip->chip_spec->num_dais;
+ chip->aios = devm_kzalloc(dev,
+ sizeof(struct uniphier_aio) * chip->num_aios,
+ GFP_KERNEL);
+ if (!chip->aios)
+ return -ENOMEM;
+
+ for (i = 0; i < chip->num_aios; i++) {
+ struct uniphier_aio *aio = &chip->aios[i];
+
+ aio->chip = chip;
+
+ for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
+ struct uniphier_aio_sub *sub = &aio->sub[j];
+
+ sub->aio = aio;
+ spin_lock_init(&sub->spin);
+ }
+ }
+
+ chip->pdev = pdev;
+ platform_set_drvdata(pdev, chip);
+
+ ret = clk_prepare_enable(chip->clk);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(chip->rst);
+ if (ret)
+ goto err_out_clock;
+
+ ret = devm_snd_soc_register_component(dev, &uniphier_aio_component,
+ chip->chip_spec->dais,
+ chip->chip_spec->num_dais);
+ if (ret) {
+ dev_err(dev, "Register component failed.\n");
+ goto err_out_reset;
+ }
+
+ ret = uniphier_aiodma_soc_register_platform(pdev);
+ if (ret) {
+ dev_err(dev, "Register platform failed.\n");
+ goto err_out_reset;
+ }
+
+ return 0;
+
+err_out_reset:
+ reset_control_assert(chip->rst);
+
+err_out_clock:
+ clk_disable_unprepare(chip->clk);
+
+ return ret;
+}
+
+static int uniphier_aio_remove(struct platform_device *pdev)
+{
+ struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
+
+ reset_control_assert(chip->rst);
+ clk_disable_unprepare(chip->clk);
+
+ return 0;
+}
+
+static const struct of_device_id uniphier_aio_of_match[] = {
+#ifdef CONFIG_SND_SOC_UNIPHIER_LD11
+ {
+ .compatible = "socionext,uniphier-ld11-aio",
+ .data = &uniphier_aio_ld11_spec,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-aio",
+ .data = &uniphier_aio_ld20_spec,
+ },
+#endif /* CONFIG_SND_SOC_UNIPHIER_LD11 */
+ {},
+};
+MODULE_DEVICE_TABLE(of, uniphier_aio_of_match);
+
+static struct platform_driver uniphier_aio_driver = {
+ .driver = {
+ .name = "snd-uniphier-aio",
+ .of_match_table = of_match_ptr(uniphier_aio_of_match),
+ },
+ .probe = uniphier_aio_probe,
+ .remove = uniphier_aio_remove,
+};
+module_platform_driver(uniphier_aio_driver);
+
+MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
+MODULE_DESCRIPTION("UniPhier AIO ALSA driver.");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/uniphier/aio-dma.c b/sound/soc/uniphier/aio-dma.c
new file mode 100644
index 000000000000..bac0eb37c2e5
--- /dev/null
+++ b/sound/soc/uniphier/aio-dma.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Socionext UniPhier AIO DMA driver.
+ *
+ * Copyright (c) 2016-2017 Socionext Inc.
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * 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/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include "aio.h"
+#include "aio-regctrl.h"
+
+static struct snd_pcm_hardware uniphier_aiodma_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .period_bytes_min = 256,
+ .period_bytes_max = 4096,
+ .periods_min = 4,
+ .periods_max = 1024,
+ .buffer_bytes_max = 128 * 1024,
+};
+
+static irqreturn_t uniphier_aiodma_irq(int irq, void *p)
+{
+ struct platform_device *pdev = p;
+ struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
+ irqreturn_t ret = IRQ_NONE;
+ int i, j;
+
+ for (i = 0; i < chip->num_aios; i++) {
+ struct uniphier_aio *aio = &chip->aios[i];
+
+ for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
+ struct uniphier_aio_sub *sub = &aio->sub[j];
+
+ if (!sub->running)
+ continue;
+
+ spin_lock(&sub->spin);
+ uniphier_aio_rb_sync(sub);
+ uniphier_aio_rb_clear_int(sub);
+ spin_unlock(&sub->spin);
+
+ snd_pcm_period_elapsed(sub->substream);
+
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ return ret;
+}
+
+static int uniphier_aiodma_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_soc_set_runtime_hwparams(substream, &uniphier_aiodma_hw);
+
+ return snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256);
+}
+
+static int uniphier_aiodma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ substream->runtime->dma_bytes = params_buffer_bytes(params);
+
+ return 0;
+}
+
+static int uniphier_aiodma_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ substream->runtime->dma_bytes = 0;
+
+ return 0;
+}
+
+static int uniphier_aiodma_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
+ struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
+ unsigned long flags;
+
+ uniphier_aio_ch_set_param(sub);
+
+ spin_lock_irqsave(&sub->spin, flags);
+ uniphier_aio_rb_set_buffer(sub, runtime->dma_addr,
+ runtime->dma_addr + runtime->dma_bytes);
+ spin_unlock_irqrestore(&sub->spin, flags);
+
+ return 0;
+}
+
+static int uniphier_aiodma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
+ struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
+ struct device *dev = &aio->chip->pdev->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sub->spin, flags);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ uniphier_aio_rb_sync(sub);
+ uniphier_aio_ch_set_enable(sub, 1);
+ sub->running = 1;
+
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ sub->running = 0;
+ uniphier_aio_ch_set_enable(sub, 0);
+
+ break;
+ default:
+ dev_warn(dev, "Unknown trigger %d, ignored.\n", cmd);
+ break;
+ }
+ spin_unlock_irqrestore(&sub->spin, flags);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t uniphier_aiodma_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
+ struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
+ unsigned long flags;
+ snd_pcm_uframes_t pos;
+
+ spin_lock_irqsave(&sub->spin, flags);
+ uniphier_aio_rb_sync(sub);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pos = bytes_to_frames(runtime, sub->rd_offs);
+ else
+ pos = bytes_to_frames(runtime, sub->wr_offs);
+ spin_unlock_irqrestore(&sub->spin, flags);
+
+ return pos;
+}
+
+static int uniphier_aiodma_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ return remap_pfn_range(vma, vma->vm_start,
+ substream->dma_buffer.addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+static const struct snd_pcm_ops uniphier_aiodma_ops = {
+ .open = uniphier_aiodma_open,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = uniphier_aiodma_hw_params,
+ .hw_free = uniphier_aiodma_hw_free,
+ .prepare = uniphier_aiodma_prepare,
+ .trigger = uniphier_aiodma_trigger,
+ .pointer = uniphier_aiodma_pointer,
+ .mmap = uniphier_aiodma_mmap,
+};
+
+static int uniphier_aiodma_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *dev = rtd->card->snd_card->dev;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
+ if (ret)
+ return ret;
+
+ return snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_DEV, dev,
+ uniphier_aiodma_hw.buffer_bytes_max,
+ uniphier_aiodma_hw.buffer_bytes_max);
+}
+
+static void uniphier_aiodma_free(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static const struct snd_soc_platform_driver uniphier_soc_platform = {
+ .pcm_new = uniphier_aiodma_new,
+ .pcm_free = uniphier_aiodma_free,
+ .ops = &uniphier_aiodma_ops,
+};
+
+static const struct regmap_config aiodma_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x7fffc,
+ .cache_type = REGCACHE_NONE,
+};
+
+/**
+ * uniphier_aiodma_soc_register_platform - register the AIO DMA
+ * @pdev: the platform device
+ *
+ * Register and setup the DMA of AIO to transfer the sound data to device.
+ * This function need to call once at driver startup and need NOT to call
+ * unregister function.
+ *
+ * Return: Zero if successful, otherwise a negative value on error.
+ */
+int uniphier_aiodma_soc_register_platform(struct platform_device *pdev)
+{
+ struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *preg;
+ int irq, ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ preg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(preg))
+ return PTR_ERR(preg);
+
+ chip->regmap = devm_regmap_init_mmio(dev, preg,
+ &aiodma_regmap_config);
+ if (IS_ERR(chip->regmap))
+ return PTR_ERR(chip->regmap);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "Could not get irq.\n");
+ return irq;
+ }
+
+ ret = devm_request_irq(dev, irq, uniphier_aiodma_irq,
+ IRQF_SHARED, dev_name(dev), pdev);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_platform(dev, &uniphier_soc_platform);
+}
diff --git a/sound/soc/uniphier/aio-regctrl.c b/sound/soc/uniphier/aio-regctrl.c
new file mode 100644
index 000000000000..1e3623f2df51
--- /dev/null
+++ b/sound/soc/uniphier/aio-regctrl.c
@@ -0,0 +1,699 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Socionext UniPhier AIO ALSA driver.
+ *
+ * Copyright (c) 2016-2017 Socionext Inc.
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * 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/bitfield.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include "aio.h"
+#include "aio-regctrl.h"
+
+/**
+ * uniphier_aio_chip_init - initialize AIO whole settings
+ * @chip: the AIO chip pointer
+ *
+ * Sets AIO fixed and whole device settings to AIO.
+ * This function need to call once at driver startup.
+ *
+ * The register area that is changed by this function is shared by all
+ * modules of AIO. But there is not race condition since this function
+ * has always set the same initialize values.
+ *
+ * Return: Zero if successful, otherwise a negative value on error.
+ */
+int uniphier_aio_chip_init(struct uniphier_aio_chip *chip)
+{
+ struct regmap *r = chip->regmap;
+
+ regmap_update_bits(r, A2APLLCTR0,
+ A2APLLCTR0_APLLXPOW_MASK,
+ A2APLLCTR0_APLLXPOW_PWON);
+
+ regmap_update_bits(r, A2APLLCTR1, A2APLLCTR1_APLL_MASK,
+ A2APLLCTR1_APLLF2_33MHZ | A2APLLCTR1_APLLA2_33MHZ |
+ A2APLLCTR1_APLLF1_36MHZ | A2APLLCTR1_APLLA1_36MHZ);
+
+ regmap_update_bits(r, A2EXMCLKSEL0,
+ A2EXMCLKSEL0_EXMCLK_MASK,
+ A2EXMCLKSEL0_EXMCLK_OUTPUT);
+
+ regmap_update_bits(r, A2AIOINPUTSEL, A2AIOINPUTSEL_RXSEL_MASK,
+ A2AIOINPUTSEL_RXSEL_PCMI1_HDMIRX1 |
+ A2AIOINPUTSEL_RXSEL_PCMI2_SIF |
+ A2AIOINPUTSEL_RXSEL_PCMI3_EVEA |
+ A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1);
+
+ if (chip->chip_spec->addr_ext)
+ regmap_update_bits(r, CDA2D_TEST, CDA2D_TEST_DDR_MODE_MASK,
+ CDA2D_TEST_DDR_MODE_EXTON0);
+ else
+ regmap_update_bits(r, CDA2D_TEST, CDA2D_TEST_DDR_MODE_MASK,
+ CDA2D_TEST_DDR_MODE_EXTOFF1);
+
+ return 0;
+}
+
+/**
+ * uniphier_aio_init - initialize AIO substream
+ * @sub: the AIO substream pointer
+ *
+ * Sets fixed settings of each AIO substreams.
+ * This function need to call once at substream startup.
+ *
+ * Return: Zero if successful, otherwise a negative value on error.
+ */
+int uniphier_aio_init(struct uniphier_aio_sub *sub)
+{
+ struct device *dev = &sub->aio->chip->pdev->dev;
+ struct regmap *r = sub->aio->chip->regmap;
+
+ regmap_write(r, A2RBNMAPCTR0(sub->swm->rb.hw),
+ MAPCTR0_EN | sub->swm->rb.map);
+ regmap_write(r, A2CHNMAPCTR0(sub->swm->ch.hw),
+ MAPCTR0_EN | sub->swm->ch.map);
+
+ switch (sub->swm->type) {
+ case PORT_TYPE_I2S:
+ case PORT_TYPE_SPDIF:
+ case PORT_TYPE_EVE:
+ if (sub->swm->dir == PORT_DIR_INPUT) {
+ regmap_write(r, A2IIFNMAPCTR0(sub->swm->iif.hw),
+ MAPCTR0_EN | sub->swm->iif.map);
+ regmap_write(r, A2IPORTNMAPCTR0(sub->swm->iport.hw),
+ MAPCTR0_EN | sub->swm->iport.map);
+ } else {
+ regmap_write(r, A2OIFNMAPCTR0(sub->swm->oif.hw),
+ MAPCTR0_EN | sub->swm->oif.map);
+ regmap_write(r, A2OPORTNMAPCTR0(sub->swm->oport.hw),
+ MAPCTR0_EN | sub->swm->oport.map);
+ }
+ break;
+ case PORT_TYPE_CONV:
+ regmap_write(r, A2OIFNMAPCTR0(sub->swm->oif.hw),
+ MAPCTR0_EN | sub->swm->oif.map);
+ regmap_write(r, A2OPORTNMAPCTR0(sub->swm->oport.hw),
+ MAPCTR0_EN | sub->swm->oport.map);
+ regmap_write(r, A2CHNMAPCTR0(sub->swm->och.hw),
+ MAPCTR0_EN | sub->swm->och.map);
+ regmap_write(r, A2IIFNMAPCTR0(sub->swm->iif.hw),
+ MAPCTR0_EN | sub->swm->iif.map);
+ break;
+ default:
+ dev_err(dev, "Unknown port type %d.\n", sub->swm->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * uniphier_aio_port_reset - reset AIO port block
+ * @sub: the AIO substream pointer
+ *
+ * Resets the digital signal input/output port block of AIO.
+ *
+ * Return: Zero if successful, otherwise a negative value on error.
+ */
+int uniphier_aio_port_reset(struct uniphier_aio_sub *sub)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+
+ if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_write(r, AOUTRSTCTR0, BIT(sub->swm->oport.map));
+ regmap_write(r, AOUTRSTCTR1, BIT(sub->swm->oport.map));
+ } else {
+ regmap_update_bits(r, IPORTMXRSTCTR(sub->swm->iport.map),
+ IPORTMXRSTCTR_RSTPI_MASK,
+ IPORTMXRSTCTR_RSTPI_RESET);
+ regmap_update_bits(r, IPORTMXRSTCTR(sub->swm->iport.map),
+ IPORTMXRSTCTR_RSTPI_MASK,
+ IPORTMXRSTCTR_RSTPI_RELEASE);
+ }
+
+ return 0;
+}
+
+/**
+ * uniphier_aio_port_set_param - set parameters of AIO port block
+ * @sub: the AIO substream pointer
+ * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM.
+ * This parameter has no effect if substream is I2S or PCM.
+ * @params: hardware parameters of ALSA
+ *
+ * Set suitable setting to input/output port block of AIO to process the
+ * specified in params.
+ *
+ * Return: Zero if successful, otherwise a negative value on error.
+ */
+int uniphier_aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through,
+ const struct snd_pcm_hw_params *params)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ u32 v;
+
+ if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (pass_through)
+ v = OPORTMXCTR1_I2SLRSEL_LEFT;
+ else
+ v = OPORTMXCTR1_I2SLRSEL_I2S;
+
+ v |= OPORTMXCTR1_OUTBITSEL_24 |
+ OPORTMXCTR1_FSSEL_48;
+ regmap_write(r, OPORTMXCTR1(sub->swm->oport.map), v);
+
+ if (sub->swm->type == PORT_TYPE_EVE)
+ v = OPORTMXCTR2_ACLKSEL_A2PLL;
+ else
+ v = OPORTMXCTR2_ACLKSEL_A1;
+
+ v |= OPORTMXCTR2_MSSEL_MASTER |
+ OPORTMXCTR2_EXTLSIFSSEL_36 |
+ OPORTMXCTR2_DACCKSEL_1_2;
+ regmap_write(r, OPORTMXCTR2(sub->swm->oport.map), v);
+
+ if (pass_through)
+ v = OPORTMXCTR3_SRCSEL_STREAM |
+ OPORTMXCTR3_VALID_STREAM;
+ else
+ v = OPORTMXCTR3_SRCSEL_PCM |
+ OPORTMXCTR3_VALID_PCM;
+
+ v |= OPORTMXCTR3_IECTHUR_IECOUT |
+ OPORTMXCTR3_PMSEL_PAUSE |
+ OPORTMXCTR3_PMSW_MUTE_OFF;
+ regmap_write(r, OPORTMXCTR3(sub->swm->oport.map), v);
+ } else {
+ regmap_write(r, IPORTMXCTR1(sub->swm->iport.map),
+ IPORTMXCTR1_LRSEL_I2S | IPORTMXCTR1_OUTBITSEL_24 |
+ IPORTMXCTR1_CHSEL_ALL | IPORTMXCTR1_FSSEL_48);
+
+ v = IPORTMXCTR2_ACLKSEL_A1 |
+ IPORTMXCTR2_MSSEL_SLAVE |
+ IPORTMXCTR2_EXTLSIFSSEL_36 |
+ IPORTMXCTR2_DACCKSEL_1_2;
+ regmap_write(r, IPORTMXCTR2(sub->swm->iport.map), v);
+
+ regmap_write(r, IPORTMXACLKSEL0EX(sub->swm->iport.map),
+ IPORTMXACLKSEL0EX_ACLKSEL0EX_INTERNAL);
+ regmap_write(r, IPORTMXEXNOE(sub->swm->iport.map),
+ IPORTMXEXNOE_PCMINOE_INPUT);
+ }
+
+ return 0;
+}
+
+/**
+ * uniphier_aio_port_set_enable - start or stop of AIO port block
+ * @sub: the AIO substream pointer
+ * @enable: zero to stop the block, otherwise to start
+ *
+ * Start or stop the signal input/output port block of AIO.
+ *
+ * Return: Zero if successful, otherwise a negative value on error.
+ */
+int uniphier_aio_port_set_enable(struct uniphier_aio_sub *sub, int enable)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+
+ if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_write(r, OPORTMXPATH(sub->swm->oport.map),
+ sub->swm->oif.map);
+
+ regmap_update_bits(r, OPORTMXMASK(sub->swm->oport.map),
+ OPORTMXMASK_IUDXMSK_MASK |
+ OPORTMXMASK_IUXCKMSK_MASK |
+ OPORTMXMASK_DXMSK_MASK |
+ OPORTMXMASK_XCKMSK_MASK,
+ OPORTMXMASK_IUDXMSK_OFF |
+ OPORTMXMASK_IUXCKMSK_OFF |
+ OPORTMXMASK_DXMSK_OFF |
+ OPORTMXMASK_XCKMSK_OFF);
+
+ if (enable)
+ regmap_write(r, AOUTENCTR0, BIT(sub->swm->oport.map));
+ else
+ regmap_write(r, AOUTENCTR1, BIT(sub->swm->oport.map));
+ } else {
+ regmap_update_bits(r, IPORTMXMASK(sub->swm->iport.map),
+ IPORTMXMASK_IUXCKMSK_MASK |
+ IPORTMXMASK_XCKMSK_MASK,
+ IPORTMXMASK_IUXCKMSK_OFF |
+ IPORTMXMASK_XCKMSK_OFF);
+
+ if (enable)
+ regmap_update_bits(r,
+ IPORTMXCTR2(sub->swm->iport.map),
+ IPORTMXCTR2_REQEN_MASK,
+ IPORTMXCTR2_REQEN_ENABLE);
+ else
+ regmap_update_bits(r,
+ IPORTMXCTR2(sub->swm->iport.map),
+ IPORTMXCTR2_REQEN_MASK,
+ IPORTMXCTR2_REQEN_DISABLE);
+ }
+
+ return 0;
+}
+
+/**
+ * uniphier_aio_if_set_param - set parameters of AIO DMA I/F block
+ * @sub: the AIO substream pointer
+ * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM.
+ * This parameter has no effect if substream is I2S or PCM.
+ *
+ * Set suitable setting to DMA interface block of AIO to process the
+ * specified in settings.
+ *
+ * Return: Zero if successful, otherwise a negative value on error.
+ */
+int uniphier_aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ u32 v;
+
+ if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (pass_through)
+ v = PBOUTMXCTR0_ENDIAN_0123 |
+ PBOUTMXCTR0_MEMFMT_STREAM;
+ else
+ v = PBOUTMXCTR0_ENDIAN_3210 |
+ PBOUTMXCTR0_MEMFMT_2CH;
+
+ regmap_write(r, PBOUTMXCTR0(sub->swm->oif.map), v);
+ regmap_write(r, PBOUTMXCTR1(sub->swm->oif.map), 0);
+ } else {
+ regmap_write(r, PBINMXCTR(sub->swm->iif.map),
+ PBINMXCTR_NCONNECT_CONNECT |
+ PBINMXCTR_INOUTSEL_IN |
+ (sub->swm->iport.map << PBINMXCTR_PBINSEL_SHIFT) |
+ PBINMXCTR_ENDIAN_3210 |
+ PBINMXCTR_MEMFMT_D0);
+ }
+
+ return 0;
+}
+
+/**
+ * uniphier_aio_oport_set_stream_type - set parameters of AIO port block
+ * @sub: the AIO substream pointer
+ * @pc: Pc type of IEC61937
+ *
+ * Set special setting to output port block of AIO to output the stream
+ * via S/PDIF.
+ *
+ * Return: Zero if successful, otherwise a negative value on error.
+ */
+int uniphier_aio_oport_set_stream_type(struct uniphier_aio_sub *sub,
+ enum IEC61937_PC pc)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ u32 repet = 0, pause = OPORTMXPAUDAT_PAUSEPC_CMN;
+
+ switch (pc) {
+ case IEC61937_PC_AC3:
+ repet = OPORTMXREPET_STRLENGTH_AC3 |
+ OPORTMXREPET_PMLENGTH_AC3;
+ pause |= OPORTMXPAUDAT_PAUSEPD_AC3;
+ break;
+ case IEC61937_PC_MPA:
+ repet = OPORTMXREPET_STRLENGTH_MPA |
+ OPORTMXREPET_PMLENGTH_MPA;
+ pause |= OPORTMXPAUDAT_PAUSEPD_MPA;
+ break;
+ case IEC61937_PC_MP3:
+ repet = OPORTMXREPET_STRLENGTH_MP3 |
+ OPORTMXREPET_PMLENGTH_MP3;
+ pause |= OPORTMXPAUDAT_PAUSEPD_MP3;
+ break;
+ case IEC61937_PC_DTS1:
+ repet = OPORTMXREPET_STRLENGTH_DTS1 |
+ OPORTMXREPET_PMLENGTH_DTS1;
+ pause |= OPORTMXPAUDAT_PAUSEPD_DTS1;
+ break;
+ case IEC61937_PC_DTS2:
+ repet = OPORTMXREPET_STRLENGTH_DTS2 |
+ OPORTMXREPET_PMLENGTH_DTS2;
+ pause |= OPORTMXPAUDAT_PAUSEPD_DTS2;
+ break;
+ case IEC61937_PC_DTS3:
+ repet = OPORTMXREPET_STRLENGTH_DTS3 |
+ OPORTMXREPET_PMLENGTH_DTS3;
+ pause |= OPORTMXPAUDAT_PAUSEPD_DTS3;
+ break;
+ case IEC61937_PC_AAC:
+ repet = OPORTMXREPET_STRLENGTH_AAC |
+ OPORTMXREPET_PMLENGTH_AAC;
+ pause |= OPORTMXPAUDAT_PAUSEPD_AAC;
+ break;
+ case IEC61937_PC_PAUSE:
+ /* Do nothing */
+ break;
+ }
+
+ regmap_write(r, OPORTMXREPET(sub->swm->oport.map), repet);
+ regmap_write(r, OPORTMXPAUDAT(sub->swm->oport.map), pause);
+
+ return 0;
+}
+
+/**
+ * uniphier_aio_srcport_reset - reset AIO SRC port block
+ * @sub: the AIO substream pointer
+ *
+ * Resets the digital signal input/output port with sampling rate converter
+ * block of AIO.
+ * This function has no effect if substream is not supported rate converter.
+ *
+ * Return: Zero if successful, otherwise a negative value on error.
+ */
+int uniphier_aio_srcport_reset(struct uniphier_aio_sub *sub)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+
+ if (sub->substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ regmap_write(r, AOUTSRCRSTCTR0, BIT(sub->swm->oport.map));
+ regmap_write(r, AOUTSRCRSTCTR1, BIT(sub->swm->oport.map));
+
+ return 0;
+}
+
+/**
+ * uniphier_aio_srcport_set_param - set parameters of AIO SRC port block
+ * @sub: the AIO substream pointer
+ * @params: hardware parameters of ALSA
+ *
+ * Set suitable setting to input/output port with sampling rate converter
+ * block of AIO to process the specified in params.
+ * This function has no effect if substream is not supported rate converter.
+ *
+ * Return: Zero if successful, otherwise a negative value on error.
+ */
+int uniphier_aio_srcport_set_param(struct uniphier_aio_sub *sub,
+ const struct snd_pcm_hw_params *params)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ u32 v;
+
+ if (sub->substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ regmap_write(r, OPORTMXSRC1CTR(sub->swm->oport.map),
+ OPORTMXSRC1CTR_THMODE_SRC |
+ OPORTMXSRC1CTR_SRCPATH_CALC |
+ OPORTMXSRC1CTR_SYNC_ASYNC |
+ OPORTMXSRC1CTR_FSIIPSEL_INNER |
+ OPORTMXSRC1CTR_FSISEL_ACLK);
+
+ switch (params_rate(params)) {
+ default:
+ case 48000:
+ v = OPORTMXRATE_I_ACLKSEL_APLLA1 |
+ OPORTMXRATE_I_MCKSEL_36 |
+ OPORTMXRATE_I_FSSEL_48;
+ break;
+ case 44100:
+ v = OPORTMXRATE_I_ACLKSEL_APLLA2 |
+ OPORTMXRATE_I_MCKSEL_33 |
+ OPORTMXRATE_I_FSSEL_44_1;
+ break;
+ case 32000:
+ v = OPORTMXRATE_I_ACLKSEL_APLLA1 |
+ OPORTMXRATE_I_MCKSEL_36 |
+ OPORTMXRATE_I_FSSEL_32;
+ break;
+ }
+
+ regmap_write(r, OPORTMXRATE_I(sub->swm->oport.map),
+ v | OPORTMXRATE_I_ACLKSRC_APLL |
+ OPORTMXRATE_I_LRCKSTP_STOP);
+ regmap_update_bits(r, OPORTMXRATE_I(sub->swm->oport.map),
+ OPORTMXRATE_I_LRCKSTP_MASK,
+ OPORTMXRATE_I_LRCKSTP_START);
+
+ return 0;
+}
+
+int uniphier_aio_srcif_set_param(struct uniphier_aio_sub *sub)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+
+ regmap_write(r, PBINMXCTR(sub->swm->iif.map),
+ PBINMXCTR_NCONNECT_CONNECT |
+ PBINMXCTR_INOUTSEL_OUT |
+ (sub->swm->oport.map << PBINMXCTR_PBINSEL_SHIFT) |
+ PBINMXCTR_ENDIAN_3210 |
+ PBINMXCTR_MEMFMT_D0);
+
+ return 0;
+}
+
+int uniphier_aio_srcch_set_param(struct uniphier_aio_sub *sub)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+
+ regmap_write(r, CDA2D_CHMXCTRL1(sub->swm->och.map),
+ CDA2D_CHMXCTRL1_INDSIZE_INFINITE);
+
+ regmap_write(r, CDA2D_CHMXSRCAMODE(sub->swm->och.map),
+ CDA2D_CHMXAMODE_ENDIAN_3210 |
+ CDA2D_CHMXAMODE_AUPDT_FIX |
+ CDA2D_CHMXAMODE_TYPE_NORMAL);
+
+ regmap_write(r, CDA2D_CHMXDSTAMODE(sub->swm->och.map),
+ CDA2D_CHMXAMODE_ENDIAN_3210 |
+ CDA2D_CHMXAMODE_AUPDT_INC |
+ CDA2D_CHMXAMODE_TYPE_RING |
+ (sub->swm->och.map << CDA2D_CHMXAMODE_RSSEL_SHIFT));
+
+ return 0;
+}
+
+int uniphier_aio_srcch_set_enable(struct uniphier_aio_sub *sub, int enable)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ u32 v;
+
+ if (enable)
+ v = CDA2D_STRT0_STOP_START;
+ else
+ v = CDA2D_STRT0_STOP_STOP;
+
+ regmap_write(r, CDA2D_STRT0,
+ v | BIT(sub->swm->och.map));
+
+ return 0;
+}
+
+int uniphier_aio_ch_set_param(struct uniphier_aio_sub *sub)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ u32 v;
+
+ regmap_write(r, CDA2D_CHMXCTRL1(sub->swm->ch.map),
+ CDA2D_CHMXCTRL1_INDSIZE_INFINITE);
+
+ v = CDA2D_CHMXAMODE_ENDIAN_3210 |
+ CDA2D_CHMXAMODE_AUPDT_INC |
+ CDA2D_CHMXAMODE_TYPE_NORMAL |
+ (sub->swm->rb.map << CDA2D_CHMXAMODE_RSSEL_SHIFT);
+ if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_write(r, CDA2D_CHMXSRCAMODE(sub->swm->ch.map), v);
+ else
+ regmap_write(r, CDA2D_CHMXDSTAMODE(sub->swm->ch.map), v);
+
+ return 0;
+}
+
+int uniphier_aio_ch_set_enable(struct uniphier_aio_sub *sub, int enable)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+
+ if (enable) {
+ regmap_write(r, CDA2D_STRT0,
+ CDA2D_STRT0_STOP_START | BIT(sub->swm->ch.map));
+
+ regmap_update_bits(r, INTRBIM(0),
+ BIT(sub->swm->rb.map),
+ BIT(sub->swm->rb.map));
+ } else {
+ regmap_write(r, CDA2D_STRT0,
+ CDA2D_STRT0_STOP_STOP | BIT(sub->swm->ch.map));
+
+ regmap_update_bits(r, INTRBIM(0),
+ BIT(sub->swm->rb.map),
+ 0);
+ }
+
+ return 0;
+}
+
+static u64 uniphier_aio_rb_get_rp(struct uniphier_aio_sub *sub)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ u32 pos_u, pos_l;
+ int i;
+
+ regmap_write(r, CDA2D_RDPTRLOAD,
+ CDA2D_RDPTRLOAD_LSFLAG_STORE | BIT(sub->swm->rb.map));
+ /* Wait for setup */
+ for (i = 0; i < 6; i++)
+ regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &pos_l);
+
+ regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &pos_l);
+ regmap_read(r, CDA2D_RBMXRDPTRU(sub->swm->rb.map), &pos_u);
+ pos_u = FIELD_GET(CDA2D_RBMXPTRU_PTRU_MASK, pos_u);
+
+ return ((u64)pos_u << 32) | pos_l;
+}
+
+static int uniphier_aio_rb_set_rp(struct uniphier_aio_sub *sub, u64 pos)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ u32 tmp;
+ int i;
+
+ regmap_write(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), (u32)pos);
+ regmap_write(r, CDA2D_RBMXRDPTRU(sub->swm->rb.map), (u32)(pos >> 32));
+ regmap_write(r, CDA2D_RDPTRLOAD, BIT(sub->swm->rb.map));
+ /* Wait for setup */
+ for (i = 0; i < 6; i++)
+ regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &tmp);
+
+ return 0;
+}
+
+static u64 uniphier_aio_rb_get_wp(struct uniphier_aio_sub *sub)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ u32 pos_u, pos_l;
+ int i;
+
+ regmap_write(r, CDA2D_WRPTRLOAD,
+ CDA2D_WRPTRLOAD_LSFLAG_STORE | BIT(sub->swm->rb.map));
+ /* Wait for setup */
+ for (i = 0; i < 6; i++)
+ regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &pos_l);
+
+ regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &pos_l);
+ regmap_read(r, CDA2D_RBMXWRPTRU(sub->swm->rb.map), &pos_u);
+ pos_u = FIELD_GET(CDA2D_RBMXPTRU_PTRU_MASK, pos_u);
+
+ return ((u64)pos_u << 32) | pos_l;
+}
+
+static int uniphier_aio_rb_set_wp(struct uniphier_aio_sub *sub, u64 pos)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ u32 tmp;
+ int i;
+
+ regmap_write(r, CDA2D_RBMXWRPTR(sub->swm->rb.map),
+ lower_32_bits(pos));
+ regmap_write(r, CDA2D_RBMXWRPTRU(sub->swm->rb.map),
+ upper_32_bits(pos));
+ regmap_write(r, CDA2D_WRPTRLOAD, BIT(sub->swm->rb.map));
+ /* Wait for setup */
+ for (i = 0; i < 6; i++)
+ regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &tmp);
+
+ return 0;
+}
+
+int uniphier_aio_rb_set_buffer(struct uniphier_aio_sub *sub,
+ u64 start, u64 end)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+ struct snd_pcm_runtime *runtime = sub->substream->runtime;
+ int byte_per_period = runtime->period_size *
+ runtime->channels * samples_to_bytes(runtime, 1);
+
+ regmap_write(r, CDA2D_RBMXCNFG(sub->swm->rb.map), 0);
+ regmap_write(r, CDA2D_RBMXBGNADRS(sub->swm->rb.map),
+ lower_32_bits(start));
+ regmap_write(r, CDA2D_RBMXBGNADRSU(sub->swm->rb.map),
+ upper_32_bits(start));
+ regmap_write(r, CDA2D_RBMXENDADRS(sub->swm->rb.map),
+ lower_32_bits(end));
+ regmap_write(r, CDA2D_RBMXENDADRSU(sub->swm->rb.map),
+ upper_32_bits(end));
+
+ regmap_write(r, CDA2D_RBADRSLOAD, BIT(sub->swm->rb.map));
+
+ uniphier_aio_rb_set_rp(sub, start);
+ uniphier_aio_rb_set_wp(sub, end - byte_per_period);
+
+ regmap_write(r, CDA2D_RBMXBTH(sub->swm->rb.map), byte_per_period * 2);
+ regmap_write(r, CDA2D_RBMXRTH(sub->swm->rb.map), byte_per_period * 2);
+
+ if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(r, CDA2D_RBMXIE(sub->swm->rb.map),
+ CDA2D_RBMXIX_SPACE,
+ CDA2D_RBMXIX_SPACE);
+ else
+ regmap_update_bits(r, CDA2D_RBMXIE(sub->swm->rb.map),
+ CDA2D_RBMXIX_REMAIN,
+ CDA2D_RBMXIX_REMAIN);
+
+ sub->rd_offs = 0;
+ sub->wr_offs = 0;
+
+ return 0;
+}
+
+void uniphier_aio_rb_sync(struct uniphier_aio_sub *sub)
+{
+ struct snd_pcm_runtime *runtime = sub->substream->runtime;
+ u64 start = runtime->dma_addr;
+ int byte_per_period = runtime->period_size *
+ runtime->channels * samples_to_bytes(runtime, 1);
+
+ if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sub->rd_offs = uniphier_aio_rb_get_rp(sub) - start;
+ sub->wr_offs = sub->rd_offs - byte_per_period;
+ if (sub->rd_offs < byte_per_period)
+ sub->wr_offs += runtime->dma_bytes;
+ uniphier_aio_rb_set_wp(sub, sub->wr_offs + start);
+ } else {
+ sub->wr_offs = uniphier_aio_rb_get_wp(sub) - start;
+ sub->rd_offs = sub->wr_offs - byte_per_period;
+ if (sub->wr_offs < byte_per_period)
+ sub->rd_offs += runtime->dma_bytes;
+ uniphier_aio_rb_set_rp(sub, sub->rd_offs + start);
+ }
+}
+
+int uniphier_aio_rb_clear_int(struct uniphier_aio_sub *sub)
+{
+ struct regmap *r = sub->aio->chip->regmap;
+
+ if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_write(r, CDA2D_RBMXIR(sub->swm->rb.map),
+ CDA2D_RBMXIX_SPACE);
+ else
+ regmap_write(r, CDA2D_RBMXIR(sub->swm->rb.map),
+ CDA2D_RBMXIX_REMAIN);
+
+ return 0;
+}
diff --git a/sound/soc/uniphier/aio-regctrl.h b/sound/soc/uniphier/aio-regctrl.h
new file mode 100644
index 000000000000..d9dfc8e03b89
--- /dev/null
+++ b/sound/soc/uniphier/aio-regctrl.h
@@ -0,0 +1,495 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Socionext UniPhier AIO ALSA driver.
+ *
+ * Copyright (c) 2016-2017 Socionext Inc.
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * 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 SND_UNIPHIER_AIO_REGCTRL_H__
+#define SND_UNIPHIER_AIO_REGCTRL_H__
+
+#include <linux/bitops.h>
+
+#include "aio.h"
+
+/* SW view */
+#define A2CHNMAPCTR0(n) (0x00000 + 0x40 * (n))
+#define A2RBNMAPCTR0(n) (0x01000 + 0x40 * (n))
+#define A2IPORTNMAPCTR0(n) (0x02000 + 0x40 * (n))
+#define A2IPORTNMAPCTR1(n) (0x02004 + 0x40 * (n))
+#define A2IIFNMAPCTR0(n) (0x03000 + 0x40 * (n))
+#define A2OPORTNMAPCTR0(n) (0x04000 + 0x40 * (n))
+#define A2OPORTNMAPCTR1(n) (0x04004 + 0x40 * (n))
+#define A2OPORTNMAPCTR2(n) (0x04008 + 0x40 * (n))
+#define A2OIFNMAPCTR0(n) (0x05000 + 0x40 * (n))
+#define A2ATNMAPCTR0(n) (0x06000 + 0x40 * (n))
+
+#define MAPCTR0_EN 0x80000000
+
+/* CTL */
+#define A2APLLCTR0 0x07000
+#define A2APLLCTR0_APLLXPOW_MASK GENMASK(3, 0)
+#define A2APLLCTR0_APLLXPOW_PWOFF (0x0 << 0)
+#define A2APLLCTR0_APLLXPOW_PWON (0xf << 0)
+#define A2APLLCTR1 0x07004
+#define A2APLLCTR1_APLL_MASK (A2APLLCTR1_APLLF2_MASK | \
+ A2APLLCTR1_APLLA2_MASK | \
+ A2APLLCTR1_APLLF1_MASK | \
+ A2APLLCTR1_APLLA1_MASK)
+#define A2APLLCTR1_APLLF2_MASK 0x00080808
+#define A2APLLCTR1_APLLF2_33MHZ 0x00000008
+#define A2APLLCTR1_APLLA2_MASK 0x00040404
+#define A2APLLCTR1_APLLA2_33MHZ 0x00000004
+#define A2APLLCTR1_APLLF1_MASK 0x00020202
+#define A2APLLCTR1_APLLF1_36MHZ 0x00000000
+#define A2APLLCTR1_APLLA1_MASK 0x00010101
+#define A2APLLCTR1_APLLA1_36MHZ 0x00000000
+#define A2EXMCLKSEL0 0x07030
+#define A2EXMCLKSEL0_EXMCLK_MASK GENMASK(2, 0)
+#define A2EXMCLKSEL0_EXMCLK_OUTPUT (0x0 << 0)
+#define A2EXMCLKSEL0_EXMCLK_INPUT (0x7 << 0)
+#define A2SSIFSW 0x07050
+#define A2CH22_2CTR 0x07054
+#define A2AIOINPUTSEL 0x070e0
+#define A2AIOINPUTSEL_RXSEL_PCMI1_MASK GENMASK(2, 0)
+#define A2AIOINPUTSEL_RXSEL_PCMI1_HDMIRX1 (0x2 << 0)
+#define A2AIOINPUTSEL_RXSEL_PCMI2_MASK GENMASK(6, 4)
+#define A2AIOINPUTSEL_RXSEL_PCMI2_SIF (0x7 << 4)
+#define A2AIOINPUTSEL_RXSEL_PCMI3_MASK GENMASK(10, 8)
+#define A2AIOINPUTSEL_RXSEL_PCMI3_EVEA (0x1 << 8)
+#define A2AIOINPUTSEL_RXSEL_IECI1_MASK GENMASK(14, 12)
+#define A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1 (0x2 << 12)
+#define A2AIOINPUTSEL_RXSEL_MASK (A2AIOINPUTSEL_RXSEL_PCMI1_MASK | \
+ A2AIOINPUTSEL_RXSEL_PCMI2_MASK | \
+ A2AIOINPUTSEL_RXSEL_PCMI3_MASK | \
+ A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1)
+
+/* INTC */
+#define INTCHIM(m) (0x9028 + 0x80 * (m))
+#define INTRBIM(m) (0x9030 + 0x80 * (m))
+#define INTCHID(m) (0xa028 + 0x80 * (m))
+#define INTRBID(m) (0xa030 + 0x80 * (m))
+
+/* AIN(PCMINN) */
+#define IPORTMXCTR1(n) (0x22000 + 0x400 * (n))
+#define IPORTMXCTR1_LRSEL_MASK GENMASK(11, 10)
+#define IPORTMXCTR1_LRSEL_RIGHT (0x0 << 10)
+#define IPORTMXCTR1_LRSEL_LEFT (0x1 << 10)
+#define IPORTMXCTR1_LRSEL_I2S (0x2 << 10)
+#define IPORTMXCTR1_OUTBITSEL_MASK (0x800003 << 8)
+#define IPORTMXCTR1_OUTBITSEL_32 (0x800000 << 8)
+#define IPORTMXCTR1_OUTBITSEL_24 (0x000000 << 8)
+#define IPORTMXCTR1_OUTBITSEL_20 (0x000001 << 8)
+#define IPORTMXCTR1_OUTBITSEL_16 (0x000002 << 8)
+#define IPORTMXCTR1_CHSEL_MASK GENMASK(6, 4)
+#define IPORTMXCTR1_CHSEL_ALL (0x0 << 4)
+#define IPORTMXCTR1_CHSEL_D0_D2 (0x1 << 4)
+#define IPORTMXCTR1_CHSEL_D0 (0x2 << 4)
+#define IPORTMXCTR1_CHSEL_D1 (0x3 << 4)
+#define IPORTMXCTR1_CHSEL_D2 (0x4 << 4)
+#define IPORTMXCTR1_CHSEL_DMIX (0x5 << 4)
+#define IPORTMXCTR1_FSSEL_MASK GENMASK(3, 0)
+#define IPORTMXCTR1_FSSEL_48 (0x0 << 0)
+#define IPORTMXCTR1_FSSEL_96 (0x1 << 0)
+#define IPORTMXCTR1_FSSEL_192 (0x2 << 0)
+#define IPORTMXCTR1_FSSEL_32 (0x3 << 0)
+#define IPORTMXCTR1_FSSEL_44_1 (0x4 << 0)
+#define IPORTMXCTR1_FSSEL_88_2 (0x5 << 0)
+#define IPORTMXCTR1_FSSEL_176_4 (0x6 << 0)
+#define IPORTMXCTR1_FSSEL_16 (0x8 << 0)
+#define IPORTMXCTR1_FSSEL_22_05 (0x9 << 0)
+#define IPORTMXCTR1_FSSEL_24 (0xa << 0)
+#define IPORTMXCTR1_FSSEL_8 (0xb << 0)
+#define IPORTMXCTR1_FSSEL_11_025 (0xc << 0)
+#define IPORTMXCTR1_FSSEL_12 (0xd << 0)
+#define IPORTMXCTR2(n) (0x22004 + 0x400 * (n))
+#define IPORTMXCTR2_ACLKSEL_MASK GENMASK(19, 16)
+#define IPORTMXCTR2_ACLKSEL_A1 (0x0 << 16)
+#define IPORTMXCTR2_ACLKSEL_F1 (0x1 << 16)
+#define IPORTMXCTR2_ACLKSEL_A2 (0x2 << 16)
+#define IPORTMXCTR2_ACLKSEL_F2 (0x3 << 16)
+#define IPORTMXCTR2_ACLKSEL_A2PLL (0x4 << 16)
+#define IPORTMXCTR2_ACLKSEL_RX1 (0x5 << 16)
+#define IPORTMXCTR2_ACLKSEL_RX2 (0x6 << 16)
+#define IPORTMXCTR2_MSSEL_MASK BIT(15)
+#define IPORTMXCTR2_MSSEL_SLAVE (0x0 << 15)
+#define IPORTMXCTR2_MSSEL_MASTER (0x1 << 15)
+#define IPORTMXCTR2_EXTLSIFSSEL_MASK BIT(14)
+#define IPORTMXCTR2_EXTLSIFSSEL_36 (0x0 << 14)
+#define IPORTMXCTR2_EXTLSIFSSEL_24 (0x1 << 14)
+#define IPORTMXCTR2_DACCKSEL_MASK GENMASK(9, 8)
+#define IPORTMXCTR2_DACCKSEL_1_2 (0x0 << 8)
+#define IPORTMXCTR2_DACCKSEL_1_3 (0x1 << 8)
+#define IPORTMXCTR2_DACCKSEL_1_1 (0x2 << 8)
+#define IPORTMXCTR2_DACCKSEL_2_3 (0x3 << 8)
+#define IPORTMXCTR2_REQEN_MASK BIT(0)
+#define IPORTMXCTR2_REQEN_DISABLE (0x0 << 0)
+#define IPORTMXCTR2_REQEN_ENABLE (0x1 << 0)
+#define IPORTMXCNTCTR(n) (0x22010 + 0x400 * (n))
+#define IPORTMXCOUNTER(n) (0x22014 + 0x400 * (n))
+#define IPORTMXCNTMONI(n) (0x22018 + 0x400 * (n))
+#define IPORTMXACLKSEL0EX(n) (0x22020 + 0x400 * (n))
+#define IPORTMXACLKSEL0EX_ACLKSEL0EX_MASK GENMASK(3, 0)
+#define IPORTMXACLKSEL0EX_ACLKSEL0EX_INTERNAL (0x0 << 0)
+#define IPORTMXACLKSEL0EX_ACLKSEL0EX_EXTERNAL (0xf << 0)
+#define IPORTMXEXNOE(n) (0x22070 + 0x400 * (n))
+#define IPORTMXEXNOE_PCMINOE_MASK BIT(0)
+#define IPORTMXEXNOE_PCMINOE_OUTPUT (0x0 << 0)
+#define IPORTMXEXNOE_PCMINOE_INPUT (0x1 << 0)
+#define IPORTMXMASK(n) (0x22078 + 0x400 * (n))
+#define IPORTMXMASK_IUXCKMSK_MASK GENMASK(18, 16)
+#define IPORTMXMASK_IUXCKMSK_ON (0x0 << 16)
+#define IPORTMXMASK_IUXCKMSK_OFF (0x7 << 16)
+#define IPORTMXMASK_XCKMSK_MASK GENMASK(2, 0)
+#define IPORTMXMASK_XCKMSK_ON (0x0 << 0)
+#define IPORTMXMASK_XCKMSK_OFF (0x7 << 0)
+#define IPORTMXRSTCTR(n) (0x2207c + 0x400 * (n))
+#define IPORTMXRSTCTR_RSTPI_MASK BIT(7)
+#define IPORTMXRSTCTR_RSTPI_RELEASE (0x0 << 7)
+#define IPORTMXRSTCTR_RSTPI_RESET (0x1 << 7)
+
+/* AIN(PBinMX) */
+#define PBINMXCTR(n) (0x20200 + 0x40 * (n))
+#define PBINMXCTR_NCONNECT_MASK BIT(15)
+#define PBINMXCTR_NCONNECT_CONNECT (0x0 << 15)
+#define PBINMXCTR_NCONNECT_DISCONNECT (0x1 << 15)
+#define PBINMXCTR_INOUTSEL_MASK BIT(14)
+#define PBINMXCTR_INOUTSEL_IN (0x0 << 14)
+#define PBINMXCTR_INOUTSEL_OUT (0x1 << 14)
+#define PBINMXCTR_PBINSEL_SHIFT (8)
+#define PBINMXCTR_ENDIAN_MASK GENMASK(5, 4)
+#define PBINMXCTR_ENDIAN_3210 (0x0 << 4)
+#define PBINMXCTR_ENDIAN_0123 (0x1 << 4)
+#define PBINMXCTR_ENDIAN_1032 (0x2 << 4)
+#define PBINMXCTR_ENDIAN_2301 (0x3 << 4)
+#define PBINMXCTR_MEMFMT_MASK GENMASK(3, 0)
+#define PBINMXCTR_MEMFMT_D0 (0x0 << 0)
+#define PBINMXCTR_MEMFMT_5_1CH_DMIX (0x1 << 0)
+#define PBINMXCTR_MEMFMT_6CH (0x2 << 0)
+#define PBINMXCTR_MEMFMT_4CH (0x3 << 0)
+#define PBINMXCTR_MEMFMT_DMIX (0x4 << 0)
+#define PBINMXCTR_MEMFMT_1CH (0x5 << 0)
+#define PBINMXCTR_MEMFMT_16LR (0x6 << 0)
+#define PBINMXCTR_MEMFMT_7_1CH (0x7 << 0)
+#define PBINMXCTR_MEMFMT_7_1CH_DMIX (0x8 << 0)
+#define PBINMXCTR_MEMFMT_STREAM (0xf << 0)
+#define PBINMXPAUSECTR0(n) (0x20204 + 0x40 * (n))
+#define PBINMXPAUSECTR1(n) (0x20208 + 0x40 * (n))
+
+/* AOUT */
+#define AOUTENCTR0 0x40040
+#define AOUTENCTR1 0x40044
+#define AOUTENCTR2 0x40048
+#define AOUTRSTCTR0 0x40060
+#define AOUTRSTCTR1 0x40064
+#define AOUTRSTCTR2 0x40068
+#define AOUTSRCRSTCTR0 0x400c0
+#define AOUTSRCRSTCTR1 0x400c4
+#define AOUTSRCRSTCTR2 0x400c8
+
+/* AOUT(PCMOUTN) */
+#define OPORTMXCTR1(n) (0x42000 + 0x400 * (n))
+#define OPORTMXCTR1_I2SLRSEL_MASK (0x11 << 10)
+#define OPORTMXCTR1_I2SLRSEL_RIGHT (0x00 << 10)
+#define OPORTMXCTR1_I2SLRSEL_LEFT (0x01 << 10)
+#define OPORTMXCTR1_I2SLRSEL_I2S (0x11 << 10)
+#define OPORTMXCTR1_OUTBITSEL_MASK (0x800003 << 8)
+#define OPORTMXCTR1_OUTBITSEL_32 (0x800000 << 8)
+#define OPORTMXCTR1_OUTBITSEL_24 (0x000000 << 8)
+#define OPORTMXCTR1_OUTBITSEL_20 (0x000001 << 8)
+#define OPORTMXCTR1_OUTBITSEL_16 (0x000002 << 8)
+#define OPORTMXCTR1_FSSEL_MASK GENMASK(3, 0)
+#define OPORTMXCTR1_FSSEL_48 (0x0 << 0)
+#define OPORTMXCTR1_FSSEL_96 (0x1 << 0)
+#define OPORTMXCTR1_FSSEL_192 (0x2 << 0)
+#define OPORTMXCTR1_FSSEL_32 (0x3 << 0)
+#define OPORTMXCTR1_FSSEL_44_1 (0x4 << 0)
+#define OPORTMXCTR1_FSSEL_88_2 (0x5 << 0)
+#define OPORTMXCTR1_FSSEL_176_4 (0x6 << 0)
+#define OPORTMXCTR1_FSSEL_16 (0x8 << 0)
+#define OPORTMXCTR1_FSSEL_22_05 (0x9 << 0)
+#define OPORTMXCTR1_FSSEL_24 (0xa << 0)
+#define OPORTMXCTR1_FSSEL_8 (0xb << 0)
+#define OPORTMXCTR1_FSSEL_11_025 (0xc << 0)
+#define OPORTMXCTR1_FSSEL_12 (0xd << 0)
+#define OPORTMXCTR2(n) (0x42004 + 0x400 * (n))
+#define OPORTMXCTR2_ACLKSEL_MASK GENMASK(19, 16)
+#define OPORTMXCTR2_ACLKSEL_A1 (0x0 << 16)
+#define OPORTMXCTR2_ACLKSEL_F1 (0x1 << 16)
+#define OPORTMXCTR2_ACLKSEL_A2 (0x2 << 16)
+#define OPORTMXCTR2_ACLKSEL_F2 (0x3 << 16)
+#define OPORTMXCTR2_ACLKSEL_A2PLL (0x4 << 16)
+#define OPORTMXCTR2_ACLKSEL_RX1 (0x5 << 16)
+#define OPORTMXCTR2_ACLKSEL_RX2 (0x6 << 16)
+#define OPORTMXCTR2_MSSEL_MASK BIT(15)
+#define OPORTMXCTR2_MSSEL_SLAVE (0x0 << 15)
+#define OPORTMXCTR2_MSSEL_MASTER (0x1 << 15)
+#define OPORTMXCTR2_EXTLSIFSSEL_MASK BIT(14)
+#define OPORTMXCTR2_EXTLSIFSSEL_36 (0x0 << 14)
+#define OPORTMXCTR2_EXTLSIFSSEL_24 (0x1 << 14)
+#define OPORTMXCTR2_DACCKSEL_MASK GENMASK(9, 8)
+#define OPORTMXCTR2_DACCKSEL_1_2 (0x0 << 8)
+#define OPORTMXCTR2_DACCKSEL_1_3 (0x1 << 8)
+#define OPORTMXCTR2_DACCKSEL_1_1 (0x2 << 8)
+#define OPORTMXCTR2_DACCKSEL_2_3 (0x3 << 8)
+#define OPORTMXCTR3(n) (0x42008 + 0x400 * (n))
+#define OPORTMXCTR3_IECTHUR_MASK BIT(19)
+#define OPORTMXCTR3_IECTHUR_IECOUT (0x0 << 19)
+#define OPORTMXCTR3_IECTHUR_IECIN (0x1 << 19)
+#define OPORTMXCTR3_SRCSEL_MASK GENMASK(18, 16)
+#define OPORTMXCTR3_SRCSEL_PCM (0x0 << 16)
+#define OPORTMXCTR3_SRCSEL_STREAM (0x1 << 16)
+#define OPORTMXCTR3_SRCSEL_CDDTS (0x2 << 16)
+#define OPORTMXCTR3_VALID_MASK BIT(12)
+#define OPORTMXCTR3_VALID_PCM (0x0 << 12)
+#define OPORTMXCTR3_VALID_STREAM (0x1 << 12)
+#define OPORTMXCTR3_PMSEL_MASK BIT(3)
+#define OPORTMXCTR3_PMSEL_MUTE (0x0 << 3)
+#define OPORTMXCTR3_PMSEL_PAUSE (0x1 << 3)
+#define OPORTMXCTR3_PMSW_MASK BIT(2)
+#define OPORTMXCTR3_PMSW_MUTE_OFF (0x0 << 2)
+#define OPORTMXCTR3_PMSW_MUTE_ON (0x1 << 2)
+#define OPORTMXSRC1CTR(n) (0x4200c + 0x400 * (n))
+#define OPORTMXSRC1CTR_FSIIPNUM_SHIFT (24)
+#define OPORTMXSRC1CTR_THMODE_MASK BIT(23)
+#define OPORTMXSRC1CTR_THMODE_SRC (0x0 << 23)
+#define OPORTMXSRC1CTR_THMODE_BYPASS (0x1 << 23)
+#define OPORTMXSRC1CTR_LOCK_MASK BIT(16)
+#define OPORTMXSRC1CTR_LOCK_UNLOCK (0x0 << 16)
+#define OPORTMXSRC1CTR_LOCK_LOCK (0x1 << 16)
+#define OPORTMXSRC1CTR_SRCPATH_MASK BIT(15)
+#define OPORTMXSRC1CTR_SRCPATH_BYPASS (0x0 << 15)
+#define OPORTMXSRC1CTR_SRCPATH_CALC (0x1 << 15)
+#define OPORTMXSRC1CTR_SYNC_MASK BIT(14)
+#define OPORTMXSRC1CTR_SYNC_ASYNC (0x0 << 14)
+#define OPORTMXSRC1CTR_SYNC_SYNC (0x1 << 14)
+#define OPORTMXSRC1CTR_FSOCK_MASK GENMASK(11, 10)
+#define OPORTMXSRC1CTR_FSOCK_44_1 (0x0 << 10)
+#define OPORTMXSRC1CTR_FSOCK_48 (0x1 << 10)
+#define OPORTMXSRC1CTR_FSOCK_32 (0x2 << 10)
+#define OPORTMXSRC1CTR_FSICK_MASK GENMASK(9, 8)
+#define OPORTMXSRC1CTR_FSICK_44_1 (0x0 << 8)
+#define OPORTMXSRC1CTR_FSICK_48 (0x1 << 8)
+#define OPORTMXSRC1CTR_FSICK_32 (0x2 << 8)
+#define OPORTMXSRC1CTR_FSIIPSEL_MASK GENMASK(5, 4)
+#define OPORTMXSRC1CTR_FSIIPSEL_INNER (0x0 << 4)
+#define OPORTMXSRC1CTR_FSIIPSEL_OUTER (0x1 << 4)
+#define OPORTMXSRC1CTR_FSISEL_MASK GENMASK(3, 0)
+#define OPORTMXSRC1CTR_FSISEL_ACLK (0x0 << 0)
+#define OPORTMXSRC1CTR_FSISEL_DD (0x1 << 0)
+#define OPORTMXDSDMUTEDAT(n) (0x42020 + 0x400 * (n))
+#define OPORTMXDXDFREQMODE(n) (0x42024 + 0x400 * (n))
+#define OPORTMXDSDSEL(n) (0x42028 + 0x400 * (n))
+#define OPORTMXDSDPORT(n) (0x4202c + 0x400 * (n))
+#define OPORTMXACLKSEL0EX(n) (0x42030 + 0x400 * (n))
+#define OPORTMXPATH(n) (0x42040 + 0x400 * (n))
+#define OPORTMXSYNC(n) (0x42044 + 0x400 * (n))
+#define OPORTMXREPET(n) (0x42050 + 0x400 * (n))
+#define OPORTMXREPET_STRLENGTH_AC3 SBF_(IEC61937_FRM_STR_AC3, 16)
+#define OPORTMXREPET_STRLENGTH_MPA SBF_(IEC61937_FRM_STR_MPA, 16)
+#define OPORTMXREPET_STRLENGTH_MP3 SBF_(IEC61937_FRM_STR_MP3, 16)
+#define OPORTMXREPET_STRLENGTH_DTS1 SBF_(IEC61937_FRM_STR_DTS1, 16)
+#define OPORTMXREPET_STRLENGTH_DTS2 SBF_(IEC61937_FRM_STR_DTS2, 16)
+#define OPORTMXREPET_STRLENGTH_DTS3 SBF_(IEC61937_FRM_STR_DTS3, 16)
+#define OPORTMXREPET_STRLENGTH_AAC SBF_(IEC61937_FRM_STR_AAC, 16)
+#define OPORTMXREPET_PMLENGTH_AC3 SBF_(IEC61937_FRM_PAU_AC3, 0)
+#define OPORTMXREPET_PMLENGTH_MPA SBF_(IEC61937_FRM_PAU_MPA, 0)
+#define OPORTMXREPET_PMLENGTH_MP3 SBF_(IEC61937_FRM_PAU_MP3, 0)
+#define OPORTMXREPET_PMLENGTH_DTS1 SBF_(IEC61937_FRM_PAU_DTS1, 0)
+#define OPORTMXREPET_PMLENGTH_DTS2 SBF_(IEC61937_FRM_PAU_DTS2, 0)
+#define OPORTMXREPET_PMLENGTH_DTS3 SBF_(IEC61937_FRM_PAU_DTS3, 0)
+#define OPORTMXREPET_PMLENGTH_AAC SBF_(IEC61937_FRM_PAU_AAC, 0)
+#define OPORTMXPAUDAT(n) (0x42054 + 0x400 * (n))
+#define OPORTMXPAUDAT_PAUSEPC_CMN (IEC61937_PC_PAUSE << 16)
+#define OPORTMXPAUDAT_PAUSEPD_AC3 (IEC61937_FRM_PAU_AC3 * 4)
+#define OPORTMXPAUDAT_PAUSEPD_MPA (IEC61937_FRM_PAU_MPA * 4)
+#define OPORTMXPAUDAT_PAUSEPD_MP3 (IEC61937_FRM_PAU_MP3 * 4)
+#define OPORTMXPAUDAT_PAUSEPD_DTS1 (IEC61937_FRM_PAU_DTS1 * 4)
+#define OPORTMXPAUDAT_PAUSEPD_DTS2 (IEC61937_FRM_PAU_DTS2 * 4)
+#define OPORTMXPAUDAT_PAUSEPD_DTS3 (IEC61937_FRM_PAU_DTS3 * 4)
+#define OPORTMXPAUDAT_PAUSEPD_AAC (IEC61937_FRM_PAU_AAC * 4)
+#define OPORTMXRATE_I(n) (0x420e4 + 0x400 * (n))
+#define OPORTMXRATE_I_EQU_MASK BIT(31)
+#define OPORTMXRATE_I_EQU_NOTEQUAL (0x0 << 31)
+#define OPORTMXRATE_I_EQU_EQUAL (0x1 << 31)
+#define OPORTMXRATE_I_SRCBPMD_MASK BIT(29)
+#define OPORTMXRATE_I_SRCBPMD_BYPASS (0x0 << 29)
+#define OPORTMXRATE_I_SRCBPMD_SRC (0x1 << 29)
+#define OPORTMXRATE_I_LRCKSTP_MASK BIT(24)
+#define OPORTMXRATE_I_LRCKSTP_START (0x0 << 24)
+#define OPORTMXRATE_I_LRCKSTP_STOP (0x1 << 24)
+#define OPORTMXRATE_I_ACLKSRC_MASK GENMASK(15, 12)
+#define OPORTMXRATE_I_ACLKSRC_APLL (0x0 << 12)
+#define OPORTMXRATE_I_ACLKSRC_USB (0x1 << 12)
+#define OPORTMXRATE_I_ACLKSRC_HSC (0x3 << 12)
+/* if OPORTMXRATE_I_ACLKSRC_APLL */
+#define OPORTMXRATE_I_ACLKSEL_MASK GENMASK(11, 8)
+#define OPORTMXRATE_I_ACLKSEL_APLLA1 (0x0 << 8)
+#define OPORTMXRATE_I_ACLKSEL_APLLF1 (0x1 << 8)
+#define OPORTMXRATE_I_ACLKSEL_APLLA2 (0x2 << 8)
+#define OPORTMXRATE_I_ACLKSEL_APLLF2 (0x3 << 8)
+#define OPORTMXRATE_I_ACLKSEL_APLL (0x4 << 8)
+#define OPORTMXRATE_I_ACLKSEL_HDMI1 (0x5 << 8)
+#define OPORTMXRATE_I_ACLKSEL_HDMI2 (0x6 << 8)
+#define OPORTMXRATE_I_ACLKSEL_AI1ADCCK (0xc << 8)
+#define OPORTMXRATE_I_ACLKSEL_AI2ADCCK (0xd << 8)
+#define OPORTMXRATE_I_ACLKSEL_AI3ADCCK (0xe << 8)
+#define OPORTMXRATE_I_MCKSEL_MASK GENMASK(7, 4)
+#define OPORTMXRATE_I_MCKSEL_36 (0x0 << 4)
+#define OPORTMXRATE_I_MCKSEL_33 (0x1 << 4)
+#define OPORTMXRATE_I_MCKSEL_HSC27 (0xb << 4)
+#define OPORTMXRATE_I_FSSEL_MASK GENMASK(3, 0)
+#define OPORTMXRATE_I_FSSEL_48 (0x0 << 0)
+#define OPORTMXRATE_I_FSSEL_96 (0x1 << 0)
+#define OPORTMXRATE_I_FSSEL_192 (0x2 << 0)
+#define OPORTMXRATE_I_FSSEL_32 (0x3 << 0)
+#define OPORTMXRATE_I_FSSEL_44_1 (0x4 << 0)
+#define OPORTMXRATE_I_FSSEL_88_2 (0x5 << 0)
+#define OPORTMXRATE_I_FSSEL_176_4 (0x6 << 0)
+#define OPORTMXRATE_I_FSSEL_16 (0x8 << 0)
+#define OPORTMXRATE_I_FSSEL_22_05 (0x9 << 0)
+#define OPORTMXRATE_I_FSSEL_24 (0xa << 0)
+#define OPORTMXRATE_I_FSSEL_8 (0xb << 0)
+#define OPORTMXRATE_I_FSSEL_11_025 (0xc << 0)
+#define OPORTMXRATE_I_FSSEL_12 (0xd << 0)
+#define OPORTMXEXNOE(n) (0x420f0 + 0x400 * (n))
+#define OPORTMXMASK(n) (0x420f8 + 0x400 * (n))
+#define OPORTMXMASK_IUDXMSK_MASK GENMASK(28, 24)
+#define OPORTMXMASK_IUDXMSK_ON (0x00 << 24)
+#define OPORTMXMASK_IUDXMSK_OFF (0x1f << 24)
+#define OPORTMXMASK_IUXCKMSK_MASK GENMASK(18, 16)
+#define OPORTMXMASK_IUXCKMSK_ON (0x0 << 16)
+#define OPORTMXMASK_IUXCKMSK_OFF (0x7 << 16)
+#define OPORTMXMASK_DXMSK_MASK GENMASK(12, 8)
+#define OPORTMXMASK_DXMSK_ON (0x00 << 8)
+#define OPORTMXMASK_DXMSK_OFF (0x1f << 8)
+#define OPORTMXMASK_XCKMSK_MASK GENMASK(2, 0)
+#define OPORTMXMASK_XCKMSK_ON (0x0 << 0)
+#define OPORTMXMASK_XCKMSK_OFF (0x7 << 0)
+#define OPORTMXDEBUG(n) (0x420fc + 0x400 * (n))
+#define OPORTMXT0RSTCTR(n) (0x4211c + 0x400 * (n))
+#define OPORTMXT1RSTCTR(n) (0x4213c + 0x400 * (n))
+#define OPORTMXT2RSTCTR(n) (0x4215c + 0x400 * (n))
+#define OPORTMXT3RSTCTR(n) (0x4217c + 0x400 * (n))
+#define OPORTMXT4RSTCTR(n) (0x4219c + 0x400 * (n))
+
+#define SBF_(frame, shift) (((frame) * 2 - 1) << shift)
+
+/* AOUT(PBoutMX) */
+#define PBOUTMXCTR0(n) (0x40200 + 0x40 * (n))
+#define PBOUTMXCTR0_ENDIAN_MASK GENMASK(5, 4)
+#define PBOUTMXCTR0_ENDIAN_3210 (0x0 << 4)
+#define PBOUTMXCTR0_ENDIAN_0123 (0x1 << 4)
+#define PBOUTMXCTR0_ENDIAN_1032 (0x2 << 4)
+#define PBOUTMXCTR0_ENDIAN_2301 (0x3 << 4)
+#define PBOUTMXCTR0_MEMFMT_MASK GENMASK(3, 0)
+#define PBOUTMXCTR0_MEMFMT_10CH (0x0 << 0)
+#define PBOUTMXCTR0_MEMFMT_8CH (0x1 << 0)
+#define PBOUTMXCTR0_MEMFMT_6CH (0x2 << 0)
+#define PBOUTMXCTR0_MEMFMT_4CH (0x3 << 0)
+#define PBOUTMXCTR0_MEMFMT_2CH (0x4 << 0)
+#define PBOUTMXCTR0_MEMFMT_STREAM (0x5 << 0)
+#define PBOUTMXCTR0_MEMFMT_1CH (0x6 << 0)
+#define PBOUTMXCTR1(n) (0x40204 + 0x40 * (n))
+#define PBOUTMXINTCTR(n) (0x40208 + 0x40 * (n))
+
+/* A2D(subsystem) */
+#define CDA2D_STRT0 0x10000
+#define CDA2D_STRT0_STOP_MASK BIT(31)
+#define CDA2D_STRT0_STOP_START (0x0 << 31)
+#define CDA2D_STRT0_STOP_STOP (0x1 << 31)
+#define CDA2D_STAT0 0x10020
+#define CDA2D_TEST 0x100a0
+#define CDA2D_TEST_DDR_MODE_MASK GENMASK(3, 2)
+#define CDA2D_TEST_DDR_MODE_EXTON0 (0x0 << 2)
+#define CDA2D_TEST_DDR_MODE_EXTOFF1 (0x3 << 2)
+#define CDA2D_STRTADRSLOAD 0x100b0
+
+#define CDA2D_CHMXCTRL1(n) (0x12000 + 0x80 * (n))
+#define CDA2D_CHMXCTRL1_INDSIZE_MASK BIT(0)
+#define CDA2D_CHMXCTRL1_INDSIZE_FINITE (0x0 << 0)
+#define CDA2D_CHMXCTRL1_INDSIZE_INFINITE (0x1 << 0)
+#define CDA2D_CHMXCTRL2(n) (0x12004 + 0x80 * (n))
+#define CDA2D_CHMXSRCAMODE(n) (0x12020 + 0x80 * (n))
+#define CDA2D_CHMXDSTAMODE(n) (0x12024 + 0x80 * (n))
+#define CDA2D_CHMXAMODE_ENDIAN_MASK GENMASK(17, 16)
+#define CDA2D_CHMXAMODE_ENDIAN_3210 (0x0 << 16)
+#define CDA2D_CHMXAMODE_ENDIAN_0123 (0x1 << 16)
+#define CDA2D_CHMXAMODE_ENDIAN_1032 (0x2 << 16)
+#define CDA2D_CHMXAMODE_ENDIAN_2301 (0x3 << 16)
+#define CDA2D_CHMXAMODE_RSSEL_SHIFT (8)
+#define CDA2D_CHMXAMODE_AUPDT_MASK GENMASK(5, 4)
+#define CDA2D_CHMXAMODE_AUPDT_INC (0x0 << 4)
+#define CDA2D_CHMXAMODE_AUPDT_FIX (0x2 << 4)
+#define CDA2D_CHMXAMODE_TYPE_MASK GENMASK(3, 2)
+#define CDA2D_CHMXAMODE_TYPE_NORMAL (0x0 << 2)
+#define CDA2D_CHMXAMODE_TYPE_RING (0x1 << 2)
+#define CDA2D_CHMXSRCSTRTADRS(n) (0x12030 + 0x80 * (n))
+#define CDA2D_CHMXSRCSTRTADRSU(n) (0x12034 + 0x80 * (n))
+#define CDA2D_CHMXDSTSTRTADRS(n) (0x12038 + 0x80 * (n))
+#define CDA2D_CHMXDSTSTRTADRSU(n) (0x1203c + 0x80 * (n))
+
+/* A2D(ring buffer) */
+#define CDA2D_RBFLUSH0 0x10040
+#define CDA2D_RBADRSLOAD 0x100b4
+#define CDA2D_RDPTRLOAD 0x100b8
+#define CDA2D_RDPTRLOAD_LSFLAG_LOAD (0x0 << 31)
+#define CDA2D_RDPTRLOAD_LSFLAG_STORE (0x1 << 31)
+#define CDA2D_WRPTRLOAD 0x100bc
+#define CDA2D_WRPTRLOAD_LSFLAG_LOAD (0x0 << 31)
+#define CDA2D_WRPTRLOAD_LSFLAG_STORE (0x1 << 31)
+
+#define CDA2D_RBMXBGNADRS(n) (0x14000 + 0x80 * (n))
+#define CDA2D_RBMXBGNADRSU(n) (0x14004 + 0x80 * (n))
+#define CDA2D_RBMXENDADRS(n) (0x14008 + 0x80 * (n))
+#define CDA2D_RBMXENDADRSU(n) (0x1400c + 0x80 * (n))
+#define CDA2D_RBMXBTH(n) (0x14038 + 0x80 * (n))
+#define CDA2D_RBMXRTH(n) (0x1403c + 0x80 * (n))
+#define CDA2D_RBMXRDPTR(n) (0x14020 + 0x80 * (n))
+#define CDA2D_RBMXRDPTRU(n) (0x14024 + 0x80 * (n))
+#define CDA2D_RBMXWRPTR(n) (0x14028 + 0x80 * (n))
+#define CDA2D_RBMXWRPTRU(n) (0x1402c + 0x80 * (n))
+#define CDA2D_RBMXPTRU_PTRU_MASK GENMASK(1, 0)
+#define CDA2D_RBMXCNFG(n) (0x14030 + 0x80 * (n))
+#define CDA2D_RBMXIR(n) (0x14014 + 0x80 * (n))
+#define CDA2D_RBMXIE(n) (0x14018 + 0x80 * (n))
+#define CDA2D_RBMXID(n) (0x1401c + 0x80 * (n))
+#define CDA2D_RBMXIX_SPACE BIT(3)
+#define CDA2D_RBMXIX_REMAIN BIT(4)
+
+int uniphier_aio_chip_init(struct uniphier_aio_chip *chip);
+int uniphier_aio_init(struct uniphier_aio_sub *sub);
+int uniphier_aio_port_reset(struct uniphier_aio_sub *sub);
+int uniphier_aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through,
+ const struct snd_pcm_hw_params *params);
+int uniphier_aio_port_set_enable(struct uniphier_aio_sub *sub, int enable);
+int uniphier_aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through);
+int uniphier_aio_oport_set_stream_type(struct uniphier_aio_sub *sub,
+ enum IEC61937_PC iec61937_pc);
+int uniphier_aio_srcif_set_param(struct uniphier_aio_sub *sub);
+int uniphier_aio_srcch_set_param(struct uniphier_aio_sub *sub);
+int uniphier_aio_srcch_set_enable(struct uniphier_aio_sub *sub, int enable);
+int uniphier_aio_srcport_reset(struct uniphier_aio_sub *sub);
+int uniphier_aio_srcport_set_param(struct uniphier_aio_sub *sub,
+ const struct snd_pcm_hw_params *params);
+int uniphier_aio_ch_set_param(struct uniphier_aio_sub *sub);
+int uniphier_aio_ch_set_enable(struct uniphier_aio_sub *sub, int enable);
+int uniphier_aio_rb_set_buffer(struct uniphier_aio_sub *sub,
+ u64 start, u64 end);
+void uniphier_aio_rb_sync(struct uniphier_aio_sub *sub);
+int uniphier_aio_rb_clear_int(struct uniphier_aio_sub *sub);
+
+#endif /* SND_UNIPHIER_AIO_REGCTRL_H__ */
diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h
new file mode 100644
index 000000000000..542467688821
--- /dev/null
+++ b/sound/soc/uniphier/aio.h
@@ -0,0 +1,261 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Socionext UniPhier AIO ALSA driver.
+ *
+ * Copyright (c) 2016-2017 Socionext Inc.
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * 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 SND_UNIPHIER_AIO_H__
+#define SND_UNIPHIER_AIO_H__
+
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+struct platform_device;
+
+enum ID_PORT_TYPE {
+ PORT_TYPE_UNKNOWN,
+ PORT_TYPE_I2S,
+ PORT_TYPE_SPDIF,
+ PORT_TYPE_EVE,
+ PORT_TYPE_CONV,
+};
+
+enum ID_PORT_DIR {
+ PORT_DIR_UNKNOWN,
+ PORT_DIR_INPUT,
+ PORT_DIR_OUTPUT,
+};
+
+enum IEC61937_PC {
+ IEC61937_PC_AC3 = 0x0001,
+ IEC61937_PC_PAUSE = 0x0003,
+ IEC61937_PC_MPA = 0x0004,
+ IEC61937_PC_MP3 = 0x0005,
+ IEC61937_PC_DTS1 = 0x000b,
+ IEC61937_PC_DTS2 = 0x000c,
+ IEC61937_PC_DTS3 = 0x000d,
+ IEC61937_PC_AAC = 0x0007,
+};
+
+/* IEC61937 Repetition period of data-burst in IEC60958 frames */
+#define IEC61937_FRM_STR_AC3 1536
+#define IEC61937_FRM_STR_MPA 1152
+#define IEC61937_FRM_STR_MP3 1152
+#define IEC61937_FRM_STR_DTS1 512
+#define IEC61937_FRM_STR_DTS2 1024
+#define IEC61937_FRM_STR_DTS3 2048
+#define IEC61937_FRM_STR_AAC 1024
+
+/* IEC61937 Repetition period of Pause data-burst in IEC60958 frames */
+#define IEC61937_FRM_PAU_AC3 3
+#define IEC61937_FRM_PAU_MPA 32
+#define IEC61937_FRM_PAU_MP3 32
+#define IEC61937_FRM_PAU_DTS1 3
+#define IEC61937_FRM_PAU_DTS2 3
+#define IEC61937_FRM_PAU_DTS3 3
+#define IEC61937_FRM_PAU_AAC 32
+
+#define AUD_HW_PCMIN1 0
+#define AUD_HW_PCMIN2 1
+#define AUD_HW_PCMIN3 2
+#define AUD_HW_IECIN1 3
+#define AUD_HW_DIECIN1 4
+
+#define AUD_NAME_PCMIN1 "aio-pcmin1"
+#define AUD_NAME_PCMIN2 "aio-pcmin2"
+#define AUD_NAME_PCMIN3 "aio-pcmin3"
+#define AUD_NAME_IECIN1 "aio-iecin1"
+#define AUD_NAME_DIECIN1 "aio-diecin1"
+
+#define AUD_HW_HPCMOUT1 0
+#define AUD_HW_PCMOUT1 1
+#define AUD_HW_PCMOUT2 2
+#define AUD_HW_PCMOUT3 3
+#define AUD_HW_EPCMOUT1 4
+#define AUD_HW_EPCMOUT2 5
+#define AUD_HW_EPCMOUT3 6
+#define AUD_HW_EPCMOUT6 9
+#define AUD_HW_HIECOUT1 10
+#define AUD_HW_IECOUT1 11
+#define AUD_HW_CMASTER 31
+
+#define AUD_NAME_HPCMOUT1 "aio-hpcmout1"
+#define AUD_NAME_PCMOUT1 "aio-pcmout1"
+#define AUD_NAME_PCMOUT2 "aio-pcmout2"
+#define AUD_NAME_PCMOUT3 "aio-pcmout3"
+#define AUD_NAME_EPCMOUT1 "aio-epcmout1"
+#define AUD_NAME_EPCMOUT2 "aio-epcmout2"
+#define AUD_NAME_EPCMOUT3 "aio-epcmout3"
+#define AUD_NAME_EPCMOUT6 "aio-epcmout6"
+#define AUD_NAME_HIECOUT1 "aio-hiecout1"
+#define AUD_NAME_IECOUT1 "aio-iecout1"
+#define AUD_NAME_CMASTER "aio-cmaster"
+
+#define AUD_GNAME_HDMI "aio-hdmi"
+#define AUD_GNAME_LINE "aio-line"
+#define AUD_GNAME_IEC "aio-iec"
+
+/*
+ * This is a selector for virtual register map of AIO.
+ *
+ * map: Specify the index of virtual register map.
+ * hw : Specify the ID of real register map, selector uses this value.
+ * A meaning of this value depends specification of SoC.
+ */
+struct uniphier_aio_selector {
+ int map;
+ int hw;
+};
+
+/**
+ * 'SoftWare MAPping' setting of UniPhier AIO registers.
+ *
+ * We have to setup 'virtual' register maps to access 'real' registers of AIO.
+ * This feature is legacy and meaningless but AIO needs this to work.
+ *
+ * Each hardware blocks have own virtual register maps as following:
+ *
+ * Address Virtual Real
+ * ------- --------- ---------------
+ * 0x12000 DMAC map0 --> [selector] --> DMAC hardware 3
+ * 0x12080 DMAC map1 --> [selector] --> DMAC hardware 1
+ * ...
+ * 0x42000 Port map0 --> [selector] --> Port hardware 1
+ * 0x42400 Port map1 --> [selector] --> Port hardware 2
+ * ...
+ *
+ * ch : Input or output channel of DMAC
+ * rb : Ring buffer
+ * iport: PCM input port
+ * iif : Input interface
+ * oport: PCM output port
+ * oif : Output interface
+ * och : Output channel of DMAC for sampling rate converter
+ *
+ * These are examples for sound data paths:
+ *
+ * For caputure device:
+ * (outer of AIO) -> iport -> iif -> ch -> rb -> (CPU)
+ * For playback device:
+ * (CPU) -> rb -> ch -> oif -> oport -> (outer of AIO)
+ * For sampling rate converter device:
+ * (CPU) -> rb -> ch -> oif -> (HW SRC) -> iif -> och -> orb -> (CPU)
+ */
+struct uniphier_aio_swmap {
+ int type;
+ int dir;
+
+ struct uniphier_aio_selector ch;
+ struct uniphier_aio_selector rb;
+ struct uniphier_aio_selector iport;
+ struct uniphier_aio_selector iif;
+ struct uniphier_aio_selector oport;
+ struct uniphier_aio_selector oif;
+ struct uniphier_aio_selector och;
+};
+
+struct uniphier_aio_spec {
+ const char *name;
+ const char *gname;
+ struct uniphier_aio_swmap swm;
+};
+
+struct uniphier_aio_chip_spec {
+ const struct uniphier_aio_spec *aio_specs;
+ int num_aio_specs;
+ struct snd_soc_dai_driver *dais;
+ int num_dais;
+
+ /* DMA access mode, this is workaround for DMA hungup */
+ int addr_ext;
+};
+
+struct uniphier_aio_sub {
+ struct uniphier_aio *aio;
+
+ /* Guard sub->rd_offs and wr_offs from IRQ handler. */
+ spinlock_t spin;
+
+ const struct uniphier_aio_swmap *swm;
+ const struct uniphier_aio_spec *spec;
+
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_hw_params params;
+
+ int setting;
+ int running;
+ u64 rd_offs;
+ u64 wr_offs;
+};
+
+struct uniphier_aio {
+ struct uniphier_aio_chip *chip;
+
+ struct snd_soc_dai *dai;
+
+ struct uniphier_aio_sub sub[2];
+
+ u8 iec_status[24];
+ int pass_through;
+};
+
+struct uniphier_aio_chip {
+ struct platform_device *pdev;
+ const struct uniphier_aio_chip_spec *chip_spec;
+
+ struct uniphier_aio *aios;
+ int num_aios;
+
+ struct clk *clk;
+ struct reset_control *rst;
+ struct regmap *regmap;
+ int active;
+};
+
+static inline struct uniphier_aio *uniphier_priv(struct snd_soc_dai *dai)
+{
+ struct uniphier_aio_chip *chip = snd_soc_dai_get_drvdata(dai);
+
+ return &chip->aios[dai->id];
+}
+
+int uniphier_aiodma_soc_register_platform(struct platform_device *pdev);
+
+int uniphier_aio_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+void uniphier_aio_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int uniphier_aio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+int uniphier_aio_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int uniphier_aio_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+
+int uniphier_aio_dai_probe(struct snd_soc_dai *dai);
+int uniphier_aio_dai_remove(struct snd_soc_dai *dai);
+int uniphier_aio_dai_suspend(struct snd_soc_dai *dai);
+int uniphier_aio_dai_resume(struct snd_soc_dai *dai);
+
+extern const struct uniphier_aio_chip_spec uniphier_aio_ld11_spec;
+extern const struct uniphier_aio_chip_spec uniphier_aio_ld20_spec;
+
+#endif /* SND_UNIPHIER_AIO_H__ */
--
2.15.0
^ permalink raw reply related
* [PATCH 4/8] ASoC: uniphier: add support for UniPhier EVEA codec
From: Katsuhiro Suzuki @ 2017-11-22 11:43 UTC (permalink / raw)
To: Mark Brown, alsa-devel, Rob Herring, devicetree, Masahiro Yamada
Cc: Masami Hiramatsu, Jassi Brar, linux-arm-kernel, linux-kernel,
Katsuhiro Suzuki
In-Reply-To: <20171122114321.29196-1-suzuki.katsuhiro@socionext.com>
This patch adds EVEA codec driver. This codec core is in inside of
UniPhier SoC.
Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
---
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/uniphier/Kconfig | 19 ++
sound/soc/uniphier/Makefile | 3 +
sound/soc/uniphier/evea.c | 567 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 591 insertions(+)
create mode 100644 sound/soc/uniphier/Kconfig
create mode 100644 sound/soc/uniphier/Makefile
create mode 100644 sound/soc/uniphier/evea.c
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index d22758165496..84c3582f3982 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -71,6 +71,7 @@ source "sound/soc/stm/Kconfig"
source "sound/soc/sunxi/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
+source "sound/soc/uniphier/Kconfig"
source "sound/soc/ux500/Kconfig"
source "sound/soc/xtensa/Kconfig"
source "sound/soc/zte/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 5327f4d6c668..74cd1858d38b 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_SND_SOC) += stm/
obj-$(CONFIG_SND_SOC) += sunxi/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
+obj-$(CONFIG_SND_SOC) += uniphier/
obj-$(CONFIG_SND_SOC) += ux500/
obj-$(CONFIG_SND_SOC) += xtensa/
obj-$(CONFIG_SND_SOC) += zte/
diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig
new file mode 100644
index 000000000000..02886a457eaf
--- /dev/null
+++ b/sound/soc/uniphier/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+config SND_SOC_UNIPHIER
+ tristate "ASoC support for UniPhier"
+ depends on (ARCH_UNIPHIER || COMPILE_TEST)
+ help
+ Say Y or M if you want to add support for the Socionext
+ UniPhier SoC audio interfaces. You will also need to select the
+ audio interfaces to support below.
+ If unsure select "N".
+
+config SND_SOC_UNIPHIER_EVEA_CODEC
+ tristate "UniPhier SoC internal audio codec"
+ depends on SND_SOC_UNIPHIER
+ select REGMAP_MMIO
+ help
+ This adds Codec driver for Socionext UniPhier LD11/20 SoC
+ internal DAC. This driver supports Line In / Out and HeadPhone.
+ Select Y if you use such device.
+ If unsure select "N".
diff --git a/sound/soc/uniphier/Makefile b/sound/soc/uniphier/Makefile
new file mode 100644
index 000000000000..3be00d72f5e5
--- /dev/null
+++ b/sound/soc/uniphier/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-soc-uniphier-evea-objs := evea.o
+obj-$(CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC) += snd-soc-uniphier-evea.o
diff --git a/sound/soc/uniphier/evea.c b/sound/soc/uniphier/evea.c
new file mode 100644
index 000000000000..0cc9efff1d9a
--- /dev/null
+++ b/sound/soc/uniphier/evea.c
@@ -0,0 +1,567 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Socionext UniPhier EVEA ADC/DAC codec driver.
+ *
+ * Copyright (c) 2016-2017 Socionext Inc.
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * 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/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "evea"
+#define EVEA_RATES SNDRV_PCM_RATE_48000
+#define EVEA_FORMATS SNDRV_PCM_FMTBIT_S32_LE
+
+#define AADCPOW(n) (0x0078 + 0x04 * (n))
+#define AADCPOW_AADC_POWD BIT(0)
+#define AHPOUTPOW 0x0098
+#define AHPOUTPOW_HP_ON BIT(4)
+#define ALINEPOW 0x009c
+#define ALINEPOW_LIN2_POWD BIT(3)
+#define ALINEPOW_LIN1_POWD BIT(4)
+#define ALO1OUTPOW 0x00a8
+#define ALO1OUTPOW_LO1_ON BIT(4)
+#define ALO2OUTPOW 0x00ac
+#define ALO2OUTPOW_ADAC2_MUTE BIT(0)
+#define ALO2OUTPOW_LO2_ON BIT(4)
+#define AANAPOW 0x00b8
+#define AANAPOW_A_POWD BIT(4)
+#define ADACSEQ1(n) (0x0144 + 0x40 * (n))
+#define ADACSEQ1_MMUTE BIT(1)
+#define ADACSEQ2(n) (0x0160 + 0x40 * (n))
+#define ADACSEQ2_ADACIN_FIX BIT(0)
+#define ADAC1ODC 0x0200
+#define ADAC1ODC_HP_DIS_RES_MASK GENMASK(2, 1)
+#define ADAC1ODC_HP_DIS_RES_OFF (0x0 << 1)
+#define ADAC1ODC_HP_DIS_RES_ON (0x3 << 1)
+#define ADAC1ODC_ADAC_RAMPCLT_MASK GENMASK(8, 7)
+#define ADAC1ODC_ADAC_RAMPCLT_NORMAL (0x0 << 7)
+#define ADAC1ODC_ADAC_RAMPCLT_REDUCE (0x1 << 7)
+
+struct evea_priv {
+ struct clk *clk, *clk_exiv;
+ struct reset_control *rst, *rst_exiv, *rst_adamv;
+ struct regmap *regmap;
+
+ int switch_lin;
+ int switch_lo;
+ int switch_hp;
+};
+
+static const struct snd_soc_dapm_widget evea_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("LIN1_LP"),
+ SND_SOC_DAPM_INPUT("LIN1_RP"),
+ SND_SOC_DAPM_INPUT("LIN2_LP"),
+ SND_SOC_DAPM_INPUT("LIN2_RP"),
+ SND_SOC_DAPM_INPUT("LIN3_LP"),
+ SND_SOC_DAPM_INPUT("LIN3_RP"),
+
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("HP1_L"),
+ SND_SOC_DAPM_OUTPUT("HP1_R"),
+ SND_SOC_DAPM_OUTPUT("LO2_L"),
+ SND_SOC_DAPM_OUTPUT("LO2_R"),
+};
+
+static const struct snd_soc_dapm_route evea_routes[] = {
+ { "ADC", NULL, "LIN1_LP" },
+ { "ADC", NULL, "LIN1_RP" },
+ { "ADC", NULL, "LIN2_LP" },
+ { "ADC", NULL, "LIN2_RP" },
+ { "ADC", NULL, "LIN3_LP" },
+ { "ADC", NULL, "LIN3_RP" },
+
+ { "HP1_L", NULL, "DAC" },
+ { "HP1_R", NULL, "DAC" },
+ { "LO2_L", NULL, "DAC" },
+ { "LO2_R", NULL, "DAC" },
+};
+
+static void evea_set_power_state_on(struct evea_priv *evea)
+{
+ struct regmap *map = evea->regmap;
+
+ regmap_update_bits(map, AANAPOW, AANAPOW_A_POWD,
+ AANAPOW_A_POWD);
+
+ regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
+ ADAC1ODC_HP_DIS_RES_ON);
+
+ regmap_update_bits(map, ADAC1ODC, ADAC1ODC_ADAC_RAMPCLT_MASK,
+ ADAC1ODC_ADAC_RAMPCLT_REDUCE);
+
+ regmap_update_bits(map, ADACSEQ2(0), ADACSEQ2_ADACIN_FIX, 0);
+ regmap_update_bits(map, ADACSEQ2(1), ADACSEQ2_ADACIN_FIX, 0);
+ regmap_update_bits(map, ADACSEQ2(2), ADACSEQ2_ADACIN_FIX, 0);
+}
+
+static void evea_set_power_state_off(struct evea_priv *evea)
+{
+ struct regmap *map = evea->regmap;
+
+ regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
+ ADAC1ODC_HP_DIS_RES_ON);
+
+ regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+ regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+ regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+
+ regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0);
+ regmap_update_bits(map, ALO2OUTPOW, ALO2OUTPOW_LO2_ON, 0);
+ regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0);
+}
+
+static int evea_update_switch_lin(struct evea_priv *evea)
+{
+ struct regmap *map = evea->regmap;
+
+ if (evea->switch_lin) {
+ regmap_update_bits(map, ALINEPOW,
+ ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD,
+ ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD);
+
+ regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD,
+ AADCPOW_AADC_POWD);
+ regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD,
+ AADCPOW_AADC_POWD);
+ } else {
+ regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, 0);
+ regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, 0);
+
+ regmap_update_bits(map, ALINEPOW,
+ ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, 0);
+ }
+
+ return 0;
+}
+
+static int evea_update_switch_lo(struct evea_priv *evea)
+{
+ struct regmap *map = evea->regmap;
+
+ if (evea->switch_lo) {
+ regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, 0);
+ regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, 0);
+
+ regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON,
+ ALO1OUTPOW_LO1_ON);
+ regmap_update_bits(map, ALO2OUTPOW,
+ ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON,
+ ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON);
+ } else {
+ regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+ regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+
+ regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0);
+ regmap_update_bits(map, ALO2OUTPOW,
+ ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON,
+ 0);
+ }
+
+ return 0;
+}
+
+static int evea_update_switch_hp(struct evea_priv *evea)
+{
+ struct regmap *map = evea->regmap;
+
+ if (evea->switch_hp) {
+ regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, 0);
+
+ regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON,
+ AHPOUTPOW_HP_ON);
+
+ regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
+ ADAC1ODC_HP_DIS_RES_OFF);
+ } else {
+ regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
+ ADAC1ODC_HP_DIS_RES_ON);
+
+ regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+
+ regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0);
+ }
+
+ return 0;
+}
+
+static void evea_update_switch_all(struct evea_priv *evea)
+{
+ evea_update_switch_lin(evea);
+ evea_update_switch_lo(evea);
+ evea_update_switch_hp(evea);
+}
+
+static int evea_get_switch_lin(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = evea->switch_lin;
+
+ return 0;
+}
+
+static int evea_set_switch_lin(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ if (evea->switch_lin == ucontrol->value.integer.value[0])
+ return 0;
+
+ evea->switch_lin = ucontrol->value.integer.value[0];
+
+ return evea_update_switch_lin(evea);
+}
+
+static int evea_get_switch_lo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = evea->switch_lo;
+
+ return 0;
+}
+
+static int evea_set_switch_lo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ if (evea->switch_lo == ucontrol->value.integer.value[0])
+ return 0;
+
+ evea->switch_lo = ucontrol->value.integer.value[0];
+
+ return evea_update_switch_lo(evea);
+}
+
+static int evea_get_switch_hp(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = evea->switch_hp;
+
+ return 0;
+}
+
+static int evea_set_switch_hp(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ if (evea->switch_hp == ucontrol->value.integer.value[0])
+ return 0;
+
+ evea->switch_hp = ucontrol->value.integer.value[0];
+
+ return evea_update_switch_hp(evea);
+}
+
+static const struct snd_kcontrol_new eva_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Line Capture Switch", 0,
+ evea_get_switch_lin, evea_set_switch_lin),
+ SOC_SINGLE_BOOL_EXT("Line Playback Switch", 0,
+ evea_get_switch_lo, evea_set_switch_lo),
+ SOC_SINGLE_BOOL_EXT("Headphone Playback Switch", 0,
+ evea_get_switch_hp, evea_set_switch_hp),
+};
+
+static int evea_codec_probe(struct snd_soc_codec *codec)
+{
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ evea->switch_lin = 1;
+ evea->switch_lo = 1;
+ evea->switch_hp = 1;
+
+ evea_set_power_state_on(evea);
+ evea_update_switch_all(evea);
+
+ return 0;
+}
+
+static int evea_codec_suspend(struct snd_soc_codec *codec)
+{
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ evea_set_power_state_off(evea);
+
+ reset_control_assert(evea->rst_adamv);
+ reset_control_assert(evea->rst_exiv);
+ reset_control_assert(evea->rst);
+
+ clk_disable_unprepare(evea->clk_exiv);
+ clk_disable_unprepare(evea->clk);
+
+ return 0;
+}
+
+static int evea_codec_resume(struct snd_soc_codec *codec)
+{
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = clk_prepare_enable(evea->clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(evea->clk_exiv);
+ if (ret)
+ goto err_out_clock;
+
+ ret = reset_control_deassert(evea->rst);
+ if (ret)
+ goto err_out_clock_exiv;
+
+ ret = reset_control_deassert(evea->rst_exiv);
+ if (ret)
+ goto err_out_reset;
+
+ ret = reset_control_deassert(evea->rst_adamv);
+ if (ret)
+ goto err_out_reset_exiv;
+
+ evea_set_power_state_on(evea);
+ evea_update_switch_all(evea);
+
+ return 0;
+
+err_out_reset_exiv:
+ reset_control_assert(evea->rst_exiv);
+
+err_out_reset:
+ reset_control_assert(evea->rst);
+
+err_out_clock_exiv:
+ clk_disable_unprepare(evea->clk_exiv);
+
+err_out_clock:
+ clk_disable_unprepare(evea->clk);
+
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_evea = {
+ .probe = evea_codec_probe,
+ .suspend = evea_codec_suspend,
+ .resume = evea_codec_resume,
+
+ .component_driver = {
+ .dapm_widgets = evea_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(evea_widgets),
+ .dapm_routes = evea_routes,
+ .num_dapm_routes = ARRAY_SIZE(evea_routes),
+ .controls = eva_controls,
+ .num_controls = ARRAY_SIZE(eva_controls),
+ },
+};
+
+static struct snd_soc_dai_driver soc_dai_evea[] = {
+ {
+ .name = DRV_NAME "-line1",
+ .playback = {
+ .stream_name = "Line Out 1",
+ .formats = EVEA_FORMATS,
+ .rates = EVEA_RATES,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = "Line In 1",
+ .formats = EVEA_FORMATS,
+ .rates = EVEA_RATES,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ },
+ {
+ .name = DRV_NAME "-hp1",
+ .playback = {
+ .stream_name = "Headphone 1",
+ .formats = EVEA_FORMATS,
+ .rates = EVEA_RATES,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ },
+ {
+ .name = DRV_NAME "-lo2",
+ .playback = {
+ .stream_name = "Line Out 2",
+ .formats = EVEA_FORMATS,
+ .rates = EVEA_RATES,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ },
+};
+
+static const struct regmap_config evea_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xffc,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int evea_probe(struct platform_device *pdev)
+{
+ struct evea_priv *evea;
+ struct resource *res;
+ void __iomem *preg;
+ int ret;
+
+ evea = devm_kzalloc(&pdev->dev, sizeof(struct evea_priv), GFP_KERNEL);
+ if (!evea)
+ return -ENOMEM;
+
+ evea->clk = devm_clk_get(&pdev->dev, "evea");
+ if (IS_ERR(evea->clk))
+ return PTR_ERR(evea->clk);
+
+ evea->clk_exiv = devm_clk_get(&pdev->dev, "exiv");
+ if (IS_ERR(evea->clk_exiv))
+ return PTR_ERR(evea->clk_exiv);
+
+ evea->rst = devm_reset_control_get_shared(&pdev->dev, "evea");
+ if (IS_ERR(evea->rst))
+ return PTR_ERR(evea->rst);
+
+ evea->rst_exiv = devm_reset_control_get_shared(&pdev->dev, "exiv");
+ if (IS_ERR(evea->rst_exiv))
+ return PTR_ERR(evea->rst_exiv);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ preg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(preg))
+ return PTR_ERR(preg);
+
+ evea->regmap = devm_regmap_init_mmio(&pdev->dev, preg,
+ &evea_regmap_config);
+ if (IS_ERR(evea->regmap))
+ return PTR_ERR(evea->regmap);
+
+ ret = clk_prepare_enable(evea->clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(evea->clk_exiv);
+ if (ret)
+ goto err_out_clock;
+
+ ret = reset_control_deassert(evea->rst);
+ if (ret)
+ goto err_out_clock_exiv;
+
+ ret = reset_control_deassert(evea->rst_exiv);
+ if (ret)
+ goto err_out_reset;
+
+ /* ADAMV will hangup if EXIV reset is asserted */
+ evea->rst_adamv = devm_reset_control_get_shared(&pdev->dev, "adamv");
+ if (IS_ERR(evea->rst_adamv)) {
+ ret = PTR_ERR(evea->rst_adamv);
+ goto err_out_reset_exiv;
+ }
+
+ ret = reset_control_deassert(evea->rst_adamv);
+ if (ret)
+ goto err_out_reset_exiv;
+
+ platform_set_drvdata(pdev, evea);
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_evea,
+ soc_dai_evea, ARRAY_SIZE(soc_dai_evea));
+ if (ret)
+ goto err_out_reset_adamv;
+
+ return 0;
+
+err_out_reset_adamv:
+ reset_control_assert(evea->rst_adamv);
+
+err_out_reset_exiv:
+ reset_control_assert(evea->rst_exiv);
+
+err_out_reset:
+ reset_control_assert(evea->rst);
+
+err_out_clock_exiv:
+ clk_disable_unprepare(evea->clk_exiv);
+
+err_out_clock:
+ clk_disable_unprepare(evea->clk);
+
+ return ret;
+}
+
+static int evea_remove(struct platform_device *pdev)
+{
+ struct evea_priv *evea = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ reset_control_assert(evea->rst_adamv);
+ reset_control_assert(evea->rst_exiv);
+ reset_control_assert(evea->rst);
+
+ clk_disable_unprepare(evea->clk_exiv);
+ clk_disable_unprepare(evea->clk);
+
+ return 0;
+}
+
+static const struct of_device_id evea_of_match[] = {
+ { .compatible = "socionext,uniphier-evea", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, evea_of_match);
+
+static struct platform_driver evea_codec_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(evea_of_match),
+ },
+ .probe = evea_probe,
+ .remove = evea_remove,
+};
+module_platform_driver(evea_codec_driver);
+
+MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
+MODULE_DESCRIPTION("UniPhier EVEA codec driver");
+MODULE_LICENSE("GPL v2");
--
2.15.0
^ permalink raw reply related
* [PATCH 3/8] ASoC: uniphier: add DT bindings documentation for UniPhier AIO
From: Katsuhiro Suzuki @ 2017-11-22 11:43 UTC (permalink / raw)
To: Mark Brown, alsa-devel, Rob Herring, devicetree, Masahiro Yamada
Cc: Katsuhiro Suzuki, Jassi Brar, linux-arm-kernel, Masami Hiramatsu,
linux-kernel
In-Reply-To: <20171122114321.29196-1-suzuki.katsuhiro@socionext.com>
This patch adds DT binding documentation for UniPhier
AIO audio subsystem.
Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
---
.../devicetree/bindings/sound/uniphier,aio.txt | 36 ++++++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/uniphier,aio.txt
diff --git a/Documentation/devicetree/bindings/sound/uniphier,aio.txt b/Documentation/devicetree/bindings/sound/uniphier,aio.txt
new file mode 100644
index 000000000000..73f6c27ae4f7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/uniphier,aio.txt
@@ -0,0 +1,36 @@
+Socionext UniPhier SoC audio driver
+
+The Socionext UniPhier audio subsystem consists of I2S and S/PDIF blocks in
+the same register space.
+
+Required properties:
+- compatible : should be one of the following:
+ "socionext,uniphier-ld11-aio"
+ "socionext,uniphier-ld20-aio"
+- reg : offset and length of the register set for the device.
+- interrupts : should contain I2S or S/PDIF interrupt.
+- pinctrl-names : should be "default".
+- pinctrl-0 : defined I2S signal pins for an external codec chip.
+- clock-names : should include following entries:
+ "aio"
+- clocks : a list of phandle, should contain an entry for each
+ entry in clock-names.
+- reset-names : should include following entries:
+ "aio"
+- resets : a list of phandle, should contain an entry for each
+ entry in reset-names.
+- #sound-dai-cells: should be 1.
+
+Example:
+ audio {
+ compatible = "socionext,uniphier-ld20-aio";
+ reg = <0x56000000 0x80000>;
+ interrupts = <0 144 4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_aout>;
+ clock-names = "aio";
+ clocks = <&sys_clk 40>;
+ reset-names = "aio";
+ resets = <&sys_rst 40>;
+ #sound-dai-cells = <1>;
+ };
--
2.15.0
^ permalink raw reply related
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