From: Stuart Longland on
On Fri, Jun 18, 2010 at 04:53:23PM +0100, Mark Brown wrote:
> On Fri, Jun 18, 2010 at 09:33:35PM +1000, Stuart Longland wrote:
> > On Fri, Jun 18, 2010 at 12:01:02PM +0100, Liam Girdwood wrote:
> > > On Fri, 2010-06-18 at 13:57 +1000, Stuart Longland wrote:
>
> > > > The audio interface supports many data bus formats, including I??S
> > > > master/slave, DSP, left/right justified and TDM.
>
> > > Just had a quick check and the register caching needs addressed.
>
> > > I agree with your comments, I don't think we really want to cache all
> > > 16K of codec registers here as we will probably only ever use a handful
> > > of them. I think a smaller lookup table containing only the registers
> > > that we care about will do.
>
> > Yeah, I wasn't sure how to go about it. It's inefficient, but it's
> > simple, and works. Other options I've considered;
>
> > (1) Mark's suggestion of using per-page arrays
> > (2) Using address translation. that is:
> > - Page 0 registers 0-127 stored in cache array elements 0-127
> > - Page 1 registers 0-127 stored in cache array elements 128-255
> > - Page 2..7 are skipped
> > - Page 8 registers 0-127 stored in cache array elements 256-383
> > ... etc.
>
> Option 2 is what others have used here. I do think that anything we do
> here really ought to have some sort of page mapping support whatever the
> actual implementation it uses - TI in particular have a bunch of chips
> which do this so there's a general call for something that can handle
> them.
>
> If you are going to do a lookup table one way of doing this would be to
> have the lookup table be a table of range mappings (I've not looked at
> the patch, perhaps that's what you've done already) - just so long as
> it's got the concept of pages covered somehow.

Yeah... for now I've re-sent a new patch... it's on its way... with the
register cache in its present form. It's huge and unsightly, but won't
otherwise cause a problem.

I'd sooner get the driver out there in its known working form, then come
up with a solution to fix the problem in a scalable fashion, then
quickly try to hack something up here (I'm at home presently) where I
have no test hardware to check I haven't broken something... and where
the implementation and design will likely be an appauling mess. ;-)

> > Another thought regarding register cache, rather than hard-coding it the
> > way we presently do, we could also build up the cache on demand... that
> > is, we maintain a table of previously read registers; if a register
> > value is read for the first time, an actual read is done from the CODEC
> > (or the value is copied from a static table). All subsequent reads then
> > come from cache. On suspend; if a register has not been touched, it is
> > deemed to be left at default settings, and skipped on resume.
>
> The only problem with this is that for a number of reasons a lot of
> devices have no read functionality at all. These need us to supply the
> defaults from outside the device, though other devices could use what
> you suggest. I'd not be so bothered about the performance here - this
> has to be high performance in comparison with doing I/O on a slow bus
> such as I2C. If we do run into performance issues we can always work on
> substituting in a higher performance algorithm later, if everything is
> well encapsulated this should be doable without much disruption.
>
> It's also useful to have the actual defaults available for allowing
> writes to be skipped when powering up the device (eg, on resume) - these
> could be cached on first write so it's not insurmountable but it does
> need to be considered.

Indeed... I don't check if a register has been modified; I just copy it
across anyway. I'm aware some devices do not implement reads, that was
a comment made about the TLV320AIC3X device driver that I based my
driver on... in this situation you'd have no choice but to do it the way
it's presently done now.

I'll think about this over the next few days... but I'm thinking the
basic structure of such a register cache would possibly take the form of
something like this:

struct soc_reg_cache_pg {
/* Page index; for single-page registers, this would be zero */
int index;
/* Number of registers cached */
int size;
/* Register offset cached */
int offset;
/* Pointer to register flags */
const int *flags_data[];
/* Pointer to default values */
const int *default_data[];
/* Pointer to cached values */
int *cached_data[];
/* Pointer to next block of page registers (for sparse maps) */
struct soc_reg_cache_pg *next_block;
};

.... then you'd use several of these in an array... one for each block of
registers in a page. If your device uses sparse registers, you can use
the next_block pointer to point to the next such struct that has the
next bank of registers, to save you scanning through *every* struct.

The register cache could be an array of pointers, size equal to the
total number of pages, if a page is not used; it is set to NULL. If a
page is sparse, the other blocks would be accessed using the next_block
pointer.

The flags_data array would be optional; and could be used to indicate
the read/write status, volatile bits, and other information. It'd be a
bitfield.

Thoughts?

Regards,
--
Stuart Longland (aka Redhatter, VK4MSL) .'''.
Gentoo Linux/MIPS Cobalt and Docs Developer '.'` :
.. . . . . . . . . . . . . . . . . . . . . . .'.'
http://dev.gentoo.org/~redhatter :.'

I haven't lost my mind...
...it's backed up on a tape somewhere.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Mark Brown on
On Sat, Jun 19, 2010 at 08:43:14AM +1000, Stuart Longland wrote:

> I'll think about this over the next few days... but I'm thinking the
> basic structure of such a register cache would possibly take the form of
> something like this:
>
> struct soc_reg_cache_pg {
> /* Page index; for single-page registers, this would be zero */
> int index;
> /* Number of registers cached */
> int size;
> /* Register offset cached */
> int offset;
> /* Pointer to register flags */
> const int *flags_data[];
> /* Pointer to default values */
> const int *default_data[];

This should probably be either unsigned or void (the latter allowing for
pluggable register sizes - otherwise you might burn as much data as you
save from the sparseness on chips that have small registers).

> ... then you'd use several of these in an array... one for each block of
> registers in a page. If your device uses sparse registers, you can use
> the next_block pointer to point to the next such struct that has the
> next bank of registers, to save you scanning through *every* struct.

This is going to be painful to write out when writing the data table -
if it's useful to do it'd be better to do it at runtime, but I think
rather than handling pages it would be at least as easy to munge the
page number into the register number like everyone does at the minute
and then just optimise plain register lookups in a sparse map. That way
way sparse chips of all kinds get the benefit.

> The register cache could be an array of pointers, size equal to the
> total number of pages, if a page is not used; it is set to NULL. If a
> page is sparse, the other blocks would be accessed using the next_block
> pointer.

Like I say, I think optimising for page based access is not the most
general approach - it'd be better to optimise sparse register maps in
general and then map page based register maps onto this.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Stuart Longland on
On Sat, Jun 19, 2010 at 02:12:21AM +0100, Mark Brown wrote:
> On Sat, Jun 19, 2010 at 08:24:36AM +1000, Stuart Longland wrote:
>
> This all looks pretty good, the comments below are fairly minor.
>
> > + /* AVDD <->DVDD Link enable */
> > + u8 avdd_link_en;
>
> What's this? It looks like a boolean which makes u8 an odd choice.
>
> > + /* LDO Enable */
> > + u8 ldo_en;
>
> Similarly here.

Well, I didn't see the point in allocating 32-bits when I'd only be
using 1 of them. However, I can change it to an int if need be. I'm
not certain that C has a true "bool" type, but I could be wrong.

> > +
> > + /* Oversampling rate (both ADC and DAC) */
> > + u8 osr;
>
> Why is this platform data rather than a runtime configurable option?

How would the application elect a particular oversampling rate? I'll
admit I'm very new to the ALSA framework in general, but I didn't
realise there was an oversampling rate setting. I'll have a look at how
the other drivers do it.

> > +config SND_SOC_TLV320AIC3204
> > + tristate
> > + depends on I2C
> > +
>
> Drop the depends, it doesn't actually do anything for selected items.
>
> > + /* Page 1 */
> > + if (page == 1) {
> > + if (reg <= 4)
> > + return 1;
>
> I can't help but think that this'd be more legible with switch ()
> statements (GCC has an extension for ranges in switch statements which
> you could use).

I did if statements here for two reasons:

(1) Range selection (I was unaware of the GCC extension)
(2) I found by experiment that if is slighly more efficient than
switch... at least that was the case on MSP430 (using mspgcc).
Other platforms could be very different.

> > +/*
> > + * Pretty-print the value of a register
> > + */
> > +static int aic3204_show_reg(struct snd_soc_codec *codec, char *buf,
> > + int buf_sz, unsigned int reg)
> > +{
> > + return snprintf(buf, buf_sz, "%02x",
> > + aic3204_read_reg_cache(codec, reg));
> > +}
>
> This looks suspiciously close to the standard show_reg() - it seems
> wrong that you should need it.

Well, the main difference; standard show_reg uses %4x as its format; and
therefore wastes two characters printing nothing useful. Given space is
already tight for displaying register dumps via debugfs, any little bit
helps. :-)

Certainly there's no explicit reason why it is necessary and it can go.

> > + /*
> > + * These registers are not manipulated by us, so we must read them from
> > + * the CODEC directly.
> > + */
>
> Hrm? Also, it strikes me that this code is also used in the WCLK
> function and might benefit from being factored out - it's moderately
> verbose.

The ADC flags register (and the DAC flags registers) are read-only and
manipulated by the CODEC when the ADCs and DACs are actually powered up.
I could use the flags set elsewhere, but this reflects what *is* the
present state, not what ought to be the present state.

There is common code between the WCLK and BCLK functions; in fact I did
consider making it the one callback, but since they are separate in the
registers, I kept them separate in the callbacks.

> > +static const char *aic3204_micbias_voltages[] = {
> > + "Low", "Med", "High", "V+"
> > +};
>
> I'd be inclined to spell medium out and write "V+" as "Supply" or
> similar unless there's a strong reason to do otherwise - it seems more
> legible.

Fair enough... I was originally going to try and use some shortened
version of what's in the datasheet... but the values of "Low", "Medium"
and "High" are dependant on the selected source and the power rails.
"Supply" is probably just as descriptive, if not as compact as "V+"...
I'll update that.

> > +static const char *aic3204_ldac_srcs[] = {
> > + "disabled", "left channel", "right channel", "mix"
> > +};
>
> Capital letters are more idiomatic for enumerations.
>
> > +/*
> > + * DAC digital volumes. From -63.5 to +24 dB in 0.5 dB steps.
> > + * Does not mute control.
> > + */
>
> I'm finding these comments a little too verbose but that's just me -
> I stop and read them only to find there's nothing odd going on here.

Yeah, some of this is due to the fact that, in the early days, I wasn't
too sure if this was the "right way". So I figured I'd describe my
intentions, and then if I was doing it wrong, people would at least
know what was intended, and could suggest improvements.

Now that the mixer is working okay, I could cut this down... but also I
hope that others who follow the same path as me, might find this helpful
to determine what's going on.

> > + /*
> > + * Note regarding SOC_DOUBLE_R_SX_TLV...
> > + *
> > + * This is a bit misleading; xshift refers to the bit one bit *past*
> > + * the most significant bit. Or at least that's what it appears to be
> > + * doing the math. Mask needs to select the last 6 bits; which is the
> > + * signed bitfield specifiying the gain in dB.
> > + */
>
> I suspect that fixing the control may break what you're doing here? It
> would certainly bitrot the comment.

Well, when the problem goes, so can the comment. :-) One question
though, the shift value derives the mask for further operations on these
registers... if we were to change the shift value so it reflected how
other widget types work, how does one define the mask?

> > + SOC_SINGLE("Differential Output Switch", AIC3204_DACS2,
> > + AIC3204_DACS2_RMOD_INV_SHIFT, 1, 0),
>
> This feels like platform data - the use of differential outputs is
> normally a property of the physical wiring of the board, it's very rare
> to be able to vary this usefully at runtime.

Arguably, so is the DAC audio path routing. I wasn't sure how to set
this up at the time, initially it was CODEC setup data, then I moved it
into the mixer.

The other consideration here; is the use case where a device with a mono
speaker, but a stereo line-out jack... I wasn't sure how to handle these
features.

> > + /* MICBIAS Options */
> > + SOC_SINGLE("MICBIAS Enable", AIC3204_MICBIAS, 6, 0x1, 0),
>
> MICBIAS Enable should definitely be DAPM.
>
> > + SOC_ENUM("MICBIAS Level", aic3204_enum_micbias_voltage),
> > + SOC_ENUM("MICBIAS Source", aic3204_enum_micbias_src),
>
> These I would expect to be managed in kernel - they're normally either a
> property of the board or need to be managed in conjunction with jack
> detection code which tends to live in kernel.

I'll look into this... I was aware of the MICBIAS widget, but will have
to do some further research here. As to the others... I wasn't sure how
they should be handled.

> > +static const struct snd_soc_dapm_widget aic3204_dapm_widgets[] = {
> > + /* Driver/Amplifier Selection */
> > + SND_SOC_DAPM_SWITCH("HPL Playback Switch", AIC3204_OUTDRV,
> > + AIC3204_OUTDRV_HPL_UP_SHIFT, 0,
> > + &aic3204_hpl_switch),
> > + SND_SOC_DAPM_SWITCH("HPR Playback Switch", AIC3204_OUTDRV,
> > + AIC3204_OUTDRV_HPR_UP_SHIFT, 0,
> > + &aic3204_hpr_switch),
>
> A lot of these SWITCH controls feel like they ought to be PGAs and the
> switch controls mutes on those. When muting an output you normally
> don't want to power up and down the path since that is slow and tends to
> trigger audible issues, you'd normally control the power for path
> endpoints and elements that affect routing only.

They are PGAs connecting the driver to its mixer... I wasn't sure how
the PGAs worked in DAPM. I knew this did work however, the only catch
is that DAPM won't power up the DACs unless you switch them through via
these mixers to one of the output drivers. I'll have a closer look at
the PGAs and see what they're doing.

> > + SND_SOC_DAPM_AIF_IN("Left Data In", "Left Playback",
> > + 0, SND_SOC_NOPM, 0, 0),
> > + SND_SOC_DAPM_AIF_IN("Right Data In", "Right Playback",
> > + 1, SND_SOC_NOPM, 0, 0),
> > + SND_SOC_DAPM_AIF_OUT("Left Data Out", "Left Capture",
> > + 0, SND_SOC_NOPM, 0, 0),
> > + SND_SOC_DAPM_AIF_OUT("Right Data Out", "Right Capture",
> > + 1, SND_SOC_NOPM, 0, 0),
>
> You have these AIF widgets but at least the DAC input selection was
> being done in the regular controls rather than in the DAPM routing.

Indeed, DAC inputs are selectable, the ADC is not. I should probably
making the DAC inputs a MUX... I'll give this a re-think too.

> > + /*
> > + * Set BCLK and WCLK sources...
> > + *
> > + * Despite DAPM; this is still needed, as DAPM doesn't yet set the
> > + * source at the right time.
> > + *
> > + * TODO: See if we can change the order of initialisation so this
> > + * selection is made after bringing up the ADCs/DACs. Alternative:
> > + * see if we can find out ahead of time which one to use.
>
> It surprises me that the ordering matters too much here; worst case you
> leave the interface declocked a little longer when you need to switch
> sources but that doesn't seem like the end of the world?

Well, what I found is that if I didn't do it there... it would call the
WCLK and BCLK functions before powering up the DACs/ADCs. Since they
have no idea whether recording or playback is in progress (I wasn't sure
how to extract this information), the functions rely on the flags
registers to determine this.

Therefore, it only works *after* DAPM has turned on the respective
parts. Without manually switching it here, the functions see that
neither ADC nor DAC is active, and therefore don't do any switching of
the BCLK and WCLK. End result; no clocks, DMA times out and the user is
left with deafening silence.

This, is probably one of the biggest areas where the DAPM support in
this CODEC driver needs work. I tried to follow what was going on in
the other CODEC drivers, and I know what's submitted isn't quite right,
but it does at least work.

> > + snd_soc_update_bits(codec, AIC3204_AISR3, AIC3204_AISR3_BDIV,
> > + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> > + ? AIC3204_AISR3_BDIV_DAC
> > + : AIC3204_AISR3_BDIV_ADC);
>
> I have to say that I'm really not a fan of the ternery operator for
> legibility.
>
> > +static int aic3204_mute(struct snd_soc_dai *dai, int mute)
> > +{
>
> ...
>
> > + aic3204_write(codec, AIC3204_DACS2, dacs2); /* Unmute DAC */
>
> ...or possibly mute it :)

Yes, obsolete comment I guess. :-) This is also a mixer control...
which is better... leave it to ASoC core, or the user to control PCM
mute? The user can mute using the line driver controls... so I guess I
should ditch the "PCM Playback Switch" control.

> > +static int aic3204_resume(struct platform_device *pdev)
> > +{
> > + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> > + struct snd_soc_codec *codec = socdev->card->codec;
> > + int i;
> > + u8 data[2];
> > + u8 *cache = codec->reg_cache;
> > +
> > + /* Sync reg_cache with the hardware */
> > + for (i = 0; i < AIC3204_CACHEREGNUM; i++) {
> > + data[0] = i;
> > + data[1] = cache[i];
> > + codec->hw_write(codec->control_data, data, 2);
>
> Since you've got a register defaults table you could skip rewriting
> registers which have their default value.

Indeed, suspend/resume isn't tested at all, so no idea if it works or
not. I should probably do a forced reset too just to be on the safe
side. This is a reminant from the aic3x driver.

> > + /* LDO enable, analogue blocks enable */
> > + snd_soc_update_bits(codec, AIC3204_LDO,
> > + AIC3204_LDO_ANALOG | AIC3204_LDO_AVDD_UP,
> > + AIC3204_LDO_ANALOG_ENABLED |
> > + (aic3204->pdata.ldo_en
> > + ? AIC3204_LDO_AVDD_UP : 0));
>
> This looks a bit fishy since the mask covers more bits than can ever be
> enabled - it reads like the other two bits should be unconditionally
> cleared.

Would I be better breaking that statement up into two statements? I did
them as one for efficiency, but I do recognise there is a potential
readability issue. Basically, it clears the "analog power-down" bit in
the LDO register (yes, this inverse logic irritates me) and optionally
turns on the LDO. I figure some users may wish to provide their own
AVDD and thus won't want the LDO.

I wasn't sure about the link between AVDD and DVDD, so I left that
configurable. I'm guessing most will probably want it disabled. This
was configured using arbitrary register initialisation scripts, passed
in via the CODEC setup data... so at least things are headded in the
right direction. :-)

Regards,
--
Stuart Longland (aka Redhatter, VK4MSL) .'''.
Gentoo Linux/MIPS Cobalt and Docs Developer '.'` :
.. . . . . . . . . . . . . . . . . . . . . . .'.'
http://dev.gentoo.org/~redhatter :.'

I haven't lost my mind...
...it's backed up on a tape somewhere.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Mark Brown on
On Sat, Jun 19, 2010 at 07:49:08PM +1000, Stuart Longland wrote:
> On Sat, Jun 19, 2010 at 02:12:21AM +0100, Mark Brown wrote:
> > On Sat, Jun 19, 2010 at 08:24:36AM +1000, Stuart Longland wrote:

> > > + /* LDO Enable */
> > > + u8 ldo_en;

> > Similarly here.

> Well, I didn't see the point in allocating 32-bits when I'd only be
> using 1 of them. However, I can change it to an int if need be. I'm
> not certain that C has a true "bool" type, but I could be wrong.

C99 introduced a bool type, and there's always been bitfields. Besides,
you're microoptimising something that doesn't need it. When you write
u8 that means you really need this field to be unsigned and 8 bits which
is a bit confusing for something that's actually just a flag.

> > > + /* Oversampling rate (both ADC and DAC) */
> > > + u8 osr;

> > Why is this platform data rather than a runtime configurable option?

> How would the application elect a particular oversampling rate? I'll
> admit I'm very new to the ALSA framework in general, but I didn't
> realise there was an oversampling rate setting. I'll have a look at how
> the other drivers do it.

The oversampling rate selection is a performance/power tradeoff. The
user may choose to do things like use high performance when listening to
music but step back a bit when playing system sounds, for example.

There's no specific ALSA control for it, just make it a Switch.

> > > +/*
> > > + * Pretty-print the value of a register
> > > + */
> > > +static int aic3204_show_reg(struct snd_soc_codec *codec, char *buf,
> > > + int buf_sz, unsigned int reg)
> > > +{
> > > + return snprintf(buf, buf_sz, "%02x",
> > > + aic3204_read_reg_cache(codec, reg));
> > > +}

> > This looks suspiciously close to the standard show_reg() - it seems
> > wrong that you should need it.

> Well, the main difference; standard show_reg uses %4x as its format; and
> therefore wastes two characters printing nothing useful. Given space is
> already tight for displaying register dumps via debugfs, any little bit
> helps. :-)

> Certainly there's no explicit reason why it is necessary and it can go.

If you want to do this it'd be better to add support to the core, for
example by keying off a register width supplied by the CODEC.

> > > + /*
> > > + * These registers are not manipulated by us, so we must read them from
> > > + * the CODEC directly.
> > > + */

> > Hrm? Also, it strikes me that this code is also used in the WCLK
> > function and might benefit from being factored out - it's moderately
> > verbose.

> The ADC flags register (and the DAC flags registers) are read-only and
> manipulated by the CODEC when the ADCs and DACs are actually powered up.
> I could use the flags set elsewhere, but this reflects what *is* the
> present state, not what ought to be the present state.

This strikes me as something that's going to hit a race condition at
some point - if the two differ I'd hope that the state the driver has
set is going to be the actual CODEC state very soon.

> There is common code between the WCLK and BCLK functions; in fact I did
> consider making it the one callback, but since they are separate in the
> registers, I kept them separate in the callbacks.

I was thinking more a utility function that both callbacks use to work
out which of the DACs and ADCs are powered.

> Now that the mixer is working okay, I could cut this down... but also I
> hope that others who follow the same path as me, might find this helpful
> to determine what's going on.

If we're going to do that we'd need to be doing it for every single TLV
control in every single driver...

> Well, when the problem goes, so can the comment. :-) One question
> though, the shift value derives the mask for further operations on these
> registers... if we were to change the shift value so it reflected how
> other widget types work, how does one define the mask?

I'd intuitively expect it to be relative to the start of the controlled
data rather than the start of the register.

> > > + SOC_SINGLE("Differential Output Switch", AIC3204_DACS2,
> > > + AIC3204_DACS2_RMOD_INV_SHIFT, 1, 0),

> > This feels like platform data - the use of differential outputs is
> > normally a property of the physical wiring of the board, it's very rare
> > to be able to vary this usefully at runtime.

> Arguably, so is the DAC audio path routing. I wasn't sure how to set
> this up at the time, initially it was CODEC setup data, then I moved it
> into the mixer.

The DAC routing can be usefully varied at runtime depending on the use
case - for example, sometimes people choose to route a mono signal into
both DACs to give stereo output. It would be very unusual hardware that
provided a way of switching between single ended and differential in a
similar fashion.

> The other consideration here; is the use case where a device with a mono
> speaker, but a stereo line-out jack... I wasn't sure how to handle these
> features.

Presumably you can make individual outputs differential and single ended
then?

> > > + SOC_ENUM("MICBIAS Level", aic3204_enum_micbias_voltage),
> > > + SOC_ENUM("MICBIAS Source", aic3204_enum_micbias_src),

> > These I would expect to be managed in kernel - they're normally either a
> > property of the board or need to be managed in conjunction with jack
> > detection code which tends to live in kernel.

> I'll look into this... I was aware of the MICBIAS widget, but will have
> to do some further research here. As to the others... I wasn't sure how
> they should be handled.

Platform data initially then provide a runtime API for machine drivers
if requireed.

> > > +static const struct snd_soc_dapm_widget aic3204_dapm_widgets[] = {
> > > + /* Driver/Amplifier Selection */
> > > + SND_SOC_DAPM_SWITCH("HPL Playback Switch", AIC3204_OUTDRV,
> > > + AIC3204_OUTDRV_HPL_UP_SHIFT, 0,
> > > + &aic3204_hpl_switch),
> > > + SND_SOC_DAPM_SWITCH("HPR Playback Switch", AIC3204_OUTDRV,
> > > + AIC3204_OUTDRV_HPR_UP_SHIFT, 0,
> > > + &aic3204_hpr_switch),

> > A lot of these SWITCH controls feel like they ought to be PGAs and the
> > switch controls mutes on those. When muting an output you normally
> > don't want to power up and down the path since that is slow and tends to
> > trigger audible issues, you'd normally control the power for path
> > endpoints and elements that affect routing only.

> They are PGAs connecting the driver to its mixer... I wasn't sure how
> the PGAs worked in DAPM. I knew this did work however, the only catch

These should definitely be PGA widgets as described above.

> is that DAPM won't power up the DACs unless you switch them through via
> these mixers to one of the output drivers. I'll have a closer look at
> the PGAs and see what they're doing.

Not powering up the DACs unless there is a connected output path is the
expected behaviour.

> > It surprises me that the ordering matters too much here; worst case you
> > leave the interface declocked a little longer when you need to switch
> > sources but that doesn't seem like the end of the world?

> Well, what I found is that if I didn't do it there... it would call the
> WCLK and BCLK functions before powering up the DACs/ADCs. Since they
> have no idea whether recording or playback is in progress (I wasn't sure
> how to extract this information), the functions rely on the flags
> registers to determine this.

If you track this in the audio path setup/teardown path instead of
looking at the DAC power status you should find everything works fine.

> > > +static int aic3204_mute(struct snd_soc_dai *dai, int mute)
> > > +{

> > ...

> > > + aic3204_write(codec, AIC3204_DACS2, dacs2); /* Unmute DAC */

> > ...or possibly mute it :)

> Yes, obsolete comment I guess. :-) This is also a mixer control...
> which is better... leave it to ASoC core, or the user to control PCM
> mute? The user can mute using the line driver controls... so I guess I
> should ditch the "PCM Playback Switch" control.

Remove the mixer control. The automatic management ensures that no
noise that is generated when starting up the audio stream propagates
into the output path and that any soft unmute support in the CODEC gets
used to stop you having a hard power up with an active signal.

> > > + for (i = 0; i < AIC3204_CACHEREGNUM; i++) {
> > > + data[0] = i;
> > > + data[1] = cache[i];
> > > + codec->hw_write(codec->control_data, data, 2);

> > Since you've got a register defaults table you could skip rewriting
> > registers which have their default value.

> Indeed, suspend/resume isn't tested at all, so no idea if it works or
> not. I should probably do a forced reset too just to be on the safe
> side. This is a reminant from the aic3x driver.

The reset should not be required, either the device will be as you left
it or the power will have gone so it will be in the reset state anyway.

> > > + /* LDO enable, analogue blocks enable */
> > > + snd_soc_update_bits(codec, AIC3204_LDO,
> > > + AIC3204_LDO_ANALOG | AIC3204_LDO_AVDD_UP,
> > > + AIC3204_LDO_ANALOG_ENABLED |
> > > + (aic3204->pdata.ldo_en
> > > + ? AIC3204_LDO_AVDD_UP : 0));

> > This looks a bit fishy since the mask covers more bits than can ever be
> > enabled - it reads like the other two bits should be unconditionally
> > cleared.

> Would I be better breaking that statement up into two statements? I did

Yes, it would be much more legible.

> I wasn't sure about the link between AVDD and DVDD, so I left that
> configurable. I'm guessing most will probably want it disabled. This
> was configured using arbitrary register initialisation scripts, passed
> in via the CODEC setup data... so at least things are headded in the
> right direction. :-)

What does this register actually do? Does it describe the hardware
configuration to the device?
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/