/* * STMicroelectronics SOC audio clocks wrapper * * Copyright (c) 2010-2011 STMicroelectronics Limited * * Authors: Pawel Moll * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #define COMPONENT clock #include "common.h" extern int snd_stm_debug_level; #define to_snd_stm_clk(clk) \ container_of(clk, struct snd_stm_clk, clk) struct snd_stm_clk_parent { struct list_head list; struct clk *parent; int children_count, ref_count; }; struct snd_stm_clk { struct clk clk; struct snd_stm_clk_parent *parent; unsigned long nominal_rate; int adjustment; /* Difference from the nominal rate, in ppm */ unsigned int enabled:1; snd_stm_magic_field; }; /* Adjustment ALSA control */ static int snd_stm_clk_adjustment_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = -999999; uinfo->value.integer.max = 1000000; return 0; } static int snd_stm_clk_adjustment_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_stm_clk *snd_stm_clk = snd_kcontrol_chip(kcontrol); snd_stm_printd(1, "%s(kcontrol=0x%p, ucontrol=0x%p)\n", __func__, kcontrol, ucontrol); BUG_ON(!snd_stm_clk); BUG_ON(!snd_stm_magic_valid(snd_stm_clk)); ucontrol->value.integer.value[0] = snd_stm_clk->adjustment; return 0; } static int snd_stm_clk_adjustment_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_stm_clk *snd_stm_clk = snd_kcontrol_chip(kcontrol); int old_adjustement; snd_stm_printd(1, "%s(kcontrol=0x%p, ucontrol=0x%p)\n", __func__, kcontrol, ucontrol); BUG_ON(!snd_stm_clk); BUG_ON(!snd_stm_magic_valid(snd_stm_clk)); BUG_ON(ucontrol->value.integer.value[0] < -999999); BUG_ON(ucontrol->value.integer.value[0] > 1000000); old_adjustement = snd_stm_clk->adjustment; snd_stm_clk->adjustment = ucontrol->value.integer.value[0]; if (snd_stm_clk->enabled && clk_set_rate(&snd_stm_clk->clk, snd_stm_clk->nominal_rate) < 0) return -EINVAL; return old_adjustement != snd_stm_clk->adjustment; } static struct snd_kcontrol_new snd_stm_clk_adjustment_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "PCM Playback Oversampling Freq. Adjustment", .info = snd_stm_clk_adjustment_info, .get = snd_stm_clk_adjustment_get, .put = snd_stm_clk_adjustment_put, }; /* Clock interface */ static LIST_HEAD(snd_stm_clk_parents); static DEFINE_SPINLOCK(snd_stm_clk_parents_lock); int snd_stm_clk_enable(struct clk *clk) { int result = -EINVAL; struct snd_stm_clk *snd_stm_clk; snd_stm_printd(1, "%s(clk=%p)\n", __func__, clk); BUG_ON(!clk); snd_stm_clk = to_snd_stm_clk(clk); BUG_ON(!snd_stm_magic_valid(snd_stm_clk)); spin_lock(&snd_stm_clk_parents_lock); if (snd_stm_clk->parent->ref_count++ == 0) { result = clk_enable(clk->parent); if (result == 0) snd_stm_clk->enabled = 1; else snd_stm_clk->parent->ref_count--; } BUG_ON(snd_stm_clk->parent->ref_count > snd_stm_clk->parent->children_count); spin_unlock(&snd_stm_clk_parents_lock); return result; } int snd_stm_clk_disable(struct clk *clk) { struct snd_stm_clk *snd_stm_clk; snd_stm_printd(1, "%s(clk=%p)\n", __func__, clk); BUG_ON(!clk); snd_stm_clk = to_snd_stm_clk(clk); BUG_ON(!snd_stm_magic_valid(snd_stm_clk)); spin_lock(&snd_stm_clk_parents_lock); if (--snd_stm_clk->parent->ref_count == 0) { clk_disable(clk->parent); snd_stm_clk->enabled = 0; } BUG_ON(snd_stm_clk->parent->ref_count < 0); spin_unlock(&snd_stm_clk_parents_lock); return 0; } int snd_stm_clk_set_rate(struct clk *clk, unsigned long rate) { struct snd_stm_clk *snd_stm_clk; int rate_adjusted, rate_achieved; int delta; snd_stm_printd(1, "%s(clk=%p, rate=%lu)\n", __func__, clk, rate); BUG_ON(!clk); snd_stm_clk = to_snd_stm_clk(clk); BUG_ON(!snd_stm_magic_valid(snd_stm_clk)); /* User must enable the clock first */ if (!snd_stm_clk->enabled) return -EAGAIN; /* a * F = f + --------- * f = f + d * 1000000 * * a * d = --------- * f * 1000000 * * where: * f - nominal rate * a - adjustment in ppm (parts per milion) * F - rate to be set in synthesizer * d - delta (difference) between f and F */ if (snd_stm_clk->adjustment < 0) { /* div64_64 operates on unsigned values... */ delta = -1; snd_stm_clk->adjustment = -snd_stm_clk->adjustment; } else { delta = 1; } /* 500000 ppm is 0.5, which is used to round up values */ delta *= (int)div64_u64((uint64_t)rate * (uint64_t)snd_stm_clk->adjustment + 500000, 1000000); rate_adjusted = rate + delta; snd_stm_printd(1, "Setting clock '%s' to rate %d.\n", clk->parent->name, rate_adjusted); /* adjusted rate should never be == 0 */ BUG_ON(rate_adjusted == 0); if (clk_set_rate(snd_stm_clk->clk.parent, rate_adjusted) < 0) { snd_stm_printe("Failed to set rate %d on clock '%s'!\n", rate_adjusted, clk->parent->name); return -EINVAL; } rate_achieved = clk_get_rate(clk->parent); snd_stm_printd(1, "Achieved rate %d.\n", rate_achieved); /* using ALSA's adjustment control, we can modify the rate to be up to twice as much as requested, but no more */ BUG_ON(rate_achieved > 2*rate); delta = rate_achieved - rate; if (delta < 0) { /* div64_64 operates on unsigned values... */ delta = -delta; snd_stm_clk->adjustment = -1; } else { snd_stm_clk->adjustment = 1; } /* frequency/2 is added to round up result */ snd_stm_clk->adjustment *= (int)div64_u64((uint64_t)delta * 1000000 + rate / 2, rate); snd_stm_clk->nominal_rate = rate; clk->rate = rate_achieved; return 0; } static struct clk_ops snd_stm_clk_ops = { .enable = snd_stm_clk_enable, .disable = snd_stm_clk_disable, .set_rate = snd_stm_clk_set_rate, }; struct clk *snd_stm_clk_get(struct device *dev, const char *id, struct snd_card *card, int card_device) { struct snd_stm_clk *snd_stm_clk; struct snd_stm_clk_parent *snd_stm_clk_parent; snd_stm_printd(0, "%s(dev=%p('%s'), id='%s')\n", __func__, dev, dev_name(dev), id); snd_stm_clk = kzalloc(sizeof(*snd_stm_clk), GFP_KERNEL); if (!snd_stm_clk) { snd_stm_printe("No memory for '%s'('%s') clock!\n", dev_name(dev), id); goto error_kzalloc_clk; } snd_stm_magic_set(snd_stm_clk); snd_stm_clk->clk.ops = &snd_stm_clk_ops; snd_stm_clk->clk.name = dev_name(dev); /* Get the parent clock */ snd_stm_clk->clk.parent = clk_get(dev, id); if (!snd_stm_clk->clk.parent || IS_ERR(snd_stm_clk->clk.parent)) { snd_stm_printe("Can't get '%s' clock ('%s's parent)!\n", id, dev_name(dev)); goto error_clk_get; } /* Find the parent clock description... */ spin_lock(&snd_stm_clk_parents_lock); list_for_each_entry(snd_stm_clk_parent, &snd_stm_clk_parents, list) { if (snd_stm_clk_parent->parent == snd_stm_clk->clk.parent) { snd_stm_clk->parent = snd_stm_clk_parent; snd_stm_clk_parent->children_count++; break; } } spin_unlock(&snd_stm_clk_parents_lock); /* ... or allocate it now */ if (!snd_stm_clk->parent) { snd_stm_clk->parent = kzalloc(sizeof(*snd_stm_clk->parent), GFP_KERNEL); if (!snd_stm_clk->parent) { snd_stm_printe("No memory for '%s'('%s') parent!\n", dev_name(dev), id); goto error_kzalloc_parent; } snd_stm_clk->parent->parent = snd_stm_clk->clk.parent; snd_stm_clk->parent->children_count = 1; spin_lock(&snd_stm_clk_parents_lock); list_add_tail(&snd_stm_clk->parent->list, &snd_stm_clk_parents); spin_unlock(&snd_stm_clk_parents_lock); } /* Register the clock "wrapper" */ if (clk_register(&snd_stm_clk->clk) < 0) { snd_stm_printe("Failed to register '%s'('%s')\n", dev_name(dev), id); goto error_clk_register; } /* Rate adjustment ALSA control */ snd_stm_clk_adjustment_ctl.device = card_device; if (snd_ctl_add(card, snd_ctl_new1(&snd_stm_clk_adjustment_ctl, snd_stm_clk)) < 0) { snd_stm_printe("Failed to add '%s'('%s') clock ALSA control!\n", dev_name(dev), id); goto error_snd_ctl_add; } snd_stm_clk_adjustment_ctl.index++; return &snd_stm_clk->clk; error_snd_ctl_add: clk_unregister(&snd_stm_clk->clk); error_clk_register: spin_lock(&snd_stm_clk_parents_lock); if (--snd_stm_clk->parent->children_count == 0) kfree(snd_stm_clk->parent); spin_unlock(&snd_stm_clk_parents_lock); error_kzalloc_parent: clk_put(snd_stm_clk->clk.parent); error_clk_get: snd_stm_magic_clear(snd_stm_clk); kfree(snd_stm_clk); error_kzalloc_clk: return NULL; } void snd_stm_clk_put(struct clk *clk) { struct snd_stm_clk *snd_stm_clk; snd_stm_printd(0, "%s(clk=%p)\n", __func__, clk); BUG_ON(!clk); snd_stm_clk = to_snd_stm_clk(clk); BUG_ON(!snd_stm_magic_valid(snd_stm_clk)); clk_unregister(clk); spin_lock(&snd_stm_clk_parents_lock); if (--snd_stm_clk->parent->children_count == 0) kfree(snd_stm_clk->parent); spin_unlock(&snd_stm_clk_parents_lock); clk_put(clk->parent); snd_stm_magic_clear(snd_stm_clk); kfree(snd_stm_clk); }