linux/sound/pci/lola/lola_clock.c
<<
>>
Prefs
   1/*
   2 *  Support for Digigram Lola PCI-e boards
   3 *
   4 *  Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
   5 *
   6 *  This program is free software; you can redistribute it and/or modify it
   7 *  under the terms of the GNU General Public License as published by the Free
   8 *  Software Foundation; either version 2 of the License, or (at your option)
   9 *  any later version.
  10 *
  11 *  This program is distributed in the hope that it will be useful, but WITHOUT
  12 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 *  more details.
  15 *
  16 *  You should have received a copy of the GNU General Public License along with
  17 *  this program; if not, write to the Free Software Foundation, Inc., 59
  18 *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  19 */
  20
  21#include <linux/kernel.h>
  22#include <linux/init.h>
  23#include <linux/delay.h>
  24#include <sound/core.h>
  25#include <sound/pcm.h>
  26#include "lola.h"
  27
  28unsigned int lola_sample_rate_convert(unsigned int coded)
  29{
  30        unsigned int freq;
  31
  32        /* base frequency */
  33        switch (coded & 0x3) {
  34        case 0:     freq = 48000; break;
  35        case 1:     freq = 44100; break;
  36        case 2:     freq = 32000; break;
  37        default:    return 0;   /* error */
  38        }
  39
  40        /* multiplier / devisor */
  41        switch (coded & 0x1c) {
  42        case (0 << 2):    break;
  43        case (4 << 2):    break;
  44        case (1 << 2):    freq *= 2; break;
  45        case (2 << 2):    freq *= 4; break;
  46        case (5 << 2):    freq /= 2; break;
  47        case (6 << 2):    freq /= 4; break;
  48        default:        return 0;   /* error */
  49        }
  50
  51        /* ajustement */
  52        switch (coded & 0x60) {
  53        case (0 << 5):    break;
  54        case (1 << 5):    freq = (freq * 999) / 1000; break;
  55        case (2 << 5):    freq = (freq * 1001) / 1000; break;
  56        default:        return 0;   /* error */
  57        }
  58        return freq;
  59}
  60
  61/*
  62 * Granualrity
  63 */
  64
  65#define LOLA_MAXFREQ_AT_GRANULARITY_MIN         48000
  66#define LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX   96000
  67
  68static bool check_gran_clock_compatibility(struct lola *chip,
  69                                           unsigned int val,
  70                                           unsigned int freq)
  71{
  72        if (!chip->granularity)
  73                return true;
  74
  75        if (val < LOLA_GRANULARITY_MIN || val > LOLA_GRANULARITY_MAX ||
  76            (val % LOLA_GRANULARITY_STEP) != 0)
  77                return false;
  78
  79        if (val == LOLA_GRANULARITY_MIN) {
  80                if (freq > LOLA_MAXFREQ_AT_GRANULARITY_MIN)
  81                        return false;
  82        } else if (val < LOLA_GRANULARITY_MAX) {
  83                if (freq > LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX)
  84                        return false;
  85        }
  86        return true;
  87}
  88
  89int lola_set_granularity(struct lola *chip, unsigned int val, bool force)
  90{
  91        int err;
  92
  93        if (!force) {
  94                if (val == chip->granularity)
  95                        return 0;
  96#if 0
  97                /* change Gran only if there are no streams allocated ! */
  98                if (chip->audio_in_alloc_mask || chip->audio_out_alloc_mask)
  99                        return -EBUSY;
 100#endif
 101                if (!check_gran_clock_compatibility(chip, val,
 102                                                    chip->clock.cur_freq))
 103                        return -EINVAL;
 104        }
 105
 106        chip->granularity = val;
 107        val /= LOLA_GRANULARITY_STEP;
 108
 109        /* audio function group */
 110        err = lola_codec_write(chip, 1, LOLA_VERB_SET_GRANULARITY_STEPS,
 111                               val, 0);
 112        if (err < 0)
 113                return err;
 114        /* this can be a very slow function !!! */
 115        usleep_range(400 * val, 20000);
 116        return lola_codec_flush(chip);
 117}
 118
 119/*
 120 * Clock widget handling
 121 */
 122
 123int lola_init_clock_widget(struct lola *chip, int nid)
 124{
 125        unsigned int val;
 126        int i, j, nitems, nb_verbs, idx, idx_list;
 127        int err;
 128
 129        err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
 130        if (err < 0) {
 131                dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
 132                return err;
 133        }
 134
 135        if ((val & 0xfff00000) != 0x01f00000) { /* test SubType and Type */
 136                dev_dbg(chip->card->dev, "No valid clock widget\n");
 137                return 0;
 138        }
 139
 140        chip->clock.nid = nid;
 141        chip->clock.items = val & 0xff;
 142        dev_dbg(chip->card->dev, "clock_list nid=%x, entries=%d\n", nid,
 143                    chip->clock.items);
 144        if (chip->clock.items > MAX_SAMPLE_CLOCK_COUNT) {
 145                dev_err(chip->card->dev, "CLOCK_LIST too big: %d\n",
 146                       chip->clock.items);
 147                return -EINVAL;
 148        }
 149
 150        nitems = chip->clock.items;
 151        nb_verbs = (nitems + 3) / 4;
 152        idx = 0;
 153        idx_list = 0;
 154        for (i = 0; i < nb_verbs; i++) {
 155                unsigned int res_ex;
 156                unsigned short items[4];
 157
 158                err = lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
 159                                      idx, 0, &val, &res_ex);
 160                if (err < 0) {
 161                        dev_err(chip->card->dev, "Can't read CLOCK_LIST\n");
 162                        return -EINVAL;
 163                }
 164
 165                items[0] = val & 0xfff;
 166                items[1] = (val >> 16) & 0xfff;
 167                items[2] = res_ex & 0xfff;
 168                items[3] = (res_ex >> 16) & 0xfff;
 169
 170                for (j = 0; j < 4; j++) {
 171                        unsigned char type = items[j] >> 8;
 172                        unsigned int freq = items[j] & 0xff;
 173                        int format = LOLA_CLOCK_FORMAT_NONE;
 174                        bool add_clock = true;
 175                        if (type == LOLA_CLOCK_TYPE_INTERNAL) {
 176                                freq = lola_sample_rate_convert(freq);
 177                                if (freq < chip->sample_rate_min)
 178                                        add_clock = false;
 179                                else if (freq == 48000) {
 180                                        chip->clock.cur_index = idx_list;
 181                                        chip->clock.cur_freq = 48000;
 182                                        chip->clock.cur_valid = true;
 183                                }
 184                        } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
 185                                freq = lola_sample_rate_convert(freq);
 186                                if (freq < chip->sample_rate_min)
 187                                        add_clock = false;
 188                                /* video clock has a format (0:NTSC, 1:PAL)*/
 189                                if (items[j] & 0x80)
 190                                        format = LOLA_CLOCK_FORMAT_NTSC;
 191                                else
 192                                        format = LOLA_CLOCK_FORMAT_PAL;
 193                        }
 194                        if (add_clock) {
 195                                struct lola_sample_clock *sc;
 196                                sc = &chip->clock.sample_clock[idx_list];
 197                                sc->type = type;
 198                                sc->format = format;
 199                                sc->freq = freq;
 200                                /* keep the index used with the board */
 201                                chip->clock.idx_lookup[idx_list] = idx;
 202                                idx_list++;
 203                        } else {
 204                                chip->clock.items--;
 205                        }
 206                        if (++idx >= nitems)
 207                                break;
 208                }
 209        }
 210        return 0;
 211}
 212
 213/* enable unsolicited events of the clock widget */
 214int lola_enable_clock_events(struct lola *chip)
 215{
 216        unsigned int res;
 217        int err;
 218
 219        err = lola_codec_read(chip, chip->clock.nid,
 220                              LOLA_VERB_SET_UNSOLICITED_ENABLE,
 221                              LOLA_UNSOLICITED_ENABLE | LOLA_UNSOLICITED_TAG,
 222                              0, &res, NULL);
 223        if (err < 0)
 224                return err;
 225        if (res) {
 226                dev_warn(chip->card->dev, "error in enable_clock_events %d\n",
 227                       res);
 228                return -EINVAL;
 229        }
 230        return 0;
 231}
 232
 233int lola_set_clock_index(struct lola *chip, unsigned int idx)
 234{
 235        unsigned int res;
 236        int err;
 237
 238        err = lola_codec_read(chip, chip->clock.nid,
 239                              LOLA_VERB_SET_CLOCK_SELECT,
 240                              chip->clock.idx_lookup[idx],
 241                              0, &res, NULL);
 242        if (err < 0)
 243                return err;
 244        if (res) {
 245                dev_warn(chip->card->dev, "error in set_clock %d\n", res);
 246                return -EINVAL;
 247        }
 248        return 0;
 249}
 250
 251bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val)
 252{
 253        unsigned int tag;
 254
 255        /* the current EXTERNAL clock information gets updated by interrupt
 256         * with an unsolicited response
 257         */
 258        if (!val)
 259                return false;
 260        tag = (val >> LOLA_UNSOL_RESP_TAG_OFFSET) & LOLA_UNSOLICITED_TAG_MASK;
 261        if (tag != LOLA_UNSOLICITED_TAG)
 262                return false;
 263
 264        /* only for current = external clocks */
 265        if (chip->clock.sample_clock[chip->clock.cur_index].type !=
 266            LOLA_CLOCK_TYPE_INTERNAL) {
 267                chip->clock.cur_freq = lola_sample_rate_convert(val & 0x7f);
 268                chip->clock.cur_valid = (val & 0x100) != 0;
 269        }
 270        return true;
 271}
 272
 273int lola_set_clock(struct lola *chip, int idx)
 274{
 275        int freq = 0;
 276        bool valid = false;
 277
 278        if (idx == chip->clock.cur_index) {
 279                /* current clock is allowed */
 280                freq = chip->clock.cur_freq;
 281                valid = chip->clock.cur_valid;
 282        } else if (chip->clock.sample_clock[idx].type ==
 283                   LOLA_CLOCK_TYPE_INTERNAL) {
 284                /* internal clocks allowed */
 285                freq = chip->clock.sample_clock[idx].freq;
 286                valid = true;
 287        }
 288
 289        if (!freq || !valid)
 290                return -EINVAL;
 291
 292        if (!check_gran_clock_compatibility(chip, chip->granularity, freq))
 293                return -EINVAL;
 294
 295        if (idx != chip->clock.cur_index) {
 296                int err = lola_set_clock_index(chip, idx);
 297                if (err < 0)
 298                        return err;
 299                /* update new settings */
 300                chip->clock.cur_index = idx;
 301                chip->clock.cur_freq = freq;
 302                chip->clock.cur_valid = true;
 303        }
 304        return 0;
 305}
 306
 307int lola_set_sample_rate(struct lola *chip, int rate)
 308{
 309        int i;
 310
 311        if (chip->clock.cur_freq == rate && chip->clock.cur_valid)
 312                return 0;
 313        /* search for new dwClockIndex */
 314        for (i = 0; i < chip->clock.items; i++) {
 315                if (chip->clock.sample_clock[i].type == LOLA_CLOCK_TYPE_INTERNAL &&
 316                    chip->clock.sample_clock[i].freq == rate)
 317                        break;
 318        }
 319        if (i >= chip->clock.items)
 320                return -EINVAL;
 321        return lola_set_clock(chip, i);
 322}
 323
 324