* asoc: s3c24xx+uda1380 - some questions
@ 2009-01-27 13:19 Vasily Khoruzhick
2009-01-27 15:00 ` Mark Brown
0 siblings, 1 reply; 19+ messages in thread
From: Vasily Khoruzhick @ 2009-01-27 13:19 UTC (permalink / raw)
To: alsa-devel
[-- Attachment #1.1: Type: text/plain, Size: 894 bytes --]
Hi,
I'm trying to develop asoc driver for hp ipaq rx1950 (s3c24xx + uda1380).
1st question: where I can enable/disable uda1380 codec power? I need to
control codec power for powersave reasons.
I tried to add custom power function call to uda1380_set_bias_level in
uda1380.c (pointer to power function is stored in codec->private_data), as it
done for uda134x codec, but after enable-disable-enable cycle (for example,
after resuming from suspend) driver cannot communicate with codec anymore
(i2c bus is always busy), only reloading i2c driver helps.
I suspect, power(0) is called in inappropriate moment, when i2c-transfer still
is not finished.
2nd question: for some reason, driver does not produce any sound during first
aplay invocation, only second and later aplay invocation driver produces
sound. What can be the reason of this problem?
Regards
Vasily
[-- Attachment #1.2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 13:19 asoc: s3c24xx+uda1380 - some questions Vasily Khoruzhick
@ 2009-01-27 15:00 ` Mark Brown
2009-01-27 15:19 ` Vasily Khoruzhick
0 siblings, 1 reply; 19+ messages in thread
From: Mark Brown @ 2009-01-27 15:00 UTC (permalink / raw)
To: Vasily Khoruzhick; +Cc: alsa-devel, Philipp Zabel
On Tue, Jan 27, 2009 at 03:19:39PM +0200, Vasily Khoruzhick wrote:
> I'm trying to develop asoc driver for hp ipaq rx1950 (s3c24xx + uda1380).
>
> 1st question: where I can enable/disable uda1380 codec power? I need to
> control codec power for powersave reasons.
CCing in Philipp Zabel who wrote the UDA1380 driver. Assuming you mean
control to cut the power to the chip the most standard way of doing it
would be with the regulator API but if the chip isn't being used much in
new designs it might not be worth it.
> I tried to add custom power function call to uda1380_set_bias_level in
> uda1380.c (pointer to power function is stored in codec->private_data), as it
> done for uda134x codec, but after enable-disable-enable cycle (for example,
> after resuming from suspend) driver cannot communicate with codec anymore
> (i2c bus is always busy), only reloading i2c driver helps.
The UDA134x is a bit of a special case since it needs the machine driver
to provide bitbanging functions for the L3 interface.
> 2nd question: for some reason, driver does not produce any sound during first
> aplay invocation, only second and later aplay invocation driver produces
> sound. What can be the reason of this problem?
That sounds like something in the teardown path should be done on
startup as well. Can't think of any obvious gotchas there, though I'd
be looking at the codec and machine drivers since the CPU driver for the
S3C24xx is used in quite a few designs.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 15:00 ` Mark Brown
@ 2009-01-27 15:19 ` Vasily Khoruzhick
2009-01-27 15:25 ` Mark Brown
0 siblings, 1 reply; 19+ messages in thread
From: Vasily Khoruzhick @ 2009-01-27 15:19 UTC (permalink / raw)
To: Mark Brown; +Cc: alsa-devel, Philipp Zabel
[-- Attachment #1.1: Type: text/plain, Size: 904 bytes --]
On Tuesday 27 January 2009 17:00:41 Mark Brown wrote:
> CCing in Philipp Zabel who wrote the UDA1380 driver. Assuming you mean
> control to cut the power to the chip the most standard way of doing it
> would be with the regulator API but if the chip isn't being used much in
> new designs it might not be worth it.
Uh-oh, I didn't thought that it will be so complicated to decide when turn one
gpio line to 0 or 1 :) Btw, I'm thinking about using own write- and read-
wrappers for uda1380, in this way I can check on each read/write whether
codec power is enabled.
> That sounds like something in the teardown path should be done on
> startup as well. Can't think of any obvious gotchas there, though I'd
> be looking at the codec and machine drivers since the CPU driver for the
> S3C24xx is used in quite a few designs.
Sorry, but what is "teardown path"?
Regards
Vasily
[-- Attachment #1.2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 15:19 ` Vasily Khoruzhick
@ 2009-01-27 15:25 ` Mark Brown
2009-01-27 15:49 ` Vasily Khoruzhick
0 siblings, 1 reply; 19+ messages in thread
From: Mark Brown @ 2009-01-27 15:25 UTC (permalink / raw)
To: Vasily Khoruzhick; +Cc: alsa-devel, Philipp Zabel
On Tue, Jan 27, 2009 at 05:19:53PM +0200, Vasily Khoruzhick wrote:
> Uh-oh, I didn't thought that it will be so complicated to decide when turn one
> gpio line to 0 or 1 :) Btw, I'm thinking about using own write- and read-
> wrappers for uda1380, in this way I can check on each read/write whether
> codec power is enabled.
Are you really cutting all power to the codec or just the analogue
supplies? Normally the digital is powered off a separate supply shared
with other bits of the system.
> > That sounds like something in the teardown path should be done on
> > startup as well. Can't think of any obvious gotchas there, though I'd
> > be looking at the codec and machine drivers since the CPU driver for the
> > S3C24xx is used in quite a few designs.
> Sorry, but what is "teardown path"?
When playback is stopped.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 15:25 ` Mark Brown
@ 2009-01-27 15:49 ` Vasily Khoruzhick
2009-01-27 16:06 ` pHilipp Zabel
0 siblings, 1 reply; 19+ messages in thread
From: Vasily Khoruzhick @ 2009-01-27 15:49 UTC (permalink / raw)
To: Mark Brown; +Cc: alsa-devel, Philipp Zabel
[-- Attachment #1.1: Type: text/plain, Size: 697 bytes --]
On Tuesday 27 January 2009 17:25:57 Mark Brown wrote:
> On Tue, Jan 27, 2009 at 05:19:53PM +0200, Vasily Khoruzhick wrote:
> > Uh-oh, I didn't thought that it will be so complicated to decide when
> > turn one gpio line to 0 or 1 :) Btw, I'm thinking about using own write-
> > and read- wrappers for uda1380, in this way I can check on each
> > read/write whether codec power is enabled.
>
> Are you really cutting all power to the codec or just the analogue
> supplies? Normally the digital is powered off a separate supply shared
> with other bits of the system.
Yep, I'm really cutting all power to the codec. AFAIR it saves ~10-15mA of
current consumption.
Regards
Vasily
[-- Attachment #1.2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 15:49 ` Vasily Khoruzhick
@ 2009-01-27 16:06 ` pHilipp Zabel
2009-01-27 16:22 ` Vasily Khoruzhick
0 siblings, 1 reply; 19+ messages in thread
From: pHilipp Zabel @ 2009-01-27 16:06 UTC (permalink / raw)
To: Vasily Khoruzhick; +Cc: alsa-devel, Mark Brown
On Tue, Jan 27, 2009 at 4:49 PM, Vasily Khoruzhick <anarsoul@gmail.com> wrote:
> On Tuesday 27 January 2009 17:25:57 Mark Brown wrote:
>> On Tue, Jan 27, 2009 at 05:19:53PM +0200, Vasily Khoruzhick wrote:
>> > Uh-oh, I didn't thought that it will be so complicated to decide when
>> > turn one gpio line to 0 or 1 :) Btw, I'm thinking about using own write-
>> > and read- wrappers for uda1380, in this way I can check on each
>> > read/write whether codec power is enabled.
>>
>> Are you really cutting all power to the codec or just the analogue
>> supplies? Normally the digital is powered off a separate supply shared
>> with other bits of the system.
>
> Yep, I'm really cutting all power to the codec. AFAIR it saves ~10-15mA of
> current consumption.
Could you show the code?
On magician I'm just powering up uda1380 when the driver is loaded,
but I wouldn't mind saving some power :)
There are two GPIOs involved - one connected to the uda1380's RESET
line and one to control the power.
The last one doesn't have anything to do with uda1380, really. It's an
external power switch in a PMIC (or CPLD, on magican) so it would be
nice if this code could live in the machine specific drivers.
regards
Philipp
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 16:06 ` pHilipp Zabel
@ 2009-01-27 16:22 ` Vasily Khoruzhick
2009-01-27 19:49 ` pHilipp Zabel
2009-01-27 20:00 ` Mark Brown
0 siblings, 2 replies; 19+ messages in thread
From: Vasily Khoruzhick @ 2009-01-27 16:22 UTC (permalink / raw)
To: pHilipp Zabel; +Cc: alsa-devel, Mark Brown
[-- Attachment #1.1.1: Type: text/plain, Size: 897 bytes --]
On Tuesday 27 January 2009 18:06:47 pHilipp Zabel wrote:
> Could you show the code?
> On magician I'm just powering up uda1380 when the driver is loaded,
> but I wouldn't mind saving some power :)
> There are two GPIOs involved - one connected to the uda1380's RESET
> line and one to control the power.
> The last one doesn't have anything to do with uda1380, really. It's an
> external power switch in a PMIC (or CPLD, on magican) so it would be
> nice if this code could live in the machine specific drivers.
>
> regards
> Philipp
Yep, of course, here's machine driver (originally created by Denis Grigoriev)
and modified codec driver.
Btw, on rx1950 there're 4 gpio pins involved:
GPJ0 controls codec power
GPA1 controls amplifier power
GPG12 is jack sense pin, it's value depends whether headphone jack is inserted
or not,
and GPD0 is connected to uda1380 reset.
[-- Attachment #1.1.2: uda1380.c --]
[-- Type: text/x-csrc, Size: 25136 bytes --]
/*
* uda1380.c - Philips UDA1380 ALSA SoC audio driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
* Improved support for DAPM and audio routing/mixing capabilities,
;* added TLV support.
*
* Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
* codec model.
*
* Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
* Copyright 2005 Openedhand Ltd.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <sound/info.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include "uda1380.h"
#define UDA1380_VERSION "0.6"
/*
* uda1380 register cache
*/
static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = {
0x0502, 0x0000, 0x0000, 0x3f3f,
0x0202, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0xff00, 0x0000, 0x4800,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x8000, 0x0002, 0x0000,
};
/*
* read uda1380 register cache
*/
static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
if (reg == UDA1380_RESET)
return 0;
if (reg >= UDA1380_CACHEREGNUM)
return -1;
return cache[reg];
}
/*
* write uda1380 register cache
*/
static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
u16 reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
if (reg >= UDA1380_CACHEREGNUM)
return;
cache[reg] = value;
}
/*
* write to the UDA1380 register space
*/
static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[3];
/* data is
* data[0] is register offset
* data[1] is MS byte
* data[2] is LS byte
*/
data[0] = reg;
data[1] = (value & 0xff00) >> 8;
data[2] = value & 0x00ff;
uda1380_write_reg_cache(codec, reg, value);
/* the interpolator & decimator regs must only be written when the
* codec DAI is active.
*/
if (!codec->active && (reg >= UDA1380_MVOL))
return 0;
pr_debug("uda1380: hw write %x val %x\n", reg, value);
if (codec->hw_write(codec->control_data, data, 3) == 3) {
unsigned int val;
i2c_master_send(codec->control_data, data, 1);
i2c_master_recv(codec->control_data, data, 2);
val = (data[0]<<8) | data[1];
if (val != value) {
pr_debug("uda1380: READ BACK VAL %x\n",
(data[0]<<8) | data[1]);
return -EIO;
}
return 0;
} else
return -EIO;
}
#define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0)
/* declarations of ALSA reg_elem_REAL controls */
static const char *uda1380_deemp[] = {
"None",
"32kHz",
"44.1kHz",
"48kHz",
"96kHz",
};
static const char *uda1380_input_sel[] = {
"Line",
"Mic + Line R",
"Line L",
"Mic",
};
static const char *uda1380_output_sel[] = {
"DAC",
"Analog Mixer",
};
static const char *uda1380_spf_mode[] = {
"Flat",
"Minimum1",
"Minimum2",
"Maximum"
};
static const char *uda1380_capture_sel[] = {
"ADC",
"Digital Mixer"
};
static const char *uda1380_sel_ns[] = {
"3rd-order",
"5th-order"
};
static const char *uda1380_mix_control[] = {
"off",
"PCM only",
"before sound processing",
"after sound processing"
};
static const char *uda1380_sdet_setting[] = {
"3200",
"4800",
"9600",
"19200"
};
static const char *uda1380_os_setting[] = {
"single-speed",
"double-speed (no mixing)",
"quad-speed (no mixing)"
};
static const struct soc_enum uda1380_deemp_enum[] = {
SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, 5, uda1380_deemp),
SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, 5, uda1380_deemp),
};
static const struct soc_enum uda1380_input_sel_enum =
SOC_ENUM_SINGLE(UDA1380_ADC, 2, 4, uda1380_input_sel); /* SEL_MIC, SEL_LNA */
static const struct soc_enum uda1380_output_sel_enum =
SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel); /* R02_EN_AVC */
static const struct soc_enum uda1380_spf_enum =
SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode); /* M */
static const struct soc_enum uda1380_capture_sel_enum =
SOC_ENUM_SINGLE(UDA1380_IFACE, 6, 2, uda1380_capture_sel); /* SEL_SOURCE */
static const struct soc_enum uda1380_sel_ns_enum =
SOC_ENUM_SINGLE(UDA1380_MIXER, 14, 2, uda1380_sel_ns); /* SEL_NS */
static const struct soc_enum uda1380_mix_enum =
SOC_ENUM_SINGLE(UDA1380_MIXER, 12, 4, uda1380_mix_control); /* MIX, MIX_POS */
static const struct soc_enum uda1380_sdet_enum =
SOC_ENUM_SINGLE(UDA1380_MIXER, 4, 4, uda1380_sdet_setting); /* SD_VALUE */
static const struct soc_enum uda1380_os_enum =
SOC_ENUM_SINGLE(UDA1380_MIXER, 0, 3, uda1380_os_setting); /* OS */
/*
* from -48 dB in 1.5 dB steps (mute instead of -49.5 dB)
*/
static DECLARE_TLV_DB_SCALE(amix_tlv, -4950, 150, 1);
/*
* from -78 dB in 1 dB steps (3 dB steps, really. LSB are ignored),
* from -66 dB in 0.5 dB steps (2 dB steps, really) and
* from -52 dB in 0.25 dB steps
*/
static const unsigned int mvol_tlv[] = {
TLV_DB_RANGE_HEAD(3),
0, 15, TLV_DB_SCALE_ITEM(-8200, 100, 1),
16, 43, TLV_DB_SCALE_ITEM(-6600, 50, 0),
44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0),
};
/*
* from -72 dB in 1.5 dB steps (6 dB steps really),
* from -66 dB in 0.75 dB steps (3 dB steps really),
* from -60 dB in 0.5 dB steps (2 dB steps really) and
* from -46 dB in 0.25 dB steps
*/
static const unsigned int vc_tlv[] = {
TLV_DB_RANGE_HEAD(4),
0, 7, TLV_DB_SCALE_ITEM(-7800, 150, 1),
8, 15, TLV_DB_SCALE_ITEM(-6600, 75, 0),
16, 43, TLV_DB_SCALE_ITEM(-6000, 50, 0),
44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0),
};
/* from 0 to 6 dB in 2 dB steps if SPF mode != flat */
static DECLARE_TLV_DB_SCALE(tr_tlv, 0, 200, 0);
/* from 0 to 24 dB in 2 dB steps, if SPF mode == maximum, otherwise cuts
* off at 18 dB max) */
static DECLARE_TLV_DB_SCALE(bb_tlv, 0, 200, 0);
/* from -63 to 24 dB in 0.5 dB steps (-128...48) */
static DECLARE_TLV_DB_SCALE(dec_tlv, -6400, 50, 1);
/* from 0 to 24 dB in 3 dB steps */
static DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
/* from 0 to 30 dB in 2 dB steps */
static DECLARE_TLV_DB_SCALE(vga_tlv, 0, 200, 0);
static const struct snd_kcontrol_new uda1380_snd_controls[] = {
SOC_DOUBLE_TLV("Analog Mixer Volume", UDA1380_AMIX, 0, 8, 44, 1, amix_tlv), /* AVCR, AVCL */
SOC_DOUBLE_TLV("Master Playback Volume", UDA1380_MVOL, 0, 8, 252, 1, mvol_tlv), /* MVCL, MVCR */
SOC_SINGLE_TLV("ADC Playback Volume", UDA1380_MIXVOL, 8, 228, 1, vc_tlv), /* VC2 */
SOC_SINGLE_TLV("PCM Playback Volume", UDA1380_MIXVOL, 0, 228, 1, vc_tlv), /* VC1 */
SOC_ENUM("Sound Processing Filter", uda1380_spf_enum), /* M */
SOC_DOUBLE_TLV("Tone Control - Treble", UDA1380_MODE, 4, 12, 3, 0, tr_tlv), /* TRL, TRR */
SOC_DOUBLE_TLV("Tone Control - Bass", UDA1380_MODE, 0, 8, 15, 0, bb_tlv), /* BBL, BBR */
/**/ SOC_SINGLE("Master Playback Switch", UDA1380_DEEMP, 14, 1, 1), /* MTM */
SOC_SINGLE("ADC Playback Switch", UDA1380_DEEMP, 11, 1, 1), /* MT2 from decimation filter */
SOC_ENUM("ADC Playback De-emphasis", uda1380_deemp_enum[0]), /* DE2 */
SOC_SINGLE("PCM Playback Switch", UDA1380_DEEMP, 3, 1, 1), /* MT1, from digital data input */
SOC_ENUM("PCM Playback De-emphasis", uda1380_deemp_enum[1]), /* DE1 */
SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0), /* DA_POL_INV */
SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum), /* SEL_NS */
SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum), /* MIX_POS, MIX */
SOC_SINGLE("Silence Switch", UDA1380_MIXER, 7, 1, 0), /* SILENCE, force DAC output to silence */
SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0), /* SDET_ON */
SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum), /* SD_VALUE */
SOC_ENUM("Oversampling Input", uda1380_os_enum), /* OS */
SOC_DOUBLE_S8_TLV("ADC Capture Volume", UDA1380_DEC, -128, 48, dec_tlv), /* ML_DEC, MR_DEC */
/**/ SOC_SINGLE("ADC Capture Switch", UDA1380_PGA, 15, 1, 1), /* MT_ADC */
SOC_DOUBLE_TLV("Line Capture Volume", UDA1380_PGA, 0, 8, 8, 0, pga_tlv), /* PGA_GAINCTRLL, PGA_GAINCTRLR */
SOC_SINGLE("ADC Polarity inverting Switch", UDA1380_ADC, 12, 1, 0), /* ADCPOL_INV */
SOC_SINGLE_TLV("Mic Capture Volume", UDA1380_ADC, 8, 15, 0, vga_tlv), /* VGA_CTRL */
SOC_SINGLE("DC Filter Bypass Switch", UDA1380_ADC, 1, 1, 0), /* SKIP_DCFIL (before decimator) */
SOC_SINGLE("DC Filter Enable Switch", UDA1380_ADC, 0, 1, 0), /* EN_DCFIL (at output of decimator) */
SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0), /* TODO: enum, see table 62 */
SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1), /* AGC_LEVEL */
/* -5.5, -8, -11.5, -14 dBFS */
SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
};
/* add non dapm controls */
static int uda1380_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&uda1380_snd_controls[i], codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* Input mux */
static const struct snd_kcontrol_new uda1380_input_mux_control =
SOC_DAPM_ENUM("Route", uda1380_input_sel_enum);
/* Output mux */
static const struct snd_kcontrol_new uda1380_output_mux_control =
SOC_DAPM_ENUM("Route", uda1380_output_sel_enum);
/* Capture mux */
static const struct snd_kcontrol_new uda1380_capture_mux_control =
SOC_DAPM_ENUM("Route", uda1380_capture_sel_enum);
static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
&uda1380_input_mux_control),
SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0,
&uda1380_output_mux_control),
SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
&uda1380_capture_mux_control),
SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0),
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", UDA1380_PM, 2, 0),
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", UDA1380_PM, 0, 0),
SND_SOC_DAPM_INPUT("VINM"),
SND_SOC_DAPM_INPUT("VINL"),
SND_SOC_DAPM_INPUT("VINR"),
SND_SOC_DAPM_MIXER("Analog Mixer", UDA1380_PM, 6, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("VOUTLHP"),
SND_SOC_DAPM_OUTPUT("VOUTRHP"),
SND_SOC_DAPM_OUTPUT("VOUTL"),
SND_SOC_DAPM_OUTPUT("VOUTR"),
SND_SOC_DAPM_DAC("DAC", "Playback", UDA1380_PM, 10, 0),
SND_SOC_DAPM_PGA("HeadPhone Driver", UDA1380_PM, 13, 0, NULL, 0),
};
static const struct snd_soc_dapm_route audio_map[] = {
/* output mux */
{"HeadPhone Driver", NULL, "Output Mux"},
{"VOUTR", NULL, "Output Mux"},
{"VOUTL", NULL, "Output Mux"},
{"Analog Mixer", NULL, "VINR"},
{"Analog Mixer", NULL, "VINL"},
{"Analog Mixer", NULL, "DAC"},
{"Output Mux", "DAC", "DAC"},
{"Output Mux", "Analog Mixer", "Analog Mixer"},
/* {"DAC", "Digital Mixer", "I2S" } */
/* headphone driver */
{"VOUTLHP", NULL, "HeadPhone Driver"},
{"VOUTRHP", NULL, "HeadPhone Driver"},
/* input mux */
{"Left ADC", NULL, "Input Mux"},
{"Input Mux", "Mic", "Mic LNA"},
{"Input Mux", "Mic + Line R", "Mic LNA"},
{"Input Mux", "Line L", "Left PGA"},
{"Input Mux", "Line", "Left PGA"},
/* right input */
{"Right ADC", "Mic + Line R", "Right PGA"},
{"Right ADC", "Line", "Right PGA"},
/* inputs */
{"Mic LNA", NULL, "VINM"},
{"Left PGA", NULL, "VINL"},
{"Right PGA", NULL, "VINR"},
};
static int uda1380_add_widgets(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets,
ARRAY_SIZE(uda1380_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
int iface;
/* set up DAI based upon fmt */
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
/* FIXME: how to select I2S for DATAO and MSB for DATAI correctly? */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
iface |= R01_SFORI_I2S | R01_SFORO_I2S;
break;
case SND_SOC_DAIFMT_LSB:
iface |= R01_SFORI_LSB16 | R01_SFORO_I2S;
break;
case SND_SOC_DAIFMT_MSB:
iface |= R01_SFORI_MSB | R01_SFORO_I2S;
}
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
iface |= R01_SIM;
uda1380_write(codec, UDA1380_IFACE, iface);
return 0;
}
/*
* Flush reg cache
* We can only write the interpolator and decimator registers
* when the DAI is being clocked by the CPU DAI. It's up to the
* machine and cpu DAI driver to do this before we are called.
*/
static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
int reg, reg_start, reg_end, clk;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
reg_start = UDA1380_MVOL;
reg_end = UDA1380_MIXER;
} else {
reg_start = UDA1380_DEC;
reg_end = UDA1380_AGC;
}
/* FIXME disable DAC_CLK */
clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
for (reg = reg_start; reg <= reg_end; reg++) {
pr_debug("uda1380: flush reg %x val %x:", reg,
uda1380_read_reg_cache(codec, reg));
uda1380_write(codec, reg, uda1380_read_reg_cache(codec, reg));
}
/* FIXME enable DAC_CLK */
uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
return 0;
}
static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
/* set WSPLL power and divider if running from this clock */
if (clk & R00_DAC_CLK) {
int rate = params_rate(params);
u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
clk &= ~0x3; /* clear SEL_LOOP_DIV */
switch (rate) {
case 6250 ... 12500:
clk |= 0x0;
break;
case 12501 ... 25000:
clk |= 0x1;
break;
case 25001 ... 50000:
clk |= 0x2;
break;
case 50001 ... 100000:
clk |= 0x3;
break;
}
uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
clk |= R00_EN_DAC | R00_EN_INT;
else
clk |= R00_EN_ADC | R00_EN_DEC;
uda1380_write(codec, UDA1380_CLK, clk);
return 0;
}
static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
/* shut down WSPLL power if running from this clock */
if (clk & R00_DAC_CLK) {
u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
clk &= ~(R00_EN_DAC | R00_EN_INT);
else
clk &= ~(R00_EN_ADC | R00_EN_DEC);
uda1380_write(codec, UDA1380_CLK, clk);
}
static int uda1380_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & ~R13_MTM;
/* FIXME: mute(codec,0) is called when the magician clock is already
* set to WSPLL, but for some unknown reason writing to interpolator
* registers works only when clocked by SYSCLK */
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
if (mute)
uda1380_write(codec, UDA1380_DEEMP, mute_reg | R13_MTM);
else
uda1380_write(codec, UDA1380_DEEMP, mute_reg);
uda1380_write(codec, UDA1380_CLK, clk);
return 0;
}
static int uda1380_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
void (*power)(int) = codec->private_data;
int i;
u8 *cache = codec->reg_cache;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
power(1);
uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
break;
case SND_SOC_BIAS_STANDBY:
if (power) power(1);
uda1380_write(codec, UDA1380_PM, R02_PON_BIAS);
break;
case SND_SOC_BIAS_OFF:
uda1380_write(codec, UDA1380_PM, 0x0);
if (power) power(0);
break;
}
codec->bias_level = level;
return 0;
}
#define UDA1380_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
struct snd_soc_dai uda1380_dai[] = {
{
.name = "UDA1380",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = UDA1380_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = UDA1380_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.prepare = uda1380_pcm_prepare,
},
.dai_ops = {
.digital_mute = uda1380_mute,
.set_fmt = uda1380_set_dai_fmt,
},
},
{ /* playback only - dual interface */
.name = "UDA1380",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = UDA1380_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.prepare = uda1380_pcm_prepare,
},
.dai_ops = {
.digital_mute = uda1380_mute,
.set_fmt = uda1380_set_dai_fmt,
},
},
{ /* capture only - dual interface*/
.name = "UDA1380",
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = UDA1380_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.prepare = uda1380_pcm_prepare,
},
.dai_ops = {
.set_fmt = uda1380_set_dai_fmt,
},
},
};
EXPORT_SYMBOL_GPL(uda1380_dai);
static int uda1380_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int uda1380_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
/* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(uda1380_reg); i++) {
data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
data[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2);
}
uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
uda1380_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
/*
* initialise the UDA1380 driver
* register mixer and dsp interfaces with the kernel
*/
static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
{
struct snd_soc_codec *codec = socdev->codec;
int ret = 0;
codec->name = "UDA1380";
codec->owner = THIS_MODULE;
codec->read = uda1380_read_reg_cache;
codec->write = uda1380_write;
codec->set_bias_level = uda1380_set_bias_level;
codec->dai = uda1380_dai;
codec->num_dai = ARRAY_SIZE(uda1380_dai);
codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg),
GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
codec->reg_cache_step = 1;
uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
uda1380_reset(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
pr_err("uda1380: failed to create pcms\n");
goto pcm_err;
}
/* power on device */
/* set clock input */
switch (dac_clk) {
case UDA1380_DAC_CLK_SYSCLK:
uda1380_write(codec, UDA1380_CLK, 0);
break;
case UDA1380_DAC_CLK_WSPLL:
uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK);
break;
}
/* uda1380 init */
uda1380_add_controls(codec);
uda1380_add_widgets(codec);
ret = snd_soc_register_card(socdev);
if (ret < 0) {
pr_err("uda1380: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;
}
static struct snd_soc_device *uda1380_socdev;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int uda1380_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = uda1380_socdev;
struct uda1380_setup_data *setup = socdev->codec_data;
struct snd_soc_codec *codec = socdev->codec;
int ret;
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
ret = uda1380_init(socdev, setup->dac_clk);
if (ret < 0)
pr_err("uda1380: failed to initialise UDA1380\n");
return ret;
}
static int uda1380_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
kfree(codec->reg_cache);
return 0;
}
static const struct i2c_device_id uda1380_i2c_id[] = {
{ "uda1380", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
static struct i2c_driver uda1380_i2c_driver = {
.driver = {
.name = "UDA1380 I2C Codec",
.owner = THIS_MODULE,
},
.probe = uda1380_i2c_probe,
.remove = uda1380_i2c_remove,
.id_table = uda1380_i2c_id,
};
static int uda1380_add_i2c_device(struct platform_device *pdev,
const struct uda1380_setup_data *setup)
{
struct i2c_board_info info;
struct i2c_adapter *adapter;
struct i2c_client *client;
int ret;
ret = i2c_add_driver(&uda1380_i2c_driver);
if (ret != 0) {
dev_err(&pdev->dev, "can't add i2c driver\n");
return ret;
}
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = setup->i2c_address;
strlcpy(info.type, "uda1380", I2C_NAME_SIZE);
adapter = i2c_get_adapter(setup->i2c_bus);
if (!adapter) {
dev_err(&pdev->dev, "can't get i2c adapter %d\n",
setup->i2c_bus);
goto err_driver;
}
client = i2c_new_device(adapter, &info);
i2c_put_adapter(adapter);
if (!client) {
dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
(unsigned int)info.addr);
goto err_driver;
}
return 0;
err_driver:
i2c_del_driver(&uda1380_i2c_driver);
return -ENODEV;
}
#endif
static int uda1380_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct uda1380_setup_data *setup;
struct snd_soc_codec *codec;
int ret;
pr_info("UDA1380 Audio Codec %s\n", UDA1380_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
socdev->codec = codec;
/* Ugly, but should work */
codec->private_data = setup->power;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
uda1380_socdev = socdev;
ret = -ENODEV;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
codec->hw_write = (hw_write_t)i2c_master_send;
ret = uda1380_add_i2c_device(pdev, setup);
}
#endif
if (ret != 0)
kfree(codec);
return ret;
}
/* power down chip */
static int uda1380_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_unregister_device(codec->control_data);
i2c_del_driver(&uda1380_i2c_driver);
#endif
kfree(codec);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_uda1380 = {
.probe = uda1380_probe,
.remove = uda1380_remove,
.suspend = uda1380_suspend,
.resume = uda1380_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
MODULE_AUTHOR("Giorgio Padrin");
MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
MODULE_LICENSE("GPL");
[-- Attachment #1.1.3: rx1950_uda1380.c --]
[-- Type: text/x-csrc, Size: 13123 bytes --]
/*
* rx1950.c -- ALSA Soc Audio Layer
*
* Copyright (c) 2007 Roman Moravcik <roman.moravcik@gmail.com>
*
* Based on smdk2440.c and magician.c
*
* Authors: Graeme Gregory graeme.gregory@wolfsonmicro.com
* Philipp Zabel <philipp.zabel@gmail.com>
* Denis Grigoriev <dgreenday@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
#include <asm/hardware/scoop.h>
#include <asm/plat-s3c24xx/regs-iis.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <mach/regs-gpioj.h>
#include <mach/audio.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include "../codecs/uda1380.h"
#include "s3c24xx-pcm.h"
#include "s3c24xx-i2s.h"
//#define RX1950_DEBUG
#ifdef RX1950_DEBUG
#define DBG(x...) printk(KERN_INFO x)
#else
#define DBG(x...)
#endif
#define RX1950_HP_OFF 0
#define RX1950_HP_ON 1
#define RX1950_MIC 2
#define RX1950_SPK_ON 0
#define RX1950_SPK_OFF 1
static int rx1950_jack_func = RX1950_HP_ON;
static int rx1950_spk_func = RX1950_SPK_OFF;
static void rx1950_ext_control(struct snd_soc_codec *codec)
{
printk("%s entered\n", __func__);
if (rx1950_spk_func == RX1950_SPK_ON)
snd_soc_dapm_enable_pin(codec, "Speaker");
else
snd_soc_dapm_disable_pin(codec, "Speaker");
/* set up jack connection */
switch (rx1950_jack_func) {
case RX1950_HP_OFF:
snd_soc_dapm_disable_pin(codec, "Headphone Jack");
snd_soc_dapm_disable_pin(codec, "Mic Jack");
break;
case RX1950_HP_ON:
snd_soc_dapm_enable_pin(codec, "Headphone Jack");
snd_soc_dapm_disable_pin(codec, "Mic Jack");
break;
case RX1950_MIC:
snd_soc_dapm_disable_pin(codec, "Headphone Jack");
snd_soc_dapm_enable_pin(codec, "Mic Jack");
break;
}
snd_soc_dapm_sync(codec);
}
static int rx1950_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->socdev->codec;
printk("%s entered\n", __func__);
/* check the jack status at stream startup */
rx1950_ext_control(codec);
return 0;
}
static void rx1950_shutdown(struct snd_pcm_substream *substream)
{
// struct snd_soc_pcm_runtime *rtd = substream->private_data;
// struct snd_soc_codec *codec = rtd->socdev->codec;
//DBG("Entered rx1950_shutdown\n");
printk("%s entered\n", __func__);
}
static int rx1950_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
unsigned long iis_clkrate;
int div, div256, div384, diff256, diff384, bclk;
int ret;
unsigned int rate=params_rate(params);
DBG("Entered %s\n",__FUNCTION__);
iis_clkrate = s3c24xx_i2s_get_clockrate();
DBG("iis_clkrate = %li\n", iis_clkrate);
/* Using PCLK doesnt seem to suit audio particularly well on these cpu's
*/
div256 = iis_clkrate / (rate * 256);
div384 = iis_clkrate / (rate * 384);
if (((iis_clkrate / div256) - (rate * 256)) <
((rate * 256) - (iis_clkrate / (div256 + 1)))) {
diff256 = (iis_clkrate / div256) - (rate * 256);
} else {
div256++;
diff256 = (iis_clkrate / div256) - (rate * 256);
}
if (((iis_clkrate / div384) - (rate * 384)) <
((rate * 384) - (iis_clkrate / (div384 + 1)))) {
diff384 = (iis_clkrate / div384) - (rate * 384);
} else {
div384++;
diff384 = (iis_clkrate / div384) - (rate * 384);
}
DBG("diff256 %d, diff384 %d\n", diff256, diff384);
if (diff256<=diff384) {
DBG("Selected 256FS\n");
div = div256;
bclk = S3C2410_IISMOD_256FS;
} else {
DBG("Selected 384FS\n");
div = div384;
bclk = S3C2410_IISMOD_384FS;
}
/* set codec DAI configuration */
ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the audio system clock for DAC and ADC */
ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK,
rate, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
/* set MCLK division for sample rate */
ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, S3C2410_IISMOD_32FS );
if (ret < 0)
return ret;
/* set BCLK division for sample rate */
ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK, bclk);
if (ret < 0)
return ret;
/* set prescaler division for sample rate */
ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
S3C24XX_PRESCALE(div,div));
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops rx1950_ops = {
.startup = rx1950_startup,
.shutdown = rx1950_shutdown,
.hw_params = rx1950_hw_params,
// .prepare = rx1950_playback_prepare,
// .hw_free = rx1950_hw_free,
};
static int rx1950_get_jack(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = rx1950_jack_func;
return 0;
}
static int rx1950_set_jack(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (rx1950_jack_func == ucontrol->value.integer.value[0])
return 0;
rx1950_jack_func = ucontrol->value.integer.value[0];
rx1950_ext_control(codec);
return 1;
}
static int rx1950_get_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = rx1950_spk_func;
return 0;
}
static int rx1950_set_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (rx1950_spk_func == ucontrol->value.integer.value[0])
return 0;
rx1950_spk_func = ucontrol->value.integer.value[0];
if (rx1950_spk_func) {
s3c2410_gpio_setpin(S3C2410_GPA1, 0);
} else {
s3c2410_gpio_setpin(S3C2410_GPA1, 1);
}
rx1950_ext_control(codec);
return 1;
}
static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
s3c2410_gpio_cfgpin(S3C2410_GPA1, S3C2410_GPIO_OUTPUT);
if (SND_SOC_DAPM_EVENT_ON(event) && (rx1950_spk_func == 0))
s3c2410_gpio_setpin(S3C2410_GPA1, 1);
else
s3c2410_gpio_setpin(S3C2410_GPA1, 0);
return 0;
}
/* rx1950 machine dapm widgets */
static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_SPK("Speaker", rx1950_spk_power),
};
/* rx1950 machine audio_map */
static const struct snd_soc_dapm_route audio_map[] = {
/* headphone connected to VOUTLHP, VOUTRHP */
{"Headphone Jack", NULL, "VOUTLHP"},
{"Headphone Jack", NULL, "VOUTRHP"},
/* ext speaker connected to VOUTL, VOUTR */
{"Speaker", NULL, "VOUTL"},
{"Speaker", NULL, "VOUTR"},
/* mic is connected to VINM */
{"VINM", NULL, "Mic Jack"},
};
static const char *jack_function[] = {"Off", "Headphone", "Mic"};
static const char *spk_function[] = {"On", "Off"};
static const struct soc_enum rx1950_enum[] = {
SOC_ENUM_SINGLE_EXT(3, jack_function),
SOC_ENUM_SINGLE_EXT(2, spk_function),
};
static const struct snd_kcontrol_new uda1380_rx1950_controls[] = {
SOC_ENUM_EXT("Jack Function", rx1950_enum[0], rx1950_get_jack,
rx1950_set_jack),
SOC_ENUM_EXT("Speaker Function", rx1950_enum[1], rx1950_get_spk,
rx1950_set_spk),
};
/*
* Logic for a UDA1380 as attached to RX1950
*/
static int rx1950_uda1380_init(struct snd_soc_codec *codec)
{
int i, err;
DBG("Staring rx1950 init\n");
/* NC codec pins */
snd_soc_dapm_nc_pin(codec, "VOUTLHP");
snd_soc_dapm_nc_pin(codec, "VOUTRHP");
/* Add rx1950 specific widgets */
snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets,
ARRAY_SIZE(uda1380_dapm_widgets));
rx1950_ext_control(codec);
/* Add rx1950 specific controls */
for (i = 0; i < ARRAY_SIZE(uda1380_rx1950_controls); i++) {
if ((err = snd_ctl_add(codec->card,
snd_soc_cnew(&uda1380_rx1950_controls[i],
codec, NULL))) < 0)
return err;
}
/* Set up rx1950 specific audio path audio_mapnects */
err = snd_soc_dapm_add_routes(codec, audio_map,
ARRAY_SIZE(audio_map));
snd_soc_dapm_sync(codec);
DBG("Ending rx1950 init\n");
return 0;
}
/* s3c24xx digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link s3c24xx_dai = {
.name = "uda1380",
.stream_name = "UDA1380",
.cpu_dai = &s3c24xx_i2s_dai,
.codec_dai = &uda1380_dai[UDA1380_DAI_DUPLEX],
.init = rx1950_uda1380_init,
.ops = &rx1950_ops,
};
/* rx1950 audio machine driver */
static struct snd_soc_machine snd_soc_machine_rx1950 = {
.name = "RX1950",
.dai_link = &s3c24xx_dai,
.num_links = 1,
};
static void rx1950_codec_enable(int enable);
static struct uda1380_setup_data rx1950_uda1380_setup = {
.i2c_address = 0x1a,
.dac_clk = UDA1380_DAC_CLK_SYSCLK,
.power = rx1950_codec_enable,
};
/* s3c24xx audio subsystem */
static struct snd_soc_device s3c24xx_snd_devdata = {
.machine = &snd_soc_machine_rx1950,
.platform = &s3c24xx_soc_platform,
.codec_dev = &soc_codec_dev_uda1380,
.codec_data = &rx1950_uda1380_setup,
};
static struct platform_device *s3c24xx_snd_device;
//static DECLARE_COMPLETION(rx1950_sound_completion);
static DECLARE_MUTEX(rx1950_power_mutex);
//static spinlock_t cmpl_lock = SPIN_LOCK_UNLOCKED;
//static int waiting_for_completion;
static irqreturn_t codec_enabled(int irq, void *dev_id)
{
/*spin_lock(&cmpl_lock);
if (waiting_for_completion) {
printk("%s: completed!\n", __func__);
complete(&rx1950_sound_completion);
waiting_for_completion = 0;
}
spin_unlock(&cmpl_lock);*/
//printk("%s: got here!\n", __func__);
return IRQ_HANDLED;
}
static void rx1950_codec_enable(int enable)
{
down(&rx1950_power_mutex);
printk("%s: enable == %d\n", __func__, enable);
if (enable) {
if (s3c2410_gpio_getpin(S3C2440_GPJ0))
goto done;
//spin_lock(&cmpl_lock);
//waiting_for_completion = 1;
//spin_unlock(&cmpl_lock);
s3c2410_gpio_setpin(S3C2410_GPD0, 0);
s3c2410_gpio_setpin(S3C2440_GPJ0, 0);
s3c2410_gpio_setpin(S3C2440_GPJ0, 1);
/* Wait for EINT20 irq to ensure uda1380 is powered */
//printk("%s: waiting for compeltion...\n", __func__);
//wait_for_completion(&rx1950_sound_completion);
//printk("%s: completed\n", __func__);
//printk("%s: GPG12: %d\n", __func__,
// s3c2410_gpio_getpin(S3C2410_GPG12));
mdelay(50);
s3c2410_gpio_setpin(S3C2410_GPD0, 1);
s3c2410_gpio_setpin(S3C2410_GPD0, 0);
}
else {
if (!s3c2410_gpio_getpin(S3C2440_GPJ0))
goto done;
s3c2410_gpio_setpin(S3C2440_GPJ0, 1);
s3c2410_gpio_setpin(S3C2440_GPJ0, 0);
}
done:
printk("%s: done!\n", __func__);
up(&rx1950_power_mutex);
}
static int __init rx1950_init(void)
{
int ret;
/* configure some gpios */
s3c2410_gpio_cfgpin(S3C2410_GPD0, S3C2410_GPIO_OUTPUT);
s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_IRQ);
s3c2410_gpio_cfgpin(S3C2440_GPJ0, S3C2410_GPIO_OUTPUT);
s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
if (!s3c24xx_snd_device) {
printk(KERN_ERR "platform_dev_alloc failed\n");
return -ENOMEM;
}
if (request_irq(IRQ_EINT20, codec_enabled,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"snd_soc_rx1950_uda1380", s3c24xx_snd_device)) {
printk(KERN_ERR "can't get codec enabled irq.\n");
/* FIXME: fix memory leak here! */
return -ENOENT;
}
/* enable codec's power */
platform_set_drvdata(s3c24xx_snd_device, &s3c24xx_snd_devdata);
s3c24xx_snd_devdata.dev = &s3c24xx_snd_device->dev;
ret = platform_device_add(s3c24xx_snd_device);
if (ret) {
DBG("ret = %i\n",ret);
platform_device_put(s3c24xx_snd_device);
}
printk("%s done\n", __func__);
return ret;
}
static void __exit rx1950_exit(void)
{
disable_irq(IRQ_EINT20);
free_irq(IRQ_EINT20, s3c24xx_snd_device);
platform_device_unregister(s3c24xx_snd_device);
}
module_init(rx1950_init);
module_exit(rx1950_exit);
/* Module information */
MODULE_AUTHOR("Denis Grigoriev");
MODULE_DESCRIPTION("ALSA SoC RX1950");
MODULE_LICENSE("GPL");
[-- Attachment #1.1.4: uda1380.h --]
[-- Type: text/x-chdr, Size: 2450 bytes --]
/*
* Audio support for Philips UDA1380
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
*/
#ifndef _UDA1380_H
#define _UDA1380_H
#define UDA1380_CLK 0x00
#define UDA1380_IFACE 0x01
#define UDA1380_PM 0x02
#define UDA1380_AMIX 0x03
#define UDA1380_HP 0x04
#define UDA1380_MVOL 0x10
#define UDA1380_MIXVOL 0x11
#define UDA1380_MODE 0x12
#define UDA1380_DEEMP 0x13
#define UDA1380_MIXER 0x14
#define UDA1380_INTSTAT 0x18
#define UDA1380_DEC 0x20
#define UDA1380_PGA 0x21
#define UDA1380_ADC 0x22
#define UDA1380_AGC 0x23
#define UDA1380_DECSTAT 0x28
#define UDA1380_RESET 0x7f
#define UDA1380_CACHEREGNUM 0x24
/* Register flags */
#define R00_EN_ADC 0x0800
#define R00_EN_DEC 0x0400
#define R00_EN_DAC 0x0200
#define R00_EN_INT 0x0100
#define R00_DAC_CLK 0x0010
#define R01_SFORI_I2S 0x0000
#define R01_SFORI_LSB16 0x0100
#define R01_SFORI_LSB18 0x0200
#define R01_SFORI_LSB20 0x0300
#define R01_SFORI_MSB 0x0500
#define R01_SFORI_MASK 0x0700
#define R01_SFORO_I2S 0x0000
#define R01_SFORO_LSB16 0x0001
#define R01_SFORO_LSB18 0x0002
#define R01_SFORO_LSB20 0x0003
#define R01_SFORO_LSB24 0x0004
#define R01_SFORO_MSB 0x0005
#define R01_SFORO_MASK 0x0007
#define R01_SEL_SOURCE 0x0040
#define R01_SIM 0x0010
#define R02_PON_PLL 0x8000
#define R02_PON_HP 0x2000
#define R02_PON_DAC 0x0400
#define R02_PON_BIAS 0x0100
#define R02_EN_AVC 0x0080
#define R02_PON_AVC 0x0040
#define R02_PON_LNA 0x0010
#define R02_PON_PGAL 0x0008
#define R02_PON_ADCL 0x0004
#define R02_PON_PGAR 0x0002
#define R02_PON_ADCR 0x0001
#define R13_MTM 0x4000
#define R14_SILENCE 0x0080
#define R14_SDET_ON 0x0040
#define R21_MT_ADC 0x8000
#define R22_SEL_LNA 0x0008
#define R22_SEL_MIC 0x0004
#define R22_SKIP_DCFIL 0x0002
#define R23_AGC_EN 0x0001
struct uda1380_setup_data {
int i2c_bus;
unsigned short i2c_address;
int dac_clk;
void (* power)(int);
#define UDA1380_DAC_CLK_SYSCLK 0
#define UDA1380_DAC_CLK_WSPLL 1
};
#define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */
#define UDA1380_DAI_PLAYBACK 1 /* playback DAI */
#define UDA1380_DAI_CAPTURE 2 /* capture DAI */
extern struct snd_soc_dai uda1380_dai[3];
extern struct snd_soc_codec_device soc_codec_dev_uda1380;
#endif /* _UDA1380_H */
[-- Attachment #1.2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 16:22 ` Vasily Khoruzhick
@ 2009-01-27 19:49 ` pHilipp Zabel
2009-01-27 20:32 ` Vasily Khoruzhick
2009-01-27 20:00 ` Mark Brown
1 sibling, 1 reply; 19+ messages in thread
From: pHilipp Zabel @ 2009-01-27 19:49 UTC (permalink / raw)
To: Vasily Khoruzhick; +Cc: alsa-devel, Mark Brown
On Tue, Jan 27, 2009 at 5:22 PM, Vasily Khoruzhick <anarsoul@gmail.com> wrote:
> On Tuesday 27 January 2009 18:06:47 pHilipp Zabel wrote:
>
>> Could you show the code?
>> On magician I'm just powering up uda1380 when the driver is loaded,
>> but I wouldn't mind saving some power :)
>> There are two GPIOs involved - one connected to the uda1380's RESET
>> line and one to control the power.
>> The last one doesn't have anything to do with uda1380, really. It's an
>> external power switch in a PMIC (or CPLD, on magican) so it would be
>> nice if this code could live in the machine specific drivers.
>>
>> regards
>> Philipp
>
> Yep, of course, here's machine driver (originally created by Denis Grigoriev)
> and modified codec driver.
>
> Btw, on rx1950 there're 4 gpio pins involved:
> GPJ0 controls codec power
> GPA1 controls amplifier power
> GPG12 is jack sense pin, it's value depends whether headphone jack is inserted
> or not,
> and GPD0 is connected to uda1380 reset.
The uda1380 data sheet says that the reset time should be at least 1
µs - could that be a problem?
regards
Philipp
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 16:22 ` Vasily Khoruzhick
2009-01-27 19:49 ` pHilipp Zabel
@ 2009-01-27 20:00 ` Mark Brown
2009-01-27 20:14 ` Vasily Khoruzhick
1 sibling, 1 reply; 19+ messages in thread
From: Mark Brown @ 2009-01-27 20:00 UTC (permalink / raw)
To: Vasily Khoruzhick; +Cc: alsa-devel, pHilipp Zabel
On Tue, Jan 27, 2009 at 06:22:25PM +0200, Vasily Khoruzhick wrote:
> Btw, on rx1950 there're 4 gpio pins involved:
> GPJ0 controls codec power
> GPA1 controls amplifier power
> GPG12 is jack sense pin, it's value depends whether headphone jack is inserted
You might want to have a look at the jack reporting API that was
recently added, it should save you a bit of code:
http://git.kernel.org/?p=linux/kernel/git/broonie/sound-2.6.git;a=commit;h=8a2cd6180f8fa00111843c2f4a4f4361995358e0
It'll need a little helper for GPIOs adding to the core but that should
be reusable.
> down(&rx1950_power_mutex);
> printk("%s: enable == %d\n", __func__, enable);
> if (enable) {
> if (s3c2410_gpio_getpin(S3C2440_GPJ0))
> goto done;
> //spin_lock(&cmpl_lock);
> //waiting_for_completion = 1;
> //spin_unlock(&cmpl_lock);
> s3c2410_gpio_setpin(S3C2410_GPD0, 0);
> s3c2410_gpio_setpin(S3C2440_GPJ0, 0);
> s3c2410_gpio_setpin(S3C2440_GPJ0, 1);
>
> /* Wait for EINT20 irq to ensure uda1380 is powered */
> //printk("%s: waiting for compeltion...\n", __func__);
> //wait_for_completion(&rx1950_sound_completion);
> //printk("%s: completed\n", __func__);
> //printk("%s: GPG12: %d\n", __func__,
> // s3c2410_gpio_getpin(S3C2410_GPG12));
> mdelay(50);
> s3c2410_gpio_setpin(S3C2410_GPD0, 1);
> s3c2410_gpio_setpin(S3C2410_GPD0, 0);
Hrm. The completion makes this look more like it could use a full blown
regulator - it's certainly a pattern that will get reused often enough.
> /* configure some gpios */
> s3c2410_gpio_cfgpin(S3C2410_GPD0, S3C2410_GPIO_OUTPUT);
> s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_IRQ);
> s3c2410_gpio_cfgpin(S3C2440_GPJ0, S3C2410_GPIO_OUTPUT);
The S3C24xx now supports gpiolib so you should be able to use that - the
platform specific GPIO API will probably get killed at some point.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 20:00 ` Mark Brown
@ 2009-01-27 20:14 ` Vasily Khoruzhick
2009-01-27 20:19 ` Mark Brown
0 siblings, 1 reply; 19+ messages in thread
From: Vasily Khoruzhick @ 2009-01-27 20:14 UTC (permalink / raw)
To: Mark Brown; +Cc: alsa-devel, pHilipp Zabel
[-- Attachment #1.1: Type: text/plain, Size: 1332 bytes --]
On 27 January 2009 22:00:52 Mark Brown wrote:
> On Tue, Jan 27, 2009 at 06:22:25PM +0200, Vasily Khoruzhick wrote:
> > Btw, on rx1950 there're 4 gpio pins involved:
> > GPJ0 controls codec power
> > GPA1 controls amplifier power
> > GPG12 is jack sense pin, it's value depends whether headphone jack is
> > inserted
>
> You might want to have a look at the jack reporting API that was
> recently added, it should save you a bit of code:
>
> http://git.kernel.org/?p=linux/kernel/git/broonie/sound-2.6.git;a=commit;h
>=8a2cd6180f8fa00111843c2f4a4f4361995358e0
>
> It'll need a little helper for GPIOs adding to the core but that should
> be reusable.
Ok, thanks for advice
> Hrm. The completion makes this look more like it could use a full blown
> regulator - it's certainly a pattern that will get reused often enough.
It's commented out, I don't use completions anymore.
> > /* configure some gpios */
> > s3c2410_gpio_cfgpin(S3C2410_GPD0, S3C2410_GPIO_OUTPUT);
> > s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_IRQ);
> > s3c2410_gpio_cfgpin(S3C2440_GPJ0, S3C2410_GPIO_OUTPUT);
>
> The S3C24xx now supports gpiolib so you should be able to use that - the
> platform specific GPIO API will probably get killed at some point.
Yep, but s3c24xx gpiolib doesn't support all gpio pins, especially port J :)
Regards
Vasily
[-- Attachment #1.2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 20:14 ` Vasily Khoruzhick
@ 2009-01-27 20:19 ` Mark Brown
0 siblings, 0 replies; 19+ messages in thread
From: Mark Brown @ 2009-01-27 20:19 UTC (permalink / raw)
To: Vasily Khoruzhick; +Cc: alsa-devel, pHilipp Zabel
On Tue, Jan 27, 2009 at 10:14:36PM +0200, Vasily Khoruzhick wrote:
> On 27 January 2009 22:00:52 Mark Brown wrote:
> > Hrm. The completion makes this look more like it could use a full blown
> > regulator - it's certainly a pattern that will get reused often enough.
> It's commented out, I don't use completions anymore.
Yes, I assumed that was just temporary while you were working on this
though.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 19:49 ` pHilipp Zabel
@ 2009-01-27 20:32 ` Vasily Khoruzhick
2009-02-02 23:46 ` Vasily Khoruzhick
0 siblings, 1 reply; 19+ messages in thread
From: Vasily Khoruzhick @ 2009-01-27 20:32 UTC (permalink / raw)
To: pHilipp Zabel; +Cc: alsa-devel, Mark Brown
[-- Attachment #1.1: Type: text/plain, Size: 266 bytes --]
On 27 January 2009 21:49:01 pHilipp Zabel wrote:
> The uda1380 data sheet says that the reset time should be at least 1
> µs - could that be a problem?
>
> regards
> Philipp
Nope, added mdelay(10) in reset sequence, but it didn't help.
Regards
Vasily
[-- Attachment #1.2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-01-27 20:32 ` Vasily Khoruzhick
@ 2009-02-02 23:46 ` Vasily Khoruzhick
2009-02-03 11:41 ` Mark Brown
0 siblings, 1 reply; 19+ messages in thread
From: Vasily Khoruzhick @ 2009-02-02 23:46 UTC (permalink / raw)
To: pHilipp Zabel; +Cc: alsa-devel, Mark Brown
[-- Attachment #1.1.1: Type: text/plain, Size: 1073 bytes --]
On Tuesday 27 January 2009 22:32:11 Vasily Khoruzhick wrote:
> On 27 January 2009 21:49:01 pHilipp Zabel wrote:
> > The uda1380 data sheet says that the reset time should be at least 1
> > µs - could that be a problem?
> >
> > regards
> > Philipp
>
> Nope, added mdelay(10) in reset sequence, but it didn't help.
>
> Regards
> Vasily
Here's patch that fixes 2nd bug I've mentioned (there's a sound only on second
and later aplay). It's bug in uda1380 driver (probably, just a typo), driver
switches to WSPLL in uda1380_pcm_prepare even if SYSCLK was chosen
(uda1380_pcm_prepare modifies UDA1380_CLK register before flushing reg cache,
but doesn't restore its value later)
One more question: it seems that my rx1950 clocked in a way that I can't get
precise divisor for 44100 and 22050 rates, but uda1380 driver propose them
(look UDA1380_RATES define and struct snd_soc_dai uda1380_dai[].
How to exclude all rates except 16000 and 48000? Should I declare my own
snd_soc_dai and copy necessary members from uda1380's one?
Regards
Vasily
[-- Attachment #1.1.2: 0001-Fix-typo-in-uda1380-driver.patch --]
[-- Type: text/x-patch, Size: 789 bytes --]
From ad025e64d006dc86b7a669b8f5ab611b0b33b292 Mon Sep 17 00:00:00 2001
From: Vasily Khoruzhick <anarsoul@gmail.com>
Date: Tue, 3 Feb 2009 01:34:09 +0200
Subject: [PATCH] Fix typo in uda1380 driver
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
sound/soc/codecs/uda1380.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index a69ee72..a4d32b1 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -433,7 +433,7 @@ static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
}
/* FIXME enable DAC_CLK */
- uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
+ uda1380_write(codec, UDA1380_CLK, clk);
return 0;
}
--
1.6.1.1
[-- Attachment #1.2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-02-02 23:46 ` Vasily Khoruzhick
@ 2009-02-03 11:41 ` Mark Brown
2009-02-03 11:57 ` Vasily Khoruzhick
0 siblings, 1 reply; 19+ messages in thread
From: Mark Brown @ 2009-02-03 11:41 UTC (permalink / raw)
To: Vasily Khoruzhick; +Cc: alsa-devel, pHilipp Zabel
On Tue, Feb 03, 2009 at 01:46:32AM +0200, Vasily Khoruzhick wrote:
> and later aplay). It's bug in uda1380 driver (probably, just a typo), driver
> switches to WSPLL in uda1380_pcm_prepare even if SYSCLK was chosen
> (uda1380_pcm_prepare modifies UDA1380_CLK register before flushing reg cache,
> but doesn't restore its value later)
Your explanation here makes sense but...
> One more question: it seems that my rx1950 clocked in a way that I can't get
> precise divisor for 44100 and 22050 rates, but uda1380 driver propose them
> (look UDA1380_RATES define and struct snd_soc_dai uda1380_dai[].
> How to exclude all rates except 16000 and 48000? Should I declare my own
> snd_soc_dai and copy necessary members from uda1380's one?
Set up additional constraints in your machine driver - see how drivers
like wm8903 enforce symmetric configurations for playback and record for
an example.
>
> /* FIXME enable DAC_CLK */
> - uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
> + uda1380_write(codec, UDA1380_CLK, clk);
..are you sure this fix won't break existing users? Based on your
explanation above (which should *really* go into the commit) I'd expect
this to be conditional on something. It looks like what you really want
to do here is clean up the FIXMEs :)
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-02-03 11:41 ` Mark Brown
@ 2009-02-03 11:57 ` Vasily Khoruzhick
2009-02-03 12:05 ` Mark Brown
0 siblings, 1 reply; 19+ messages in thread
From: Vasily Khoruzhick @ 2009-02-03 11:57 UTC (permalink / raw)
To: Mark Brown; +Cc: alsa-devel, pHilipp Zabel
[-- Attachment #1.1: Type: text/plain, Size: 1296 bytes --]
On Tuesday 03 February 2009 13:41:37 Mark Brown wrote:
> > One more question: it seems that my rx1950 clocked in a way that I can't
> > get precise divisor for 44100 and 22050 rates, but uda1380 driver propose
> > them (look UDA1380_RATES define and struct snd_soc_dai uda1380_dai[]. How
> > to exclude all rates except 16000 and 48000? Should I declare my own
> > snd_soc_dai and copy necessary members from uda1380's one?
>
> Set up additional constraints in your machine driver - see how drivers
> like wm8903 enforce symmetric configurations for playback and record for
> an example.
Cool, thanks :)
> > /* FIXME enable DAC_CLK */
> > - uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
> > + uda1380_write(codec, UDA1380_CLK, clk);
>
> ..are you sure this fix won't break existing users? Based on your
> explanation above (which should *really* go into the commit) I'd expect
> this to be conditional on something. It looks like what you really want
> to do here is clean up the FIXMEs :)
Yep, I'm pretty sure. It will restore WSPLL bit if it was set before. It only
changes behavior of driver if SYSCLK was chosen. It seems that FIXMEs can be
removed, I can resubmit patch if you want.
Btw, Philipp, what do you think about it?
Regards
Vasily
[-- Attachment #1.2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-02-03 11:57 ` Vasily Khoruzhick
@ 2009-02-03 12:05 ` Mark Brown
2009-02-03 13:52 ` Vasily Khoruzhick
0 siblings, 1 reply; 19+ messages in thread
From: Mark Brown @ 2009-02-03 12:05 UTC (permalink / raw)
To: Vasily Khoruzhick; +Cc: alsa-devel, pHilipp Zabel
On Tue, Feb 03, 2009 at 01:57:12PM +0200, Vasily Khoruzhick wrote:
> Yep, I'm pretty sure. It will restore WSPLL bit if it was set before. It only
> changes behavior of driver if SYSCLK was chosen. It seems that FIXMEs can be
> removed, I can resubmit patch if you want.
Please do. You should at least include some text in the patch
description explaining what you're doing.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-02-03 12:05 ` Mark Brown
@ 2009-02-03 13:52 ` Vasily Khoruzhick
2009-02-03 16:09 ` pHilipp Zabel
2009-02-03 17:21 ` Mark Brown
0 siblings, 2 replies; 19+ messages in thread
From: Vasily Khoruzhick @ 2009-02-03 13:52 UTC (permalink / raw)
To: Mark Brown; +Cc: alsa-devel, pHilipp Zabel
[-- Attachment #1.1.1: Type: text/plain, Size: 356 bytes --]
On Tuesday 03 February 2009 14:05:08 Mark Brown wrote:
> Please do. You should at least include some text in the patch
> description explaining what you're doing.
One more version of patch, I've fixed patch description, but didn't removed
FIXME's, just fixed one comment.
Btw, it would be nice to hear Philipp's opinion :)
Regards
Vasily
[-- Attachment #1.1.2: 0001-UDA1380-driver-should-restore-UDA1380_CLK-register-v.patch --]
[-- Type: text/x-patch, Size: 1172 bytes --]
From 507528459911f7cc5745274bffd652e2f5302889 Mon Sep 17 00:00:00 2001
From: Vasily Khoruzhick <anarsoul@gmail.com>
Date: Tue, 3 Feb 2009 15:28:35 +0200
Subject: [PATCH] UDA1380 driver should restore UDA1380_CLK register value in
uda1380_pcm_prepare instead of enabling R00_DAC_CLK.
Without this fix driver switches to WSPLL in uda1380_pcm_prepare
even if SYSCLK was chosen (uda1380_pcm_prepare modifies UDA1380_CLK
register to disable R00_DAC_CLK before flushing reg cache)
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
sound/soc/codecs/uda1380.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index a69ee72..eba034a 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -432,8 +432,8 @@ static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
uda1380_write(codec, reg, uda1380_read_reg_cache(codec, reg));
}
- /* FIXME enable DAC_CLK */
- uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
+ /* FIXME restore DAC_CLK */
+ uda1380_write(codec, UDA1380_CLK, clk);
return 0;
}
--
1.6.1.1
[-- Attachment #1.2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-02-03 13:52 ` Vasily Khoruzhick
@ 2009-02-03 16:09 ` pHilipp Zabel
2009-02-03 17:21 ` Mark Brown
1 sibling, 0 replies; 19+ messages in thread
From: pHilipp Zabel @ 2009-02-03 16:09 UTC (permalink / raw)
To: Vasily Khoruzhick; +Cc: alsa-devel, Mark Brown
2009/2/3 Vasily Khoruzhick <anarsoul@gmail.com>:
> On Tuesday 03 February 2009 14:05:08 Mark Brown wrote:
>
>> Please do. You should at least include some text in the patch
>> description explaining what you're doing.
>
> One more version of patch, I've fixed patch description, but didn't removed
> FIXME's, just fixed one comment.
>
> Btw, it would be nice to hear Philipp's opinion :)
Yes, that patch is correct - thanks!
The FIXMEs are there because on magician flushing the interpolator
registers only seems to succeed when it is clocked by SYSCLK (see also
the comment in uda1380_mute).
I think this might be because at the time I flush the registers (in
uda1380_pcm_prepare), the SSP link is not yet running.
regards
Philipp
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: asoc: s3c24xx+uda1380 - some questions
2009-02-03 13:52 ` Vasily Khoruzhick
2009-02-03 16:09 ` pHilipp Zabel
@ 2009-02-03 17:21 ` Mark Brown
1 sibling, 0 replies; 19+ messages in thread
From: Mark Brown @ 2009-02-03 17:21 UTC (permalink / raw)
To: Vasily Khoruzhick; +Cc: alsa-devel, pHilipp Zabel
On Tue, Feb 03, 2009 at 03:52:56PM +0200, Vasily Khoruzhick wrote:
> One more version of patch, I've fixed patch description, but didn't removed
> FIXME's, just fixed one comment.
Applied, thanks.
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2009-02-03 17:21 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-01-27 13:19 asoc: s3c24xx+uda1380 - some questions Vasily Khoruzhick
2009-01-27 15:00 ` Mark Brown
2009-01-27 15:19 ` Vasily Khoruzhick
2009-01-27 15:25 ` Mark Brown
2009-01-27 15:49 ` Vasily Khoruzhick
2009-01-27 16:06 ` pHilipp Zabel
2009-01-27 16:22 ` Vasily Khoruzhick
2009-01-27 19:49 ` pHilipp Zabel
2009-01-27 20:32 ` Vasily Khoruzhick
2009-02-02 23:46 ` Vasily Khoruzhick
2009-02-03 11:41 ` Mark Brown
2009-02-03 11:57 ` Vasily Khoruzhick
2009-02-03 12:05 ` Mark Brown
2009-02-03 13:52 ` Vasily Khoruzhick
2009-02-03 16:09 ` pHilipp Zabel
2009-02-03 17:21 ` Mark Brown
2009-01-27 20:00 ` Mark Brown
2009-01-27 20:14 ` Vasily Khoruzhick
2009-01-27 20:19 ` Mark Brown
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.