Alsa-devel
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
December 2019
- 123 participants
- 374 discussions

[alsa-devel] [PATCH] ASoC: soc-core: rename snd_soc_rtdcom_add() to snd_soc_rtd_add_component()
by Kuninori Morimoto 18 Dec '19
by Kuninori Morimoto 18 Dec '19
18 Dec '19
From: Kuninori Morimoto <kuninori.morimoto.gx(a)renesas.com>
We don't have snd_soc_rtdcom_list anymore.
Let's rename snd_soc_rtdcom_add() to more understandable
snd_soc_rtd_add_component()
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx(a)renesas.com>
---
This is based on my previous soc-core cleanup patch-set.
sound/soc/soc-core.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index f0217b8..554409d 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -257,8 +257,8 @@ static inline void snd_soc_debugfs_exit(void)
#endif
-static int snd_soc_rtdcom_add(struct snd_soc_pcm_runtime *rtd,
- struct snd_soc_component *component)
+static int snd_soc_rtd_add_component(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_component *component)
{
struct snd_soc_component *comp;
int i;
@@ -1010,7 +1010,7 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
cpu->dai_name);
goto _err_defer;
}
- snd_soc_rtdcom_add(rtd, asoc_cpu_dai(rtd, i)->component);
+ snd_soc_rtd_add_component(rtd, asoc_cpu_dai(rtd, i)->component);
}
/* Find CODEC from registered CODECs */
@@ -1022,7 +1022,7 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
goto _err_defer;
}
- snd_soc_rtdcom_add(rtd, asoc_codec_dai(rtd, i)->component);
+ snd_soc_rtd_add_component(rtd, asoc_codec_dai(rtd, i)->component);
}
/* Find PLATFORM from registered PLATFORMs */
@@ -1031,7 +1031,7 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
if (!snd_soc_is_matching_component(platform, component))
continue;
- snd_soc_rtdcom_add(rtd, component);
+ snd_soc_rtd_add_component(rtd, component);
}
}
--
2.7.4
1
0

[alsa-devel] [PATCH v8 6/9] ALSA: Avoid using timespec for struct snd_timer_tread
by Arnd Bergmann 17 Dec '19
by Arnd Bergmann 17 Dec '19
17 Dec '19
From: Baolin Wang <baolin.wang(a)linaro.org>
The struct snd_timer_tread will use 'timespec' type variables to record
timestamp, which is not year 2038 safe on 32bits system.
Since the struct snd_timer_tread is passed through read() rather than
ioctl(), and the read syscall has no command number that lets us pick
between the 32-bit or 64-bit version of this structure.
Thus we introduced one new command SNDRV_TIMER_IOCTL_TREAD64 and new
struct snd_timer_tread64 replacing timespec with s64 type to handle
64bit time_t. That means we will set tu->tread = TREAD_FORMAT_64BIT
when user space has a 64bit time_t, then we will copy to user with
struct snd_timer_tread64. Otherwise we will use 32bit time_t variables
when copying to user.
Moreover this patch replaces timespec type with timespec64 type and
related y2038 safe APIs.
Signed-off-by: Baolin Wang <baolin.wang(a)linaro.org>
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
This v8 is what is in the pull request "[GIT PULL, v8] Fix year 2038
issue for sound subsystem", posted here for reference
include/uapi/sound/asound.h | 15 +++-
sound/core/timer.c | 150 +++++++++++++++++++++++++++---------
sound/core/timer_compat.c | 5 +-
3 files changed, 131 insertions(+), 39 deletions(-)
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index e0ada33afa1e..ad86c5a7a1e2 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -783,7 +783,7 @@ struct snd_timer_status {
#define SNDRV_TIMER_IOCTL_PVERSION _IOR('T', 0x00, int)
#define SNDRV_TIMER_IOCTL_NEXT_DEVICE _IOWR('T', 0x01, struct snd_timer_id)
-#define SNDRV_TIMER_IOCTL_TREAD _IOW('T', 0x02, int)
+#define SNDRV_TIMER_IOCTL_TREAD_OLD _IOW('T', 0x02, int)
#define SNDRV_TIMER_IOCTL_GINFO _IOWR('T', 0x03, struct snd_timer_ginfo)
#define SNDRV_TIMER_IOCTL_GPARAMS _IOW('T', 0x04, struct snd_timer_gparams)
#define SNDRV_TIMER_IOCTL_GSTATUS _IOWR('T', 0x05, struct snd_timer_gstatus)
@@ -796,6 +796,15 @@ struct snd_timer_status {
#define SNDRV_TIMER_IOCTL_STOP _IO('T', 0xa1)
#define SNDRV_TIMER_IOCTL_CONTINUE _IO('T', 0xa2)
#define SNDRV_TIMER_IOCTL_PAUSE _IO('T', 0xa3)
+#define SNDRV_TIMER_IOCTL_TREAD64 _IOW('T', 0xa4, int)
+
+#if __BITS_PER_LONG == 64
+#define SNDRV_TIMER_IOCTL_TREAD SNDRV_TIMER_IOCTL_TREAD_OLD
+#else
+#define SNDRV_TIMER_IOCTL_TREAD ((sizeof(__kernel_long_t) >= sizeof(time_t)) ? \
+ SNDRV_TIMER_IOCTL_TREAD_OLD : \
+ SNDRV_TIMER_IOCTL_TREAD64)
+#endif
struct snd_timer_read {
unsigned int resolution;
@@ -821,11 +830,15 @@ enum {
SNDRV_TIMER_EVENT_MRESUME = SNDRV_TIMER_EVENT_RESUME + 10,
};
+#ifndef __KERNEL__
struct snd_timer_tread {
int event;
+ __time_pad pad1;
struct timespec tstamp;
unsigned int val;
+ __time_pad pad2;
};
+#endif
/****************************************************************************
* *
diff --git a/sound/core/timer.c b/sound/core/timer.c
index d2f69039f941..4cfd8e691903 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -44,6 +44,28 @@ MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for t
MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER);
MODULE_ALIAS("devname:snd/timer");
+enum timer_tread_format {
+ TREAD_FORMAT_NONE = 0,
+ TREAD_FORMAT_TIME64,
+ TREAD_FORMAT_TIME32,
+};
+
+struct snd_timer_tread32 {
+ int event;
+ s32 tstamp_sec;
+ s32 tstamp_nsec;
+ unsigned int val;
+};
+
+struct snd_timer_tread64 {
+ int event;
+ u8 pad1[4];
+ s64 tstamp_sec;
+ s64 tstamp_nsec;
+ unsigned int val;
+ u8 pad2[4];
+};
+
struct snd_timer_user {
struct snd_timer_instance *timeri;
int tread; /* enhanced read with timestamps and events */
@@ -55,7 +77,7 @@ struct snd_timer_user {
int queue_size;
bool disconnected;
struct snd_timer_read *queue;
- struct snd_timer_tread *tqueue;
+ struct snd_timer_tread64 *tqueue;
spinlock_t qlock;
unsigned long last_resolution;
unsigned int filter;
@@ -1329,7 +1351,7 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
}
static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
- struct snd_timer_tread *tread)
+ struct snd_timer_tread64 *tread)
{
if (tu->qused >= tu->queue_size) {
tu->overrun++;
@@ -1346,7 +1368,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
unsigned long resolution)
{
struct snd_timer_user *tu = timeri->callback_data;
- struct snd_timer_tread r1;
+ struct snd_timer_tread64 r1;
unsigned long flags;
if (event >= SNDRV_TIMER_EVENT_START &&
@@ -1356,7 +1378,8 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
return;
memset(&r1, 0, sizeof(r1));
r1.event = event;
- r1.tstamp = timespec64_to_timespec(*tstamp);
+ r1.tstamp_sec = tstamp->tv_sec;
+ r1.tstamp_nsec = tstamp->tv_nsec;
r1.val = resolution;
spin_lock_irqsave(&tu->qlock, flags);
snd_timer_user_append_to_tqueue(tu, &r1);
@@ -1378,7 +1401,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
unsigned long ticks)
{
struct snd_timer_user *tu = timeri->callback_data;
- struct snd_timer_tread *r, r1;
+ struct snd_timer_tread64 *r, r1;
struct timespec64 tstamp;
int prev, append = 0;
@@ -1399,7 +1422,8 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
tu->last_resolution != resolution) {
r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
- r1.tstamp = timespec64_to_timespec(tstamp);
+ r1.tstamp_sec = tstamp.tv_sec;
+ r1.tstamp_nsec = tstamp.tv_nsec;
r1.val = resolution;
snd_timer_user_append_to_tqueue(tu, &r1);
tu->last_resolution = resolution;
@@ -1413,14 +1437,16 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
r = &tu->tqueue[prev];
if (r->event == SNDRV_TIMER_EVENT_TICK) {
- r->tstamp = timespec64_to_timespec(tstamp);
+ r->tstamp_sec = tstamp.tv_sec;
+ r->tstamp_nsec = tstamp.tv_nsec;
r->val += ticks;
append++;
goto __wake;
}
}
r1.event = SNDRV_TIMER_EVENT_TICK;
- r1.tstamp = timespec64_to_timespec(tstamp);
+ r1.tstamp_sec = tstamp.tv_sec;
+ r1.tstamp_nsec = tstamp.tv_nsec;
r1.val = ticks;
snd_timer_user_append_to_tqueue(tu, &r1);
append++;
@@ -1435,7 +1461,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
static int realloc_user_queue(struct snd_timer_user *tu, int size)
{
struct snd_timer_read *queue = NULL;
- struct snd_timer_tread *tqueue = NULL;
+ struct snd_timer_tread64 *tqueue = NULL;
if (tu->tread) {
tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL);
@@ -1874,11 +1900,11 @@ static int snd_timer_user_params(struct file *file,
tu->qhead = tu->qtail = tu->qused = 0;
if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
if (tu->tread) {
- struct snd_timer_tread tread;
+ struct snd_timer_tread64 tread;
memset(&tread, 0, sizeof(tread));
tread.event = SNDRV_TIMER_EVENT_EARLY;
- tread.tstamp.tv_sec = 0;
- tread.tstamp.tv_nsec = 0;
+ tread.tstamp_sec = 0;
+ tread.tstamp_nsec = 0;
tread.val = 0;
snd_timer_user_append_to_tqueue(tu, &tread);
} else {
@@ -2008,6 +2034,36 @@ static int snd_timer_user_pause(struct file *file)
return 0;
}
+static int snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu,
+ unsigned int cmd, bool compat)
+{
+ int __user *p = argp;
+ int xarg, old_tread;
+
+ if (tu->timeri) /* too late */
+ return -EBUSY;
+ if (get_user(xarg, p))
+ return -EFAULT;
+
+ old_tread = tu->tread;
+
+ if (!xarg)
+ tu->tread = TREAD_FORMAT_NONE;
+ else if (cmd == SNDRV_TIMER_IOCTL_TREAD64 ||
+ (IS_ENABLED(CONFIG_64BIT) && !compat))
+ tu->tread = TREAD_FORMAT_TIME64;
+ else
+ tu->tread = TREAD_FORMAT_TIME32;
+
+ if (tu->tread != old_tread &&
+ realloc_user_queue(tu, tu->queue_size) < 0) {
+ tu->tread = old_tread;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
enum {
SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20),
SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21),
@@ -2016,7 +2072,7 @@ enum {
};
static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+ unsigned long arg, bool compat)
{
struct snd_timer_user *tu;
void __user *argp = (void __user *)arg;
@@ -2028,23 +2084,9 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0;
case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
return snd_timer_user_next_device(argp);
- case SNDRV_TIMER_IOCTL_TREAD:
- {
- int xarg, old_tread;
-
- if (tu->timeri) /* too late */
- return -EBUSY;
- if (get_user(xarg, p))
- return -EFAULT;
- old_tread = tu->tread;
- tu->tread = xarg ? 1 : 0;
- if (tu->tread != old_tread &&
- realloc_user_queue(tu, tu->queue_size) < 0) {
- tu->tread = old_tread;
- return -ENOMEM;
- }
- return 0;
- }
+ case SNDRV_TIMER_IOCTL_TREAD_OLD:
+ case SNDRV_TIMER_IOCTL_TREAD64:
+ return snd_timer_user_tread(argp, tu, cmd, compat);
case SNDRV_TIMER_IOCTL_GINFO:
return snd_timer_user_ginfo(file, argp);
case SNDRV_TIMER_IOCTL_GPARAMS:
@@ -2084,7 +2126,7 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
long ret;
mutex_lock(&tu->ioctl_lock);
- ret = __snd_timer_user_ioctl(file, cmd, arg);
+ ret = __snd_timer_user_ioctl(file, cmd, arg, false);
mutex_unlock(&tu->ioctl_lock);
return ret;
}
@@ -2100,13 +2142,29 @@ static int snd_timer_user_fasync(int fd, struct file * file, int on)
static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
size_t count, loff_t *offset)
{
+ struct snd_timer_tread64 *tread;
+ struct snd_timer_tread32 tread32;
struct snd_timer_user *tu;
long result = 0, unit;
int qhead;
int err = 0;
tu = file->private_data;
- unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read);
+ switch (tu->tread) {
+ case TREAD_FORMAT_TIME64:
+ unit = sizeof(struct snd_timer_tread64);
+ break;
+ case TREAD_FORMAT_TIME32:
+ unit = sizeof(struct snd_timer_tread32);
+ break;
+ case TREAD_FORMAT_NONE:
+ unit = sizeof(struct snd_timer_read);
+ break;
+ default:
+ WARN_ONCE(1, "Corrupt snd_timer_user\n");
+ return -ENOTSUPP;
+ }
+
mutex_lock(&tu->ioctl_lock);
spin_lock_irq(&tu->qlock);
while ((long)count - result >= unit) {
@@ -2145,14 +2203,34 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
tu->qused--;
spin_unlock_irq(&tu->qlock);
- if (tu->tread) {
- if (copy_to_user(buffer, &tu->tqueue[qhead],
- sizeof(struct snd_timer_tread)))
+ tread = &tu->tqueue[qhead];
+
+ switch (tu->tread) {
+ case TREAD_FORMAT_TIME64:
+ if (copy_to_user(buffer, tread,
+ sizeof(struct snd_timer_tread64)))
err = -EFAULT;
- } else {
+ break;
+ case TREAD_FORMAT_TIME32:
+ memset(&tread32, 0, sizeof(tread32));
+ tread32 = (struct snd_timer_tread32) {
+ .event = tread->event,
+ .tstamp_sec = tread->tstamp_sec,
+ .tstamp_sec = tread->tstamp_nsec,
+ .val = tread->val,
+ };
+
+ if (copy_to_user(buffer, &tread32, sizeof(tread32)))
+ err = -EFAULT;
+ break;
+ case TREAD_FORMAT_NONE:
if (copy_to_user(buffer, &tu->queue[qhead],
sizeof(struct snd_timer_read)))
err = -EFAULT;
+ break;
+ default:
+ err = -ENOTSUPP;
+ break;
}
spin_lock_irq(&tu->qlock);
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index 20eef5bc304b..0103d16f6f9f 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -83,7 +83,8 @@ static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
switch (cmd) {
case SNDRV_TIMER_IOCTL_PVERSION:
- case SNDRV_TIMER_IOCTL_TREAD:
+ case SNDRV_TIMER_IOCTL_TREAD_OLD:
+ case SNDRV_TIMER_IOCTL_TREAD64:
case SNDRV_TIMER_IOCTL_GINFO:
case SNDRV_TIMER_IOCTL_GSTATUS:
case SNDRV_TIMER_IOCTL_SELECT:
@@ -97,7 +98,7 @@ static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
case SNDRV_TIMER_IOCTL_PAUSE:
case SNDRV_TIMER_IOCTL_PAUSE_OLD:
case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
- return __snd_timer_user_ioctl(file, cmd, (unsigned long)argp);
+ return __snd_timer_user_ioctl(file, cmd, (unsigned long)argp, true);
case SNDRV_TIMER_IOCTL_GPARAMS32:
return snd_timer_user_gparams_compat(file, argp);
case SNDRV_TIMER_IOCTL_INFO32:
--
2.20.0
1
0

Re: [alsa-devel] [PATCH v3 08/15] ASoC: tegra: Add audio mclk control through clk_out_1 and extern1
by Dmitry Osipenko 17 Dec '19
by Dmitry Osipenko 17 Dec '19
17 Dec '19
17.12.2019 19:39, Sowjanya Komatineni пишет:
>
> On 12/17/19 8:16 AM, Dmitry Osipenko wrote:
>> 17.12.2019 19:12, Sowjanya Komatineni пишет:
>>> On 12/17/19 7:36 AM, Dmitry Osipenko wrote:
>>>> 17.12.2019 04:29, Sowjanya Komatineni пишет:
>>>>> On 12/7/19 11:20 AM, Sowjanya Komatineni wrote:
>>>>>> On 12/7/19 6:58 AM, Dmitry Osipenko wrote:
>>>>>>> 06.12.2019 05:48, Sowjanya Komatineni пишет:
>>>>>>>> Current ASoC driver uses extern1 as cdev1 clock from Tegra30
>>>>>>>> onwards
>>>>>>>> through device tree.
>>>>>>>>
>>>>>>>> Actual audio mclk is clk_out_1 and to use PLLA for mclk rate
>>>>>>>> control,
>>>>>>>> need to clk_out_1_mux parent to extern1 and extern1 parent to
>>>>>>>> PLLA_OUT0.
>>>>>>>>
>>>>>>>> Currently Tegra clock driver init sets the parents and enables both
>>>>>>>> clk_out_1 and extern1 clocks. But these clocks parent and enables
>>>>>>>> should
>>>>>>>> be controlled by ASoC driver.
>>>>>>>>
>>>>>>>> Clock parents can be specified in device tree using assigned-clocks
>>>>>>>> and assigned-clock-parents.
>>>>>>>>
>>>>>>>> To enable audio mclk, both clk_out_1 and extern1 clocks need to be
>>>>>>>> enabled.
>>>>>>>>
>>>>>>>> This patch configures parents for clk_out_1 and extern1 clocks if
>>>>>>>> device
>>>>>>>> tree does not specify clock parents inorder to support old device
>>>>>>>> tree
>>>>>>>> and controls mclk using both clk_out_1 and extern1 clocks.
>>>>>>>>
>>>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni(a)nvidia.com>
>>>>>>>> ---
>>>>>>>> sound/soc/tegra/tegra_asoc_utils.c | 66
>>>>>>>> ++++++++++++++++++++++++++++++++++++++
>>>>>>>> sound/soc/tegra/tegra_asoc_utils.h | 1 +
>>>>>>>> 2 files changed, 67 insertions(+)
>>>>>>>>
>>>>>>>> diff --git a/sound/soc/tegra/tegra_asoc_utils.c
>>>>>>>> b/sound/soc/tegra/tegra_asoc_utils.c
>>>>>>>> index 536a578e9512..8e3a3740df7c 100644
>>>>>>>> --- a/sound/soc/tegra/tegra_asoc_utils.c
>>>>>>>> +++ b/sound/soc/tegra/tegra_asoc_utils.c
>>>>>>>> @@ -60,6 +60,7 @@ int tegra_asoc_utils_set_rate(struct
>>>>>>>> tegra_asoc_utils_data *data, int srate,
>>>>>>>> data->set_mclk = 0;
>>>>>>>> clk_disable_unprepare(data->clk_cdev1);
>>>>>>>> + clk_disable_unprepare(data->clk_extern1);
>>>>>>>> clk_disable_unprepare(data->clk_pll_a_out0);
>>>>>>>> clk_disable_unprepare(data->clk_pll_a);
>>>>>>>> @@ -89,6 +90,14 @@ int tegra_asoc_utils_set_rate(struct
>>>>>>>> tegra_asoc_utils_data *data, int srate,
>>>>>>>> return err;
>>>>>>>> }
>>>>>>>> + if (!IS_ERR_OR_NULL(data->clk_extern1)) {
>>>>>>>> + err = clk_prepare_enable(data->clk_extern1);
>>>>>>>> + if (err) {
>>>>>>>> + dev_err(data->dev, "Can't enable extern1: %d\n", err);
>>>>>>>> + return err;
>>>>>>>> + }
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> err = clk_prepare_enable(data->clk_cdev1);
>>>>>>>> if (err) {
>>>>>>>> dev_err(data->dev, "Can't enable cdev1: %d\n", err);
>>>>>>>> @@ -109,6 +118,7 @@ int tegra_asoc_utils_set_ac97_rate(struct
>>>>>>>> tegra_asoc_utils_data *data)
>>>>>>>> int err;
>>>>>>>> clk_disable_unprepare(data->clk_cdev1);
>>>>>>>> + clk_disable_unprepare(data->clk_extern1);
>>>>>>>> clk_disable_unprepare(data->clk_pll_a_out0);
>>>>>>>> clk_disable_unprepare(data->clk_pll_a);
>>>>>>>> @@ -142,6 +152,14 @@ int tegra_asoc_utils_set_ac97_rate(struct
>>>>>>>> tegra_asoc_utils_data *data)
>>>>>>>> return err;
>>>>>>>> }
>>>>>>>> + if (!IS_ERR_OR_NULL(data->clk_extern1)) {
>>>>>>>> + err = clk_prepare_enable(data->clk_extern1);
>>>>>>>> + if (err) {
>>>>>>>> + dev_err(data->dev, "Can't enable extern1: %d\n", err);
>>>>>>>> + return err;
>>>>>>>> + }
>>>>>>>> + }
>>>>>>> Why this is needed given that clk_extern1 is either a child of
>>>>>>> MCLK or
>>>>>>> MCLK itself (on T20)? The child clocks are enabled when the
>>>>>>> parent is
>>>>>>> enabled.
>>>>>> For T30 and later, clk_extern1 is one of the source for
>>>>>> clk_out_1_mux.
>>>>>> clk_extern1 is in CAR and it has its own gate and mux.
>>>>>>
>>>>>> As audio mclk related clocks (clk_out_1, clk_out_1_mux, and extern1)
>>>>>> are moved into ASoC driver from clock driver
>>>>>>
>>>>>> need to enable extern1 gate as well along with clk_out1 for T30
>>>>>> through T210.
>>>>>>
>>>>>> Just FYI, extern1 enable here happens only when data->clk_extern1 is
>>>>>> available which is for T30 onwards.
>>>>>>
>>>>>>>> err = clk_prepare_enable(data->clk_cdev1);
>>>>>>>> if (err) {
>>>>>>>> dev_err(data->dev, "Can't enable cdev1: %d\n", err);
>>>>>>>> @@ -158,6 +176,7 @@
>>>>>>>> EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
>>>>>>>> int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
>>>>>>>> struct device *dev)
>>>>>>>> {
>>>>>>>> + struct clk *clk_out_1_mux;
>>>>>>>> int ret;
>>>>>>>> data->dev = dev;
>>>>>>>> @@ -196,6 +215,51 @@ int tegra_asoc_utils_init(struct
>>>>>>>> tegra_asoc_utils_data *data,
>>>>>>>> goto err_put_pll_a_out0;
>>>>>>>> }
>>>>>>> In a previous patch you added fallback to EXTPERIPH when
>>>>>>> clk_get(MCLK)
>>>>>>> fails. This will work perfectly fine for the older kernels which
>>>>>>> have
>>>>>>> all clocks in the same single CaR driver, but this may not work that
>>>>>>> great for the newer kernels because PMC driver isn't registered
>>>>>>> early
>>>>>>> during boot and thus it is possible to get a legit -EPROBE_DEFER
>>>>>>> which
>>>>>>> shouldn't be ignored. In other words, you need to add into this
>>>>>>> patch a
>>>>>>> check for the error code returned by clk_get(MCLK) and fallback
>>>>>>> only for
>>>>>>> -EINVAL.
>>>>>> yeah right, will add check in next version.
>>>>>>>> + /*
>>>>>>>> + * If clock parents are not set in DT, configure here to use
>>>>>>>> clk_out_1
>>>>>>>> + * as mclk and extern1 as parent for Tegra30 and higher.
>>>>>>>> + */
>>>>>>>> + if (!of_find_property(dev->of_node, "assigned-clock-parents",
>>>>>>>> NULL) &&
>>>>>>>> + data->soc > TEGRA_ASOC_UTILS_SOC_TEGRA20) {
>>>>>>>> + data->clk_extern1 = clk_get_sys("clk_out_1", "extern1");
>>>>>>>> + if (IS_ERR(data->clk_extern1)) {
>>>>>>>> + dev_err(data->dev, "Can't retrieve clk extern1\n");
>>>>>>>> + ret = PTR_ERR(data->clk_extern1);
>>>>>>>> + goto err_put_cdev1;
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> + ret = clk_set_parent(data->clk_extern1,
>>>>>>>> data->clk_pll_a_out0);
>>>>>>>> + if (ret < 0) {
>>>>>>>> + dev_err(data->dev,
>>>>>>>> + "Set parent failed for clk extern1: %d\n",
>>>>>>>> + ret);
>>>>>>>> + goto err_put_cdev1;
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> + clk_out_1_mux = clk_get_sys(NULL, "clk_out_1_mux");
>>>>>>> Note1: clk_get(dev, "clk_out_1_mux") should work here by letting clk
>>>>>>> core to fall back to the clk_get_sys() by itself. Either way should
>>>>>>> be good.
>>>>> clk_get uses device rather and dev_id will be name of this device and
>>>>> when clk_get fall back to __clk_get_sys() it still will use dev id of
>>>>> this device rather than actual dev_id that pmc clocks are added to the
>>>>> lookup. So clk_get_sys() seems to be correct to use as we can specify
>>>>> exact dev_id and con_id.
>>>> It should be better to use something "resource managed", thus
>>>> devm_clk_get() should be a better choice.
>>>>
>>>>> Also, clk_find retrieves clock from lookup only when it finds matching
>>>>> clock with both dev_id and con_id as pmc clocks are registered with
>>>>> both
>>>>> dev_id and con_id.
>>>>>
>>>>> I see existing clock driver adds both extern and pmc clocks
>>>>> (clk_out) to
>>>>> lookup with same dev_id of clk_out_1/2/3 and con_id of extern1/2/3 and
>>>>> with this always extern clock will be retrieved and this is probably
>>>>> because old DT and audio driver always uses extern1 rather than actual
>>>>> clk_out_1
>>>>>
>>>>> But this need to be fixed now as we changed to use clk_out directly
>>>>> rather than extern (even for other pmc clocks) to match actual hw
>>>>> design.
>>>>>
>>>>> Will fix this as well to register pmc clocks using con_id as
>>>>> clk_out_1/2/3 in pmc driver and extern clocks using con_id of
>>>>> extern1/2/3 with dev_id being NULL so we can retrieve these clocks by
>>>>> just using con_id only using clk_get_sys as we switched to use
>>>>> clk_out_1
>>>>> directly as pmc clock rather than extern from DT and no longer need to
>>>>> pair pmc clocks to extern clocks.
>>>> I'm not sure it's worth the effort to care about con_ids if implicit
>>>> fallback to clk_get_sys(NULL, "...") does the right thing for the audio
>>>> driver.
>>>>
>>>> IIRC, CCF uses variant of matching clocks by names, although I'm not
>>>> sure whether that applies to older stable kernels.
>>>>
>>>> [snip]
>>> Current clock driver adds EXTERN clock to lookup with dev_id as
>>> clk_out_1/2/3 and con_id as extern_1/2/3
>>>
>>> With this we can retrieve clock from lookup only with clk_get_sys where
>>> we can pass dev_id as clk_out_1/2/3 and con_id as extern_1/2/3.
>>>
>>> We cant use devm_clk_get() or clk_get() for retrieving clocks from
>>> lookup by passing device object from sound driver as dev_id will be diff
>>> and clk_find will return NULL.
>> Have you actually tried to test that it fails? I think it's a false
>> assumption.
>
> Yes I tried and looking at devm_clk_get it falls back to __clk_get_sys
> which uses dev_id as dev_name of the device we pass from audio driver.
>
> looking into clk_find API implementation it doesn't find the clock from
> lookup unless both dev_id and con_id matches if a clock is added to
> lookup with both dev_id and clk_id
>
>
>>> But with the fix of having dev_id as NULL and use only con_id to add to
>>> lookup, we can use resource managed APIs devm_clk_get.
>>>
>>> So was saying will fix this in clock driver as part of removing
>>> clk_out_1/2/3 ids and pmc init from clock driver so we can use
>>> devm_clk_get API in audio driver.
>>>
Ok
1
0

Re: [alsa-devel] linux-next: Tree for Dec 17 (sound/soc/intel/boards/cml_rt1011_rt5682.o)
by Randy Dunlap 17 Dec '19
by Randy Dunlap 17 Dec '19
17 Dec '19
On 12/16/19 6:08 PM, Stephen Rothwell wrote:
> Hi all,
>
> Changes since 20191216:
>
on x86_64:
CC sound/soc/intel/boards/cml_rt1011_rt5682.o
../sound/soc/intel/boards/cml_rt1011_rt5682.c:409:4: error: ‘struct snd_soc_codec_conf’ has no member named ‘dev_name’
.dev_name = "i2c-10EC1011:00",
^~~~~~~~
../sound/soc/intel/boards/cml_rt1011_rt5682.c:407:50: warning: missing braces around initializer [-Wmissing-braces]
static struct snd_soc_codec_conf rt1011_conf[] = {
^
../sound/soc/intel/boards/cml_rt1011_rt5682.c:409:15:
.dev_name = "i2c-10EC1011:00",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:413:4: error: ‘struct snd_soc_codec_conf’ has no member named ‘dev_name’
.dev_name = "i2c-10EC1011:01",
^~~~~~~~
../sound/soc/intel/boards/cml_rt1011_rt5682.c:407:50: warning: missing braces around initializer [-Wmissing-braces]
static struct snd_soc_codec_conf rt1011_conf[] = {
^
../sound/soc/intel/boards/cml_rt1011_rt5682.c:409:15:
.dev_name = "i2c-10EC1011:00",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:413:15:
.dev_name = "i2c-10EC1011:01",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:417:4: error: ‘struct snd_soc_codec_conf’ has no member named ‘dev_name’
.dev_name = "i2c-10EC1011:02",
^~~~~~~~
../sound/soc/intel/boards/cml_rt1011_rt5682.c:407:50: warning: missing braces around initializer [-Wmissing-braces]
static struct snd_soc_codec_conf rt1011_conf[] = {
^
../sound/soc/intel/boards/cml_rt1011_rt5682.c:409:15:
.dev_name = "i2c-10EC1011:00",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:413:15:
.dev_name = "i2c-10EC1011:01",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:417:15:
.dev_name = "i2c-10EC1011:02",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:421:4: error: ‘struct snd_soc_codec_conf’ has no member named ‘dev_name’
.dev_name = "i2c-10EC1011:03",
^~~~~~~~
../sound/soc/intel/boards/cml_rt1011_rt5682.c:407:50: warning: missing braces around initializer [-Wmissing-braces]
static struct snd_soc_codec_conf rt1011_conf[] = {
^
../sound/soc/intel/boards/cml_rt1011_rt5682.c:409:15:
.dev_name = "i2c-10EC1011:00",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:413:15:
.dev_name = "i2c-10EC1011:01",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:417:15:
.dev_name = "i2c-10EC1011:02",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:421:15:
.dev_name = "i2c-10EC1011:03",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:407:50: warning: missing braces around initializer [-Wmissing-braces]
static struct snd_soc_codec_conf rt1011_conf[] = {
^
../sound/soc/intel/boards/cml_rt1011_rt5682.c:409:15:
.dev_name = "i2c-10EC1011:00",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:413:15:
.dev_name = "i2c-10EC1011:01",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:417:15:
.dev_name = "i2c-10EC1011:02",
{ }
../sound/soc/intel/boards/cml_rt1011_rt5682.c:421:15:
.dev_name = "i2c-10EC1011:03",
{ }
make[5]: *** [../scripts/Makefile.build:266: sound/soc/intel/boards/cml_rt1011_rt5682.o] Error 1
--
~Randy
Reported-by: Randy Dunlap <rdunlap(a)infradead.org>
2
1

[alsa-devel] [PATCH] ALSA: cmipci: Allow disabling MPU port via module option
by Takashi Iwai 17 Dec '19
by Takashi Iwai 17 Dec '19
17 Dec '19
Patrick May reported that his sound card with CMI8378 chip causes a
crash / reboot when accessing the MIDI port that isn't actually
present on the board. Moreover, despite of the documentation,
passing mpu_port=0 doesn't disable the MIDI port on this board.
It implies that the chip is a newer revision and the MPU401 port is
integrated and mapped on the PCI register. For this chip model, the
driver enables the MPU port unconditionally, so far.
Although fixing the unexpected reboot would be the best solution, it's
not so trivial to identify the cause. So, as a plan B, this patch
extends the existing mpu_port option usage to allow disabling the port
by specifying the value 0, just like we applied for fm_port option in
commit 2f24d159d5ac ("[ALSA] cmipci - Allow to disable integrated FM
port"). As default, the MPU port is still enabled, but user can pass
mpu_port=0 to disable it.
Reported-and-tested-by: Patrick May <dusthillresident(a)gmail.com>
Signed-off-by: Takashi Iwai <tiwai(a)suse.de>
---
Documentation/sound/alsa-configuration.rst | 3 ++-
sound/pci/cmipci.c | 5 +++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst
index 02aacd69ab96..392875a1b94e 100644
--- a/Documentation/sound/alsa-configuration.rst
+++ b/Documentation/sound/alsa-configuration.rst
@@ -495,7 +495,8 @@ Module for C-Media CMI8338/8738/8768/8770 PCI sound cards.
mpu_port
port address of MIDI interface (8338 only):
0x300,0x310,0x320,0x330 = legacy port,
- 0 = disable (default)
+ 1 = integrated PCI port (default on 8738),
+ 0 = disable
fm_port
port address of OPL-3 FM synthesizer (8x38 only):
0x388 = legacy port,
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 4bfab21c53f4..266c4cf28b78 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -42,7 +42,7 @@ MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8738},"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
-static long mpu_port[SNDRV_CARDS];
+static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
static long fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
static bool soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
#ifdef SUPPORT_JOYSTICK
@@ -3132,7 +3132,8 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
if (cm->chip_version >= 39) {
val = snd_cmipci_read_b(cm, CM_REG_MPU_PCI + 1);
if (val != 0x00 && val != 0xff) {
- iomidi = cm->iobase + CM_REG_MPU_PCI;
+ if (mpu_port[dev])
+ iomidi = cm->iobase + CM_REG_MPU_PCI;
integrated_midi = 1;
}
}
--
2.16.4
2
1

Re: [alsa-devel] [PATCH v3 08/15] ASoC: tegra: Add audio mclk control through clk_out_1 and extern1
by Dmitry Osipenko 17 Dec '19
by Dmitry Osipenko 17 Dec '19
17 Dec '19
17.12.2019 19:12, Sowjanya Komatineni пишет:
>
> On 12/17/19 7:36 AM, Dmitry Osipenko wrote:
>> 17.12.2019 04:29, Sowjanya Komatineni пишет:
>>> On 12/7/19 11:20 AM, Sowjanya Komatineni wrote:
>>>> On 12/7/19 6:58 AM, Dmitry Osipenko wrote:
>>>>> 06.12.2019 05:48, Sowjanya Komatineni пишет:
>>>>>> Current ASoC driver uses extern1 as cdev1 clock from Tegra30 onwards
>>>>>> through device tree.
>>>>>>
>>>>>> Actual audio mclk is clk_out_1 and to use PLLA for mclk rate control,
>>>>>> need to clk_out_1_mux parent to extern1 and extern1 parent to
>>>>>> PLLA_OUT0.
>>>>>>
>>>>>> Currently Tegra clock driver init sets the parents and enables both
>>>>>> clk_out_1 and extern1 clocks. But these clocks parent and enables
>>>>>> should
>>>>>> be controlled by ASoC driver.
>>>>>>
>>>>>> Clock parents can be specified in device tree using assigned-clocks
>>>>>> and assigned-clock-parents.
>>>>>>
>>>>>> To enable audio mclk, both clk_out_1 and extern1 clocks need to be
>>>>>> enabled.
>>>>>>
>>>>>> This patch configures parents for clk_out_1 and extern1 clocks if
>>>>>> device
>>>>>> tree does not specify clock parents inorder to support old device
>>>>>> tree
>>>>>> and controls mclk using both clk_out_1 and extern1 clocks.
>>>>>>
>>>>>> Signed-off-by: Sowjanya Komatineni <skomatineni(a)nvidia.com>
>>>>>> ---
>>>>>> sound/soc/tegra/tegra_asoc_utils.c | 66
>>>>>> ++++++++++++++++++++++++++++++++++++++
>>>>>> sound/soc/tegra/tegra_asoc_utils.h | 1 +
>>>>>> 2 files changed, 67 insertions(+)
>>>>>>
>>>>>> diff --git a/sound/soc/tegra/tegra_asoc_utils.c
>>>>>> b/sound/soc/tegra/tegra_asoc_utils.c
>>>>>> index 536a578e9512..8e3a3740df7c 100644
>>>>>> --- a/sound/soc/tegra/tegra_asoc_utils.c
>>>>>> +++ b/sound/soc/tegra/tegra_asoc_utils.c
>>>>>> @@ -60,6 +60,7 @@ int tegra_asoc_utils_set_rate(struct
>>>>>> tegra_asoc_utils_data *data, int srate,
>>>>>> data->set_mclk = 0;
>>>>>> clk_disable_unprepare(data->clk_cdev1);
>>>>>> + clk_disable_unprepare(data->clk_extern1);
>>>>>> clk_disable_unprepare(data->clk_pll_a_out0);
>>>>>> clk_disable_unprepare(data->clk_pll_a);
>>>>>> @@ -89,6 +90,14 @@ int tegra_asoc_utils_set_rate(struct
>>>>>> tegra_asoc_utils_data *data, int srate,
>>>>>> return err;
>>>>>> }
>>>>>> + if (!IS_ERR_OR_NULL(data->clk_extern1)) {
>>>>>> + err = clk_prepare_enable(data->clk_extern1);
>>>>>> + if (err) {
>>>>>> + dev_err(data->dev, "Can't enable extern1: %d\n", err);
>>>>>> + return err;
>>>>>> + }
>>>>>> + }
>>>>>> +
>>>>>> err = clk_prepare_enable(data->clk_cdev1);
>>>>>> if (err) {
>>>>>> dev_err(data->dev, "Can't enable cdev1: %d\n", err);
>>>>>> @@ -109,6 +118,7 @@ int tegra_asoc_utils_set_ac97_rate(struct
>>>>>> tegra_asoc_utils_data *data)
>>>>>> int err;
>>>>>> clk_disable_unprepare(data->clk_cdev1);
>>>>>> + clk_disable_unprepare(data->clk_extern1);
>>>>>> clk_disable_unprepare(data->clk_pll_a_out0);
>>>>>> clk_disable_unprepare(data->clk_pll_a);
>>>>>> @@ -142,6 +152,14 @@ int tegra_asoc_utils_set_ac97_rate(struct
>>>>>> tegra_asoc_utils_data *data)
>>>>>> return err;
>>>>>> }
>>>>>> + if (!IS_ERR_OR_NULL(data->clk_extern1)) {
>>>>>> + err = clk_prepare_enable(data->clk_extern1);
>>>>>> + if (err) {
>>>>>> + dev_err(data->dev, "Can't enable extern1: %d\n", err);
>>>>>> + return err;
>>>>>> + }
>>>>>> + }
>>>>> Why this is needed given that clk_extern1 is either a child of MCLK or
>>>>> MCLK itself (on T20)? The child clocks are enabled when the parent is
>>>>> enabled.
>>>> For T30 and later, clk_extern1 is one of the source for clk_out_1_mux.
>>>> clk_extern1 is in CAR and it has its own gate and mux.
>>>>
>>>> As audio mclk related clocks (clk_out_1, clk_out_1_mux, and extern1)
>>>> are moved into ASoC driver from clock driver
>>>>
>>>> need to enable extern1 gate as well along with clk_out1 for T30
>>>> through T210.
>>>>
>>>> Just FYI, extern1 enable here happens only when data->clk_extern1 is
>>>> available which is for T30 onwards.
>>>>
>>>>>> err = clk_prepare_enable(data->clk_cdev1);
>>>>>> if (err) {
>>>>>> dev_err(data->dev, "Can't enable cdev1: %d\n", err);
>>>>>> @@ -158,6 +176,7 @@
>>>>>> EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
>>>>>> int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
>>>>>> struct device *dev)
>>>>>> {
>>>>>> + struct clk *clk_out_1_mux;
>>>>>> int ret;
>>>>>> data->dev = dev;
>>>>>> @@ -196,6 +215,51 @@ int tegra_asoc_utils_init(struct
>>>>>> tegra_asoc_utils_data *data,
>>>>>> goto err_put_pll_a_out0;
>>>>>> }
>>>>> In a previous patch you added fallback to EXTPERIPH when clk_get(MCLK)
>>>>> fails. This will work perfectly fine for the older kernels which have
>>>>> all clocks in the same single CaR driver, but this may not work that
>>>>> great for the newer kernels because PMC driver isn't registered early
>>>>> during boot and thus it is possible to get a legit -EPROBE_DEFER which
>>>>> shouldn't be ignored. In other words, you need to add into this
>>>>> patch a
>>>>> check for the error code returned by clk_get(MCLK) and fallback
>>>>> only for
>>>>> -EINVAL.
>>>> yeah right, will add check in next version.
>>>>>> + /*
>>>>>> + * If clock parents are not set in DT, configure here to use
>>>>>> clk_out_1
>>>>>> + * as mclk and extern1 as parent for Tegra30 and higher.
>>>>>> + */
>>>>>> + if (!of_find_property(dev->of_node, "assigned-clock-parents",
>>>>>> NULL) &&
>>>>>> + data->soc > TEGRA_ASOC_UTILS_SOC_TEGRA20) {
>>>>>> + data->clk_extern1 = clk_get_sys("clk_out_1", "extern1");
>>>>>> + if (IS_ERR(data->clk_extern1)) {
>>>>>> + dev_err(data->dev, "Can't retrieve clk extern1\n");
>>>>>> + ret = PTR_ERR(data->clk_extern1);
>>>>>> + goto err_put_cdev1;
>>>>>> + }
>>>>>> +
>>>>>> + ret = clk_set_parent(data->clk_extern1,
>>>>>> data->clk_pll_a_out0);
>>>>>> + if (ret < 0) {
>>>>>> + dev_err(data->dev,
>>>>>> + "Set parent failed for clk extern1: %d\n",
>>>>>> + ret);
>>>>>> + goto err_put_cdev1;
>>>>>> + }
>>>>>> +
>>>>>> + clk_out_1_mux = clk_get_sys(NULL, "clk_out_1_mux");
>>>>> Note1: clk_get(dev, "clk_out_1_mux") should work here by letting clk
>>>>> core to fall back to the clk_get_sys() by itself. Either way should
>>>>> be good.
>>> clk_get uses device rather and dev_id will be name of this device and
>>> when clk_get fall back to __clk_get_sys() it still will use dev id of
>>> this device rather than actual dev_id that pmc clocks are added to the
>>> lookup. So clk_get_sys() seems to be correct to use as we can specify
>>> exact dev_id and con_id.
>> It should be better to use something "resource managed", thus
>> devm_clk_get() should be a better choice.
>>
>>> Also, clk_find retrieves clock from lookup only when it finds matching
>>> clock with both dev_id and con_id as pmc clocks are registered with both
>>> dev_id and con_id.
>>>
>>> I see existing clock driver adds both extern and pmc clocks (clk_out) to
>>> lookup with same dev_id of clk_out_1/2/3 and con_id of extern1/2/3 and
>>> with this always extern clock will be retrieved and this is probably
>>> because old DT and audio driver always uses extern1 rather than actual
>>> clk_out_1
>>>
>>> But this need to be fixed now as we changed to use clk_out directly
>>> rather than extern (even for other pmc clocks) to match actual hw
>>> design.
>>>
>>> Will fix this as well to register pmc clocks using con_id as
>>> clk_out_1/2/3 in pmc driver and extern clocks using con_id of
>>> extern1/2/3 with dev_id being NULL so we can retrieve these clocks by
>>> just using con_id only using clk_get_sys as we switched to use clk_out_1
>>> directly as pmc clock rather than extern from DT and no longer need to
>>> pair pmc clocks to extern clocks.
>> I'm not sure it's worth the effort to care about con_ids if implicit
>> fallback to clk_get_sys(NULL, "...") does the right thing for the audio
>> driver.
>>
>> IIRC, CCF uses variant of matching clocks by names, although I'm not
>> sure whether that applies to older stable kernels.
>>
>> [snip]
>
> Current clock driver adds EXTERN clock to lookup with dev_id as
> clk_out_1/2/3 and con_id as extern_1/2/3
>
> With this we can retrieve clock from lookup only with clk_get_sys where
> we can pass dev_id as clk_out_1/2/3 and con_id as extern_1/2/3.
>
> We cant use devm_clk_get() or clk_get() for retrieving clocks from
> lookup by passing device object from sound driver as dev_id will be diff
> and clk_find will return NULL.
Have you actually tried to test that it fails? I think it's a false
assumption.
> But with the fix of having dev_id as NULL and use only con_id to add to
> lookup, we can use resource managed APIs devm_clk_get.
>
> So was saying will fix this in clock driver as part of removing
> clk_out_1/2/3 ids and pmc init from clock driver so we can use
> devm_clk_get API in audio driver.
>
>>
1
0

Re: [alsa-devel] [PATCH v3 08/15] ASoC: tegra: Add audio mclk control through clk_out_1 and extern1
by Dmitry Osipenko 17 Dec '19
by Dmitry Osipenko 17 Dec '19
17 Dec '19
17.12.2019 04:29, Sowjanya Komatineni пишет:
>
> On 12/7/19 11:20 AM, Sowjanya Komatineni wrote:
>>
>> On 12/7/19 6:58 AM, Dmitry Osipenko wrote:
>>> 06.12.2019 05:48, Sowjanya Komatineni пишет:
>>>> Current ASoC driver uses extern1 as cdev1 clock from Tegra30 onwards
>>>> through device tree.
>>>>
>>>> Actual audio mclk is clk_out_1 and to use PLLA for mclk rate control,
>>>> need to clk_out_1_mux parent to extern1 and extern1 parent to
>>>> PLLA_OUT0.
>>>>
>>>> Currently Tegra clock driver init sets the parents and enables both
>>>> clk_out_1 and extern1 clocks. But these clocks parent and enables
>>>> should
>>>> be controlled by ASoC driver.
>>>>
>>>> Clock parents can be specified in device tree using assigned-clocks
>>>> and assigned-clock-parents.
>>>>
>>>> To enable audio mclk, both clk_out_1 and extern1 clocks need to be
>>>> enabled.
>>>>
>>>> This patch configures parents for clk_out_1 and extern1 clocks if
>>>> device
>>>> tree does not specify clock parents inorder to support old device tree
>>>> and controls mclk using both clk_out_1 and extern1 clocks.
>>>>
>>>> Signed-off-by: Sowjanya Komatineni <skomatineni(a)nvidia.com>
>>>> ---
>>>> sound/soc/tegra/tegra_asoc_utils.c | 66
>>>> ++++++++++++++++++++++++++++++++++++++
>>>> sound/soc/tegra/tegra_asoc_utils.h | 1 +
>>>> 2 files changed, 67 insertions(+)
>>>>
>>>> diff --git a/sound/soc/tegra/tegra_asoc_utils.c
>>>> b/sound/soc/tegra/tegra_asoc_utils.c
>>>> index 536a578e9512..8e3a3740df7c 100644
>>>> --- a/sound/soc/tegra/tegra_asoc_utils.c
>>>> +++ b/sound/soc/tegra/tegra_asoc_utils.c
>>>> @@ -60,6 +60,7 @@ int tegra_asoc_utils_set_rate(struct
>>>> tegra_asoc_utils_data *data, int srate,
>>>> data->set_mclk = 0;
>>>> clk_disable_unprepare(data->clk_cdev1);
>>>> + clk_disable_unprepare(data->clk_extern1);
>>>> clk_disable_unprepare(data->clk_pll_a_out0);
>>>> clk_disable_unprepare(data->clk_pll_a);
>>>> @@ -89,6 +90,14 @@ int tegra_asoc_utils_set_rate(struct
>>>> tegra_asoc_utils_data *data, int srate,
>>>> return err;
>>>> }
>>>> + if (!IS_ERR_OR_NULL(data->clk_extern1)) {
>>>> + err = clk_prepare_enable(data->clk_extern1);
>>>> + if (err) {
>>>> + dev_err(data->dev, "Can't enable extern1: %d\n", err);
>>>> + return err;
>>>> + }
>>>> + }
>>>> +
>>>> err = clk_prepare_enable(data->clk_cdev1);
>>>> if (err) {
>>>> dev_err(data->dev, "Can't enable cdev1: %d\n", err);
>>>> @@ -109,6 +118,7 @@ int tegra_asoc_utils_set_ac97_rate(struct
>>>> tegra_asoc_utils_data *data)
>>>> int err;
>>>> clk_disable_unprepare(data->clk_cdev1);
>>>> + clk_disable_unprepare(data->clk_extern1);
>>>> clk_disable_unprepare(data->clk_pll_a_out0);
>>>> clk_disable_unprepare(data->clk_pll_a);
>>>> @@ -142,6 +152,14 @@ int tegra_asoc_utils_set_ac97_rate(struct
>>>> tegra_asoc_utils_data *data)
>>>> return err;
>>>> }
>>>> + if (!IS_ERR_OR_NULL(data->clk_extern1)) {
>>>> + err = clk_prepare_enable(data->clk_extern1);
>>>> + if (err) {
>>>> + dev_err(data->dev, "Can't enable extern1: %d\n", err);
>>>> + return err;
>>>> + }
>>>> + }
>>> Why this is needed given that clk_extern1 is either a child of MCLK or
>>> MCLK itself (on T20)? The child clocks are enabled when the parent is
>>> enabled.
>>
>> For T30 and later, clk_extern1 is one of the source for clk_out_1_mux.
>> clk_extern1 is in CAR and it has its own gate and mux.
>>
>> As audio mclk related clocks (clk_out_1, clk_out_1_mux, and extern1)
>> are moved into ASoC driver from clock driver
>>
>> need to enable extern1 gate as well along with clk_out1 for T30
>> through T210.
>>
>> Just FYI, extern1 enable here happens only when data->clk_extern1 is
>> available which is for T30 onwards.
>>
>>>> err = clk_prepare_enable(data->clk_cdev1);
>>>> if (err) {
>>>> dev_err(data->dev, "Can't enable cdev1: %d\n", err);
>>>> @@ -158,6 +176,7 @@ EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
>>>> int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
>>>> struct device *dev)
>>>> {
>>>> + struct clk *clk_out_1_mux;
>>>> int ret;
>>>> data->dev = dev;
>>>> @@ -196,6 +215,51 @@ int tegra_asoc_utils_init(struct
>>>> tegra_asoc_utils_data *data,
>>>> goto err_put_pll_a_out0;
>>>> }
>>> In a previous patch you added fallback to EXTPERIPH when clk_get(MCLK)
>>> fails. This will work perfectly fine for the older kernels which have
>>> all clocks in the same single CaR driver, but this may not work that
>>> great for the newer kernels because PMC driver isn't registered early
>>> during boot and thus it is possible to get a legit -EPROBE_DEFER which
>>> shouldn't be ignored. In other words, you need to add into this patch a
>>> check for the error code returned by clk_get(MCLK) and fallback only for
>>> -EINVAL.
>> yeah right, will add check in next version.
>>>> + /*
>>>> + * If clock parents are not set in DT, configure here to use
>>>> clk_out_1
>>>> + * as mclk and extern1 as parent for Tegra30 and higher.
>>>> + */
>>>> + if (!of_find_property(dev->of_node, "assigned-clock-parents",
>>>> NULL) &&
>>>> + data->soc > TEGRA_ASOC_UTILS_SOC_TEGRA20) {
>>>> + data->clk_extern1 = clk_get_sys("clk_out_1", "extern1");
>>>> + if (IS_ERR(data->clk_extern1)) {
>>>> + dev_err(data->dev, "Can't retrieve clk extern1\n");
>>>> + ret = PTR_ERR(data->clk_extern1);
>>>> + goto err_put_cdev1;
>>>> + }
>>>> +
>>>> + ret = clk_set_parent(data->clk_extern1, data->clk_pll_a_out0);
>>>> + if (ret < 0) {
>>>> + dev_err(data->dev,
>>>> + "Set parent failed for clk extern1: %d\n",
>>>> + ret);
>>>> + goto err_put_cdev1;
>>>> + }
>>>> +
>>>> + clk_out_1_mux = clk_get_sys(NULL, "clk_out_1_mux");
>>> Note1: clk_get(dev, "clk_out_1_mux") should work here by letting clk
>>> core to fall back to the clk_get_sys() by itself. Either way should
>>> be good.
>
> clk_get uses device rather and dev_id will be name of this device and
> when clk_get fall back to __clk_get_sys() it still will use dev id of
> this device rather than actual dev_id that pmc clocks are added to the
> lookup. So clk_get_sys() seems to be correct to use as we can specify
> exact dev_id and con_id.
It should be better to use something "resource managed", thus
devm_clk_get() should be a better choice.
> Also, clk_find retrieves clock from lookup only when it finds matching
> clock with both dev_id and con_id as pmc clocks are registered with both
> dev_id and con_id.
>
> I see existing clock driver adds both extern and pmc clocks (clk_out) to
> lookup with same dev_id of clk_out_1/2/3 and con_id of extern1/2/3 and
> with this always extern clock will be retrieved and this is probably
> because old DT and audio driver always uses extern1 rather than actual
> clk_out_1
>
> But this need to be fixed now as we changed to use clk_out directly
> rather than extern (even for other pmc clocks) to match actual hw design.
>
> Will fix this as well to register pmc clocks using con_id as
> clk_out_1/2/3 in pmc driver and extern clocks using con_id of
> extern1/2/3 with dev_id being NULL so we can retrieve these clocks by
> just using con_id only using clk_get_sys as we switched to use clk_out_1
> directly as pmc clock rather than extern from DT and no longer need to
> pair pmc clocks to extern clocks.
I'm not sure it's worth the effort to care about con_ids if implicit
fallback to clk_get_sys(NULL, "...") does the right thing for the audio
driver.
IIRC, CCF uses variant of matching clocks by names, although I'm not
sure whether that applies to older stable kernels.
[snip]
1
0

17 Dec '19
The MT6660 is a boosted BTL class-D amplifier with V/I sensing.
A built-in DC-DC step-up converter is used to provide efficient
power for class-D amplifier with multi-level class-G operation.
The digital audio interface supports I2S, left-justified,
right-justified, TDM and DSP A/B format for audio in with a data
out used for chip information like voltage sense and current
sense, which are able to be monitored via DATAO through proper
---
sound/soc/codecs/Kconfig | 14 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/mt6660.c | 1063 +++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/mt6660.h | 95 ++++
4 files changed, 1174 insertions(+)
create mode 100644 sound/soc/codecs/mt6660.c
create mode 100644 sound/soc/codecs/mt6660.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 229cc89..806cd03 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -122,6 +122,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_ML26124 if I2C
select SND_SOC_MT6351 if MTK_PMIC_WRAP
select SND_SOC_MT6358 if MTK_PMIC_WRAP
+ select SND_SOC_MT6660 if I2C
select SND_SOC_NAU8540 if I2C
select SND_SOC_NAU8810 if I2C
select SND_SOC_NAU8822 if I2C
@@ -1465,6 +1466,19 @@ config SND_SOC_MT6358
Enable support for the platform which uses MT6358 as
external codec device.
+config SND_SOC_MT6660
+ tristate "Mediatek MT6660 Speaker Amplifier"
+ depends on I2C
+ select CRC32
+ select CRYPTO_SHA256
+ select CRYTO_RSA
+ help
+ MediaTek MT6660 is a smart power amplifier which contain
+ speaker protection, multi-band DRC, equalizer functions.
+ Select N if you don't have MT6660 on board.
+ Select M to build this as module.
+
+
config SND_SOC_NAU8540
tristate "Nuvoton Technology Corporation NAU85L40 CODEC"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index c498373..2b6814c 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -119,6 +119,7 @@ snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o
snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o
snd-soc-mt6351-objs := mt6351.o
snd-soc-mt6358-objs := mt6358.o
+snd-soc-mt6660-objs := mt6660.o
snd-soc-nau8540-objs := nau8540.o
snd-soc-nau8810-objs := nau8810.o
snd-soc-nau8822-objs := nau8822.o
@@ -403,6 +404,7 @@ obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o
obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o
obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o
obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o
+obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o
obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o
obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o
obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o
diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c
new file mode 100644
index 0000000..5ba8ec5
--- /dev/null
+++ b/sound/soc/codecs/mt6660.c
@@ -0,0 +1,1063 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+#include <linux/debugfs.h>
+
+#include "mt6660.h"
+
+struct codec_reg_val {
+ u32 addr;
+ u32 mask;
+ u32 data;
+};
+
+struct reg_size_table {
+ u32 addr;
+ u8 size;
+};
+
+static const struct reg_size_table mt6660_reg_size_table[] = {
+ { MT6660_REG_HPF1_COEF, 4 },
+ { MT6660_REG_HPF2_COEF, 4 },
+ { MT6660_REG_TDM_CFG3, 2 },
+ { MT6660_REG_RESV17, 2 },
+ { MT6660_REG_RESV23, 2 },
+ { MT6660_REG_SIGMAX, 2 },
+ { MT6660_REG_DEVID, 2},
+ { MT6660_REG_TDM_CFG3, 2},
+ { MT6660_REG_HCLIP_CTRL, 2},
+ { MT6660_REG_DA_GAIN, 2},
+};
+
+static int mt6660_get_reg_size(uint32_t addr)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(mt6660_reg_size_table); i++) {
+ if (mt6660_reg_size_table[i].addr == addr)
+ return mt6660_reg_size_table[i].size;
+ }
+ return 1;
+}
+
+#if GENERIC_DEBUGFS
+static int mt6660_dbg_io_read(void *drvdata, u16 reg, void *val, u16 size)
+{
+ struct mt6660_chip *chip = (struct mt6660_chip *)drvdata;
+ int ret = 0, i = 0;
+ unsigned int data;
+ u8 *_val = val;
+
+ if (size != 1 && size != 2 && size != 4) {
+ dev_err(chip->dev, "%s size not match\n", __func__);
+ return -EINVAL;
+ }
+ ret = regmap_read(chip->regmap, reg, &data);
+ if (reg < 0)
+ return ret;
+
+ for (i = 0; i < size; i++)
+ _val[size-i-1] = (data >> (8*i))&0xff;
+
+ return 0;
+}
+
+static int mt6660_dbg_io_write(void *drvdata, u16 reg,
+ const void *val, u16 size)
+{
+ struct mt6660_chip *chip = (struct mt6660_chip *)drvdata;
+ int reg_size = mt6660_get_reg_size(reg);
+ int i = 0;
+ unsigned int regval = 0;
+ u8 *_val = (u8 *)val;
+
+ if (size != reg_size) {
+ dev_err(chip->dev,
+ "%s size not match reg_size(%d), size(%d)\n",
+ __func__, reg_size, size);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i++) {
+ regval <<= 8;
+ regval |= _val[i];
+ }
+
+ return regmap_write(chip->regmap, reg, regval);
+}
+#endif /* GENERIC_DEBUGFS */
+
+static int mt6660_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct mt6660_chip *chip = context;
+ int size = mt6660_get_reg_size(reg);
+ u8 reg_data[4] = {0};
+ int i = 0;
+
+ for (i = 0; i < size; i++)
+ reg_data[size - i - 1] = (val >> (8 * i)) & 0xff;
+
+ return i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data);
+}
+
+static int mt6660_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct mt6660_chip *chip = context;
+ int size = mt6660_get_reg_size(reg);
+ int i = 0, ret = 0;
+ u8 data[4] = {0};
+ u32 reg_data = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(chip->i2c, reg, size, data);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < size; i++) {
+ reg_data <<= 8;
+ reg_data |= data[i];
+ }
+ *val = reg_data;
+
+ return ret;
+}
+
+static bool mt6660_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return true;
+}
+
+static struct regmap_config mt6660_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_write = mt6660_reg_write,
+ .reg_read = mt6660_reg_read,
+ .volatile_reg = mt6660_volatile_reg,
+};
+
+static unsigned int mt6660_component_io_read(
+ struct snd_soc_component *component, unsigned int reg)
+{
+ struct mt6660_chip *chip = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(chip->regmap, reg, &val);
+ if (ret < 0) /* ret success -> >= 0, fail -> < - */
+ return ret;
+ pr_err("%s val = 0x%x\n", __func__, val);
+ return val;
+}
+
+static int mt6660_component_io_write(struct snd_soc_component *component,
+ unsigned int reg, unsigned int data)
+{
+ struct mt6660_chip *chip = snd_soc_component_get_drvdata(component);
+
+ pr_err("%s data = 0x%x\n", __func__, data);
+ return regmap_write(chip->regmap, reg, data);
+}
+
+static const int mt6660_dump_table[] = {
+ MT6660_REG_DEVID,
+ MT6660_REG_SYSTEM_CTRL,
+ MT6660_REG_IRQ_STATUS1,
+ MT6660_REG_ADDA_CLOCK,
+ MT6660_REG_SERIAL_CFG1,
+ MT6660_REG_DATAO_SEL,
+ MT6660_REG_TDM_CFG3,
+ MT6660_REG_HPF_CTRL,
+ MT6660_REG_HPF1_COEF,
+ MT6660_REG_HPF2_COEF,
+ MT6660_REG_PATH_BYPASS,
+ MT6660_REG_WDT_CTRL,
+ MT6660_REG_HCLIP_CTRL,
+ MT6660_REG_VOL_CTRL,
+ MT6660_REG_SPS_CTRL,
+ MT6660_REG_SIGMAX,
+ MT6660_REG_CALI_T0,
+ MT6660_REG_BST_CTRL,
+ MT6660_REG_PROTECTION_CFG,
+ MT6660_REG_DA_GAIN,
+ MT6660_REG_AUDIO_IN2_SEL,
+ MT6660_REG_SIG_GAIN,
+ MT6660_REG_PLL_CFG1,
+ MT6660_REG_DRE_CTRL,
+ MT6660_REG_DRE_THDMODE,
+ MT6660_REG_DRE_CORASE,
+ MT6660_REG_PWM_CTRL,
+ MT6660_REG_DC_PROTECT_CTRL,
+ MT6660_REG_ADC_USB_MODE,
+ MT6660_REG_INTERNAL_CFG,
+ MT6660_REG_RESV0,
+ MT6660_REG_RESV1,
+ MT6660_REG_RESV2,
+ MT6660_REG_RESV3,
+ MT6660_REG_RESV7,
+ MT6660_REG_RESV10,
+ MT6660_REG_RESV11,
+ MT6660_REG_RESV16,
+ MT6660_REG_RESV17,
+ MT6660_REG_RESV19,
+ MT6660_REG_RESV21,
+ MT6660_REG_RESV23,
+ MT6660_REG_RESV31,
+ MT6660_REG_RESV40,
+};
+
+#if GENERIC_DEBUGFS
+#ifdef CONFIG_DEBUG_FS
+/* reg/size/data/bustype */
+#define PREALLOC_RBUFFER_SIZE (32)
+#define PREALLOC_WBUFFER_SIZE (1000)
+
+static int data_debug_show(struct seq_file *s, void *data)
+{
+ struct dbg_info *di = s->private;
+ struct dbg_internal *d = &di->internal;
+ void *buffer;
+ u8 *pdata;
+ int i, ret;
+
+ if (d->data_buffer_size < d->size) {
+ buffer = kzalloc(d->size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ kfree(d->data_buffer);
+ d->data_buffer = buffer;
+ d->data_buffer_size = d->size;
+ }
+ /* read transfer */
+ if (!di->io_read)
+ return -EPERM;
+ ret = di->io_read(di->io_drvdata, d->reg, d->data_buffer, d->size);
+ if (ret < 0)
+ return ret;
+ pdata = d->data_buffer;
+ seq_printf(s, "0x");
+ for (i = 0; i < d->size; i++)
+ seq_printf(s, "%02x,", *(pdata + i));
+ seq_printf(s, "\n");
+ return 0;
+}
+
+static int data_debug_open(struct inode *inode, struct file *file)
+{
+ if (file->f_mode & FMODE_READ)
+ return single_open(file, data_debug_show, inode->i_private);
+ return simple_open(inode, file);
+}
+
+static ssize_t data_debug_write(struct file *file,
+ const char __user *user_buf,
+ size_t cnt, loff_t *loff)
+{
+ struct dbg_info *di = file->private_data;
+ struct dbg_internal *d = &di->internal;
+ void *buffer;
+ u8 *pdata;
+ char buf[PREALLOC_WBUFFER_SIZE + 1], *token, *cur;
+ int val_cnt = 0, ret;
+
+ if (cnt > PREALLOC_WBUFFER_SIZE)
+ return -ENOMEM;
+ if (copy_from_user(buf, user_buf, cnt))
+ return -EFAULT;
+ buf[cnt] = 0;
+ /* buffer size check */
+ if (d->data_buffer_size < d->size) {
+ buffer = kzalloc(d->size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ kfree(d->data_buffer);
+ d->data_buffer = buffer;
+ d->data_buffer_size = d->size;
+ }
+ /* data parsing */
+ cur = buf;
+ pdata = d->data_buffer;
+ while ((token = strsep(&cur, ",\n")) != NULL) {
+ if (!*token)
+ break;
+ if (val_cnt++ >= d->size)
+ break;
+ if (kstrtou8(token, 16, pdata++))
+ return -EINVAL;
+ }
+ if (val_cnt != d->size)
+ return -EINVAL;
+ /* write transfer */
+ if (!di->io_write)
+ return -EPERM;
+ ret = di->io_write(di->io_drvdata, d->reg, d->data_buffer, d->size);
+ return (ret < 0) ? ret : cnt;
+}
+
+static int data_debug_release(struct inode *inode, struct file *file)
+{
+ if (file->f_mode & FMODE_READ)
+ return single_release(inode, file);
+ return 0;
+}
+
+static const struct file_operations data_debug_fops = {
+ .open = data_debug_open,
+ .read = seq_read,
+ .write = data_debug_write,
+ .llseek = seq_lseek,
+ .release = data_debug_release,
+};
+
+static int type_debug_show(struct seq_file *s, void *data)
+{
+ struct dbg_info *di = s->private;
+
+ seq_printf(s, "%s,%s\n", di->typestr, di->devname);
+ return 0;
+}
+
+static int type_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, type_debug_show, inode->i_private);
+}
+
+static const struct file_operations type_debug_fops = {
+ .open = type_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int dump_debug_show(struct seq_file *s, void *data)
+{
+ struct dbg_info *di = s->private;
+ struct mt6660_chip *chip =
+ container_of(di, struct mt6660_chip, dbg_info);
+ int i = 0, j = 0, ret = 0;
+ unsigned int val;
+ int size = 0;
+
+ if (!chip) {
+ pr_err("%s chip is null\n", __func__);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mt6660_dump_table); i++) {
+ ret = regmap_read(chip->regmap, mt6660_dump_table[i], &val);
+ size = mt6660_get_reg_size(mt6660_dump_table[i]);
+ seq_printf(s, "reg0x%02x:0x", mt6660_dump_table[i]);
+ for (j = size - 1; j >= 0; j--)
+ seq_printf(s, "%x,", (val >> 8*j)&0xff);
+ seq_printf(s, "\n");
+ }
+ return 0;
+}
+
+static int dump_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dump_debug_show, inode->i_private);
+}
+
+static const struct file_operations dump_debug_fops = {
+ .open = dump_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static ssize_t lock_debug_read(struct file *file,
+ char __user *user_buf, size_t cnt, loff_t *loff)
+{
+ struct dbg_info *di = file->private_data;
+ struct dbg_internal *d = &di->internal;
+ char buf[10];
+
+ snprintf(buf, sizeof(buf), "%d\n", mutex_is_locked(&d->io_lock));
+ return simple_read_from_buffer(user_buf, cnt, loff, buf, strlen(buf));
+}
+
+static ssize_t lock_debug_write(struct file *file,
+ const char __user *user_buf,
+ size_t cnt, loff_t *loff)
+{
+ struct dbg_info *di = file->private_data;
+ struct dbg_internal *d = &di->internal;
+ u32 lock;
+ int ret;
+
+ ret = kstrtou32_from_user(user_buf, cnt, 0, &lock);
+ if (ret < 0)
+ return ret;
+ lock ? mutex_lock(&d->io_lock) : mutex_unlock(&d->io_lock);
+ return cnt;
+}
+
+static const struct file_operations lock_debug_fops = {
+ .open = simple_open,
+ .read = lock_debug_read,
+ .write = lock_debug_write,
+};
+
+static int generic_debugfs_init(struct dbg_info *di)
+{
+ struct dbg_internal *d = &di->internal;
+
+ /* valid check */
+ if (!di->dirname || !di->devname || !di->typestr)
+ return -EINVAL;
+ d->data_buffer_size = PREALLOC_RBUFFER_SIZE;
+ d->data_buffer = kzalloc(PREALLOC_RBUFFER_SIZE, GFP_KERNEL);
+ if (!d->data_buffer)
+ return -ENOMEM;
+ /* create debugfs */
+ d->rt_root = debugfs_lookup("ext_dev_io", NULL);
+ if (!d->rt_root) {
+ d->rt_root = debugfs_create_dir("ext_dev_io", NULL);
+ if (!d->rt_root)
+ return -ENODEV;
+ d->rt_dir_create = true;
+ }
+ d->ic_root = debugfs_create_dir(di->dirname, d->rt_root);
+ if (!d->ic_root)
+ goto err_cleanup_rt;
+ if (!debugfs_create_u16("reg", 0644, d->ic_root, &d->reg))
+ goto err_cleanup_ic;
+ if (!debugfs_create_u16("size", 0644, d->ic_root, &d->size))
+ goto err_cleanup_ic;
+ if (!debugfs_create_file("data", 0644,
+ d->ic_root, di, &data_debug_fops))
+ goto err_cleanup_ic;
+ if (!debugfs_create_file("type", 0444,
+ d->ic_root, di, &type_debug_fops))
+ goto err_cleanup_ic;
+ if (!debugfs_create_file("lock", 0644,
+ d->ic_root, di, &lock_debug_fops))
+ goto err_cleanup_ic;
+ if (!debugfs_create_file("dumps", 0444,
+ d->ic_root, di, &dump_debug_fops))
+ goto err_cleanup_ic;
+ mutex_init(&d->io_lock);
+ return 0;
+err_cleanup_ic:
+ debugfs_remove_recursive(d->ic_root);
+err_cleanup_rt:
+ if (d->rt_dir_create)
+ debugfs_remove_recursive(d->rt_root);
+ kfree(d->data_buffer);
+ return -ENODEV;
+}
+
+static void generic_debugfs_exit(struct dbg_info *di)
+{
+ struct dbg_internal *d = &di->internal;
+
+ mutex_destroy(&d->io_lock);
+ debugfs_remove_recursive(d->ic_root);
+ if (d->rt_dir_create)
+ debugfs_remove_recursive(d->rt_root);
+ kfree(d->data_buffer);
+}
+#else
+static inline int generic_debugfs_init(struct dbg_info *di)
+{
+ return 0;
+}
+
+static inline void generic_debugfs_exit(struct dbg_info *di) {}
+#endif /* CONFIG_DEBUG_FS */
+#endif /* GENERIC_DEBUGFS */
+
+/*
+ * MT6660 Generic Setting make this chip work normally.
+ * it is tuned by Richtek RDs.
+ */
+static const struct codec_reg_val generic_reg_inits[] = {
+ { MT6660_REG_WDT_CTRL, 0x80, 0x00 },
+ { MT6660_REG_SPS_CTRL, 0x01, 0x00 },
+ { MT6660_REG_AUDIO_IN2_SEL, 0x1c, 0x04 },
+ { MT6660_REG_RESV11, 0x0c, 0x00 },
+ { MT6660_REG_RESV31, 0x03, 0x03 },
+ { MT6660_REG_RESV40, 0x01, 0x00 },
+ { MT6660_REG_RESV0, 0x44, 0x04 },
+ { MT6660_REG_RESV19, 0xff, 0x82 },
+ { MT6660_REG_RESV17, 0x7777, 0x7273 },
+ { MT6660_REG_RESV16, 0x07, 0x03 },
+ { MT6660_REG_DRE_CORASE, 0xe0, 0x20 },
+ { MT6660_REG_ADDA_CLOCK, 0xff, 0x70 },
+ { MT6660_REG_RESV21, 0xff, 0x20 },
+ { MT6660_REG_DRE_THDMODE, 0xff, 0x40 },
+ { MT6660_REG_RESV23, 0xffff, 0x17f8 },
+ { MT6660_REG_PWM_CTRL, 0xff, 0x15 },
+ { MT6660_REG_ADC_USB_MODE, 0xff, 0x00 },
+ { MT6660_REG_PROTECTION_CFG, 0xff, 0x1d },
+ { MT6660_REG_HPF1_COEF, 0xffffffff, 0x7fdb7ffe },
+ { MT6660_REG_HPF2_COEF, 0xffffffff, 0x7fdb7ffe },
+ { MT6660_REG_SIG_GAIN, 0xff, 0x58 },
+ { MT6660_REG_RESV6, 0xff, 0xce },
+ { MT6660_REG_SIGMAX, 0xffff, 0x7fff },
+ { MT6660_REG_DA_GAIN, 0xffff, 0x0116 },
+ { MT6660_REG_TDM_CFG3, 0x1800, 0x0800 },
+ { MT6660_REG_DRE_CTRL, 0x1f, 0x07 },
+};
+
+static int mt6660_component_init_setting(struct snd_soc_component *component)
+{
+ int i, len, ret;
+ const struct codec_reg_val *init_table;
+
+ pr_info("%s start\n", __func__);
+ init_table = generic_reg_inits;
+ len = ARRAY_SIZE(generic_reg_inits);
+
+ for (i = 0; i < len; i++) {
+ ret = snd_soc_component_update_bits(component,
+ init_table[i].addr,
+ init_table[i].mask, init_table[i].data);
+ if (ret < 0)
+ return ret;
+ }
+ pr_info("%s end\n", __func__);
+ return 0;
+}
+
+static int mt6660_component_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct mt6660_chip *chip = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ ret = regmap_read(chip->regmap, MT6660_REG_IRQ_STATUS1, &val);
+ dev_info(component->dev,
+ "%s reg0x05 = 0x%x\n", __func__, val);
+ break;
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ /* Fall Through */
+ case SND_SOC_BIAS_STANDBY:
+ default:
+ break;
+ }
+ dapm->bias_level = level;
+ dev_dbg(component->dev, "c bias_level = %d\n", level);
+ return 0;
+}
+
+static int mt6660_codec_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ }
+ return 0;
+}
+
+static int mt6660_codec_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(component->dev,
+ "%s: before classd turn on\n", __func__);
+ /* config to adaptive mode */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_BST_CTRL, 0x03, 0x03);
+ if (ret < 0) {
+ dev_err(component->dev, "config mode adaptive fail\n");
+ return ret;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* voltage sensing enable */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV7, 0x04, 0x04);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "enable voltage sensing fail\n");
+ return ret;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* voltage sensing disable */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV7, 0x04, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "disable voltage sensing fail\n");
+ return ret;
+ }
+ /* pop-noise improvement 1 */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV10, 0x10, 0x10);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "pop-noise improvement 1 fail\n");
+ return ret;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(component->dev,
+ "%s: after classd turn off\n", __func__);
+ /* pop-noise improvement 2 */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_RESV10, 0x10, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "pop-noise improvement 2 fail\n");
+ return ret;
+ }
+ /* config to off mode */
+ ret = snd_soc_component_update_bits(component,
+ MT6660_REG_BST_CTRL, 0x03, 0x00);
+ if (ret < 0) {
+ dev_err(component->dev, "config mode off fail\n");
+ return ret;
+ }
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mt6660_component_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC", NULL, MT6660_REG_PLL_CFG1,
+ 0, 1, mt6660_codec_dac_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_ADC("VI ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV_E("ClassD", MT6660_REG_SYSTEM_CTRL, 2, 0,
+ NULL, 0, mt6660_codec_classd_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUTP"),
+ SND_SOC_DAPM_OUTPUT("OUTN"),
+};
+
+static const struct snd_soc_dapm_route mt6660_component_dapm_routes[] = {
+ { "DAC", NULL, "aif_playback"},
+ { "PGA", NULL, "DAC"},
+ { "ClassD", NULL, "PGA"},
+ { "OUTP", NULL, "ClassD"},
+ { "OUTN", NULL, "ClassD"},
+ { "VI ADC", NULL, "ClassD"},
+ { "aif_capture", NULL, "VI ADC"},
+};
+
+static int mt6660_component_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ int put_ret = 0;
+
+ pm_runtime_get_sync(component->dev);
+ put_ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (put_ret < 0)
+ return put_ret;
+ pm_runtime_put(component->dev);
+ return put_ret;
+}
+
+static int mt6660_component_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct mt6660_chip *chip = (struct mt6660_chip *)
+ snd_soc_component_get_drvdata(component);
+ int ret = -EINVAL;
+
+ if (!strcmp(kcontrol->id.name, "Chip_Rev")) {
+ ucontrol->value.integer.value[0] = chip->chip_rev & 0x0f;
+ ret = 0;
+ }
+ return ret;
+}
+
+static const DECLARE_TLV_DB_SCALE(vol_ctl_tlv, -1155, 5, 0);
+
+static const struct snd_kcontrol_new mt6660_component_snd_controls[] = {
+ SOC_SINGLE_EXT_TLV("Digital Volume", MT6660_REG_VOL_CTRL, 0, 255,
+ 1, snd_soc_get_volsw, mt6660_component_put_volsw,
+ vol_ctl_tlv),
+ SOC_SINGLE_EXT("WDT Switch", MT6660_REG_WDT_CTRL, 7, 1, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("Hard Clip Switch", MT6660_REG_HCLIP_CTRL, 8, 1, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("Clip Switch", MT6660_REG_SPS_CTRL, 0, 1, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("BoostMode", MT6660_REG_BST_CTRL, 0, 3, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("DRE Switch", MT6660_REG_DRE_CTRL, 0, 1, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("DC_Protect Switch",
+ MT6660_REG_DC_PROTECT_CTRL, 3, 1, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("audio input selection", MT6660_REG_DATAO_SEL, 6, 3, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("Data Output Left Channel Selection",
+ MT6660_REG_DATAO_SEL, 3, 7, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("Data Output Right Channel Selection",
+ MT6660_REG_DATAO_SEL, 0, 7, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ /* for debug purpose */
+ SOC_SINGLE_EXT("HPF AUD IN Switch", MT6660_REG_HPF_CTRL, 0, 1, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("AUD LOOP BACK Switch", MT6660_REG_PATH_BYPASS, 4, 1, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("Mute Switch", MT6660_REG_SYSTEM_CTRL, 1, 1, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("Bypass CS Comp Switch", MT6660_REG_PATH_BYPASS, 2, 1, 0,
+ snd_soc_get_volsw, mt6660_component_put_volsw),
+ SOC_SINGLE_EXT("T0 SEL", MT6660_REG_CALI_T0, 0, 7, 0,
+ snd_soc_get_volsw, NULL),
+ SOC_SINGLE_EXT("Chip Rev", SND_SOC_NOPM, 0, 16, 0,
+ mt6660_component_get_volsw, NULL),
+};
+
+static int mt6660_component_probe(struct snd_soc_component *component)
+{
+ struct mt6660_chip *chip = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ pr_info("%s\n", __func__);
+ //snd_soc_component_init_regmap(component, chip->regmap);
+
+ /* init chip setting */
+ ret = mt6660_component_init_setting(component);
+ if (ret < 0)
+ dev_err(chip->dev, "chip i2c init setting fail\n");
+
+ return ret;
+}
+
+static void mt6660_component_remove(struct snd_soc_component *component)
+{
+ pr_info("%s\n", __func__);
+ snd_soc_component_exit_regmap(component);
+}
+
+static const struct snd_soc_component_driver mt6660_component_driver = {
+ .probe = mt6660_component_probe,
+ .remove = mt6660_component_remove,
+ .read = mt6660_component_io_read,
+ .write = mt6660_component_io_write,
+
+ .controls = mt6660_component_snd_controls,
+ .num_controls = ARRAY_SIZE(mt6660_component_snd_controls),
+ .dapm_widgets = mt6660_component_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6660_component_dapm_widgets),
+ .dapm_routes = mt6660_component_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6660_component_dapm_routes),
+
+ .set_bias_level = mt6660_component_set_bias_level,
+ .idle_bias_on = false, /* idle_bias_off = true */
+};
+
+static int mt6660_component_aif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+ int word_len = params_physical_width(hw_params);
+ int aud_bit = params_width(hw_params);
+ u16 reg_data = 0;
+ int ret = 0;
+
+ dev_dbg(dai->dev, "%s: ++\n", __func__);
+ dev_dbg(dai->dev, "format: 0x%08x\n", params_format(hw_params));
+ dev_dbg(dai->dev, "rate: 0x%08x\n", params_rate(hw_params));
+ dev_dbg(dai->dev, "word_len: %d, aud_bit: %d\n", word_len, aud_bit);
+ if (word_len > 32 || word_len < 16) {
+ dev_err(dai->dev, "not supported word length\n");
+ return -ENOTSUPP;
+ }
+ switch (aud_bit) {
+ case 16:
+ reg_data = 3;
+ break;
+ case 18:
+ reg_data = 2;
+ break;
+ case 20:
+ reg_data = 1;
+ break;
+ case 24:
+ case 32:
+ reg_data = 0;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ ret = snd_soc_component_update_bits(dai->component,
+ MT6660_REG_SERIAL_CFG1, 0xc0, (reg_data << 6));
+ if (ret < 0) {
+ dev_err(dai->dev, "config aud bit fail\n");
+ return ret;
+ }
+ ret = snd_soc_component_update_bits(dai->component,
+ MT6660_REG_TDM_CFG3, 0x3f0, word_len << 4);
+ if (ret < 0) {
+ dev_err(dai->dev, "config word len fail\n");
+ return ret;
+ }
+ dev_dbg(dai->dev, "%s: --\n", __func__);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt6660_component_aif_ops = {
+ .hw_params = mt6660_component_aif_hw_params,
+};
+
+#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver mt6660_codec_dai = {
+ .name = "mt6660-aif",
+ .playback = {
+ .stream_name = "aif_playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STUB_RATES,
+ .formats = STUB_FORMATS,
+ },
+ .capture = {
+ .stream_name = "aif_capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STUB_RATES,
+ .formats = STUB_FORMATS,
+ },
+ /* dai properties */
+ .symmetric_rates = 1,
+ .symmetric_channels = 1,
+ .symmetric_samplebits = 1,
+ /* dai operations */
+ .ops = &mt6660_component_aif_ops,
+};
+
+static inline int _mt6660_chip_id_check(struct mt6660_chip *chip)
+{
+ u8 id[2] = {0};
+ int ret = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(chip->i2c, MT6660_REG_DEVID, 2, id);
+ if (ret < 0)
+ return ret;
+ ret = (id[0] << 8) + id[1];
+ ret &= 0x0ff0;
+ if (ret != 0x00e0 && ret != 0x01e0)
+ return -ENODEV;
+ return ret;
+}
+
+static inline int _mt6660_chip_sw_reset(struct mt6660_chip *chip)
+{
+ int ret;
+
+ /* turn on main pll first, then trigger reset */
+ ret = regmap_write(chip->regmap, 0x03, 0x00);
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x80);
+ msleep(30);
+ return 0;
+}
+
+static inline int _mt6660_chip_power_on(struct mt6660_chip *chip, int on_off)
+{
+ u8 reg_data = 0;
+ int ret = 0;
+
+ ret = i2c_smbus_read_byte_data(chip->i2c, MT6660_REG_SYSTEM_CTRL);
+ if (ret < 0)
+ return ret;
+ reg_data = (u8)ret;
+ if (on_off)
+ reg_data &= (~0x01);
+ else
+ reg_data |= 0x01;
+ return regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, reg_data);
+}
+
+static inline int _mt6660_read_chip_revision(struct mt6660_chip *chip)
+{
+ u8 reg_data[2] = {0};
+ int ret = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(
+ chip->i2c, MT6660_REG_DEVID, 2, reg_data);
+ if (ret < 0) {
+ dev_err(chip->dev, "get chip revision fail\n");
+ return ret;
+ }
+ chip->chip_rev = reg_data[1];
+ return 0;
+}
+
+static int mt6660_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mt6660_chip *chip = NULL;
+ int ret = 0;
+
+ pr_info("%s start\n", __func__);
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ chip->i2c = client;
+ chip->dev = &client->dev;
+ mutex_init(&chip->io_lock);
+ i2c_set_clientdata(client, chip);
+
+ chip->regmap = devm_regmap_init(&client->dev,
+ NULL, chip, &mt6660_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ ret = PTR_ERR(chip->regmap);
+ dev_err(&client->dev, "failed to initialise regmap: %d\n", ret);
+ return ret;
+ }
+
+#if GENERIC_DEBUGFS
+ /* debugfs interface */
+ chip->dbg_info.dirname = devm_kasprintf(&client->dev,
+ GFP_KERNEL, "MT6660.%s",
+ dev_name(&client->dev));
+ chip->dbg_info.devname = dev_name(&client->dev);
+ chip->dbg_info.typestr = devm_kasprintf(&client->dev,
+ GFP_KERNEL, "I2C,MT6660");
+ chip->dbg_info.io_drvdata = chip;
+ chip->dbg_info.io_read = mt6660_dbg_io_read;
+ chip->dbg_info.io_write = mt6660_dbg_io_write;
+
+ ret = generic_debugfs_init(&chip->dbg_info);
+ if (ret < 0) {
+ dev_err(&client->dev, "generic dbg init fail\n");
+ return -EINVAL;
+ }
+#endif /* GENERIC_DEBUGFS */
+
+ /* chip reset first */
+ ret = _mt6660_chip_sw_reset(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "chip reset fail\n");
+ goto probe_fail;
+ }
+ /* chip power on */
+ ret = _mt6660_chip_power_on(chip, 1);
+ if (ret < 0) {
+ dev_err(chip->dev, "chip power on 2 fail\n");
+ goto probe_fail;
+ }
+ /* chip devid check */
+ ret = _mt6660_chip_id_check(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "chip id check fail\n");
+ goto probe_fail;
+ }
+ /* chip revision get */
+ ret = _mt6660_read_chip_revision(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "read chip revision fail\n");
+ goto probe_fail;
+ }
+ pm_runtime_set_active(chip->dev);
+ pm_runtime_enable(chip->dev);
+
+ ret = devm_snd_soc_register_component(chip->dev,
+ &mt6660_component_driver,
+ &mt6660_codec_dai, 1);
+ pr_info("%s end, ret = %d\n", __func__, ret);
+ return ret;
+probe_fail:
+ _mt6660_chip_power_on(chip, 0);
+ mutex_destroy(&chip->io_lock);
+ return ret;
+}
+
+static int mt6660_i2c_remove(struct i2c_client *client)
+{
+ struct mt6660_chip *chip = i2c_get_clientdata(client);
+
+ pm_runtime_disable(chip->dev);
+ pm_runtime_set_suspended(chip->dev);
+#if GENERIC_DEBUGFS
+ generic_debugfs_exit(&chip->dbg_info);
+#endif /* GENERIC_DEBUGFS */
+ mutex_destroy(&chip->io_lock);
+ return 0;
+}
+
+static int __maybe_unused mt6660_i2c_runtime_suspend(struct device *dev)
+{
+ struct mt6660_chip *chip = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "enter low power mode\n");
+ return regmap_update_bits(chip->regmap,
+ MT6660_REG_SYSTEM_CTRL, 0x01, 0x01);
+}
+
+static int __maybe_unused mt6660_i2c_runtime_resume(struct device *dev)
+{
+ struct mt6660_chip *chip = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "exit low power mode\n");
+ return regmap_update_bits(chip->regmap,
+ MT6660_REG_SYSTEM_CTRL, 0x01, 0x00);
+}
+
+static const struct dev_pm_ops mt6660_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(mt6660_i2c_runtime_suspend,
+ mt6660_i2c_runtime_resume, NULL)
+};
+
+static const struct of_device_id __maybe_unused mt6660_of_id[] = {
+ { .compatible = "mediatek,mt6660",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6660_of_id);
+
+static const struct i2c_device_id mt6660_i2c_id[] = {
+ {"mt6660", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mt6660_i2c_id);
+
+static struct i2c_driver mt6660_i2c_driver = {
+ .driver = {
+ .name = "mt6660",
+ .of_match_table = of_match_ptr(mt6660_of_id),
+ .pm = &mt6660_dev_pm_ops,
+ },
+ .probe = mt6660_i2c_probe,
+ .remove = mt6660_i2c_remove,
+ .id_table = mt6660_i2c_id,
+};
+module_i2c_driver(mt6660_i2c_driver);
+
+MODULE_AUTHOR("Jeff Chang <jeff_chang(a)richtek.com>");
+MODULE_DESCRIPTION("MT6660 SPKAMP Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0.7_G");
diff --git a/sound/soc/codecs/mt6660.h b/sound/soc/codecs/mt6660.h
new file mode 100644
index 0000000..9e62dcc
--- /dev/null
+++ b/sound/soc/codecs/mt6660.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+#ifndef __SND_SOC_MT6660_H
+#define __SND_SOC_MT6660_H
+
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+
+#define GENERIC_DEBUGFS 0
+
+#if GENERIC_DEBUGFS
+struct dbg_internal {
+ struct dentry *rt_root;
+ struct dentry *ic_root;
+ bool rt_dir_create;
+ struct mutex io_lock;
+ u16 reg;
+ u16 size;
+ u16 data_buffer_size;
+ void *data_buffer;
+};
+
+struct dbg_info {
+ const char *dirname;
+ const char *devname;
+ const char *typestr;
+ void *io_drvdata;
+ int (*io_read)(void *drvdata, u16 reg, void *val, u16 size);
+ int (*io_write)(void *drvdata, u16 reg, const void *val, u16 size);
+ struct dbg_internal internal;
+};
+#endif /* GENERIC_DEBUGFS */
+
+struct mt6660_chip {
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct platform_device *param_dev;
+ struct mutex io_lock;
+#if GENERIC_DEBUGFS
+ struct dbg_info dbg_info;
+#endif /* GENERIC_DEBUGFS */
+ struct regmap *regmap;
+ u16 chip_rev;
+};
+
+#define MT6660_REG_DEVID (0x00)
+#define MT6660_REG_SYSTEM_CTRL (0x03)
+#define MT6660_REG_IRQ_STATUS1 (0x05)
+#define MT6660_REG_ADDA_CLOCK (0x07)
+#define MT6660_REG_SERIAL_CFG1 (0x10)
+#define MT6660_REG_DATAO_SEL (0x12)
+#define MT6660_REG_TDM_CFG3 (0x15)
+#define MT6660_REG_HPF_CTRL (0x18)
+#define MT6660_REG_HPF1_COEF (0x1A)
+#define MT6660_REG_HPF2_COEF (0x1B)
+#define MT6660_REG_PATH_BYPASS (0x1E)
+#define MT6660_REG_WDT_CTRL (0x20)
+#define MT6660_REG_HCLIP_CTRL (0x24)
+#define MT6660_REG_VOL_CTRL (0x29)
+#define MT6660_REG_SPS_CTRL (0x30)
+#define MT6660_REG_SIGMAX (0x33)
+#define MT6660_REG_CALI_T0 (0x3F)
+#define MT6660_REG_BST_CTRL (0x40)
+#define MT6660_REG_PROTECTION_CFG (0x46)
+#define MT6660_REG_DA_GAIN (0x4c)
+#define MT6660_REG_AUDIO_IN2_SEL (0x50)
+#define MT6660_REG_SIG_GAIN (0x51)
+#define MT6660_REG_PLL_CFG1 (0x60)
+#define MT6660_REG_DRE_CTRL (0x68)
+#define MT6660_REG_DRE_THDMODE (0x69)
+#define MT6660_REG_DRE_CORASE (0x6B)
+#define MT6660_REG_PWM_CTRL (0x70)
+#define MT6660_REG_DC_PROTECT_CTRL (0x74)
+#define MT6660_REG_ADC_USB_MODE (0x7c)
+#define MT6660_REG_INTERNAL_CFG (0x88)
+#define MT6660_REG_RESV0 (0x98)
+#define MT6660_REG_RESV1 (0x99)
+#define MT6660_REG_RESV2 (0x9A)
+#define MT6660_REG_RESV3 (0x9B)
+#define MT6660_REG_RESV6 (0xA2)
+#define MT6660_REG_RESV7 (0xA3)
+#define MT6660_REG_RESV10 (0xB0)
+#define MT6660_REG_RESV11 (0xB1)
+#define MT6660_REG_RESV16 (0xB6)
+#define MT6660_REG_RESV17 (0xB7)
+#define MT6660_REG_RESV19 (0xB9)
+#define MT6660_REG_RESV21 (0xBB)
+#define MT6660_REG_RESV23 (0xBD)
+#define MT6660_REG_RESV31 (0xD3)
+#define MT6660_REG_RESV40 (0xE0)
+
+#endif /* __SND_SOC_MT6660_H */
--
2.7.4
4
3

[alsa-devel] [BUG] ALSA: seq: a possible sleep-in-atomic-context bug in snd_virmidi_dev_receive_event()
by Jia-Ju Bai 17 Dec '19
by Jia-Ju Bai 17 Dec '19
17 Dec '19
The driver may sleep while holding a read lock.
The function call path (from bottom to top) in Linux 4.19 is:
sound/core/seq/seq_memory.c, 96:
copy_from_user in snd_seq_dump_var_event
sound/core/seq/seq_virmidi.c, 97:
snd_seq_dump_var_event in snd_virmidi_dev_receive_event
sound/core/seq/seq_virmidi.c, 88:
_raw_read_lock in snd_virmidi_dev_receive_event
copy_from_user() can sleep at runtime.
I am not sure how to properly fix this possible bug, so I only report it.
This bug is found by a static analysis tool STCheck written by myself.
Best wishes,
Jia-Ju Bai
3
3
The following changes since commit 39870b0dec68ed7dd814beb697e541670975c7d8:
ASoC: DMI long name - avoid to add board name if matches with product name (2019-11-22 19:54:50 +0000)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git tags/asoc-fix-v5.5-rc2
for you to fetch changes up to 556672d75ff486e0b6786056da624131679e0576:
ASoC: wm8962: fix lambda value (2019-12-16 11:47:28 +0000)
----------------------------------------------------------------
ASoC: Fixes for v5.5
A collection of fixes since the merge window, mostly driver specific but
there's a few in the core that clean up fallout from the refactorings
done in the last cycle.
----------------------------------------------------------------
Amery Song (1):
ASoC: Intel: common: work-around incorrect ACPI HID for CML boards
Curtis Malainey (3):
ASoC: core: Init pcm runtime work early to avoid warnings
ASoC: core: only flush inited work during free
ASoC: SOF: Intel: split cht and byt debug window sizes
Daniel Baluta (2):
ASoC: SOF: topology: Fix unload for SAI/ESAI
ASoC: simple-card: Don't create separate link when platform is present
Dragos Tarcatu (2):
ASoC: topology: Check return value for snd_soc_add_dai_link()
ASoC: topology: Check return value for soc_tplg_pcm_create()
Hans de Goede (1):
ASoC: Intel: bytcr_rt5640: Update quirk for Teclast X89
Karol Trzcinski (2):
ASoC: SOF: loader: snd_sof_fw_parse_ext_data log warning on unknown header
ASoC: SOF: loader: fix snd_sof_fw_parse_ext_data
Keyon Jie (1):
ASoC: SOF: Intel: BYT: fix a copy/paste mistake in byt_dump()
Michael Walle (1):
ASoC: wm8904: fix automatic sysclk configuration
Shengjiu Wang (1):
ASoC: wm8962: fix lambda value
Shuming Fan (1):
ASoC: rt5682: fix i2c arbitration lost issue
Tzung-Bi Shih (3):
ASoC: max98090: remove msleep in PLL unlocked workaround
ASoC: max98090: exit workaround earlier if PLL is locked
ASoC: max98090: fix possible race conditions
Yu-Hsuan Hsu (1):
ASoC: AMD: Enable clk in startup intead of hw_params
YueHaibing (2):
ASoC: rt5677: Fix build error without CONFIG_SPI
ASoC: Intel: sst: Add missing include <linux/io.h>
include/sound/soc.h | 1 +
sound/soc/amd/acp-da7219-max98357a.c | 46 +++++++----------------
sound/soc/codecs/max98090.c | 30 ++++++++++-----
sound/soc/codecs/max98090.h | 1 -
sound/soc/codecs/rt5677-spi.h | 16 ++++++++
sound/soc/codecs/rt5682.c | 2 +
sound/soc/codecs/wm8904.c | 6 +++
sound/soc/codecs/wm8962.c | 4 +-
sound/soc/generic/simple-card.c | 6 +++
sound/soc/intel/atom/sst/sst.c | 1 +
sound/soc/intel/boards/bytcr_rt5640.c | 8 +++-
sound/soc/intel/common/soc-acpi-intel-cml-match.c | 41 +++++++++++---------
sound/soc/soc-compress.c | 6 +--
sound/soc/soc-core.c | 13 ++++++-
sound/soc/soc-pcm.c | 11 ++----
sound/soc/soc-topology.c | 27 +++++++++----
sound/soc/sof/intel/byt.c | 25 ++++++------
sound/soc/sof/loader.c | 7 +++-
sound/soc/sof/topology.c | 4 +-
19 files changed, 158 insertions(+), 97 deletions(-)
2
1