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