162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for Digigram Lola PCI-e boards 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <sound/core.h> 1262306a36Sopenharmony_ci#include <sound/pcm.h> 1362306a36Sopenharmony_ci#include "lola.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciunsigned int lola_sample_rate_convert(unsigned int coded) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci unsigned int freq; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci /* base frequency */ 2062306a36Sopenharmony_ci switch (coded & 0x3) { 2162306a36Sopenharmony_ci case 0: freq = 48000; break; 2262306a36Sopenharmony_ci case 1: freq = 44100; break; 2362306a36Sopenharmony_ci case 2: freq = 32000; break; 2462306a36Sopenharmony_ci default: return 0; /* error */ 2562306a36Sopenharmony_ci } 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci /* multiplier / devisor */ 2862306a36Sopenharmony_ci switch (coded & 0x1c) { 2962306a36Sopenharmony_ci case (0 << 2): break; 3062306a36Sopenharmony_ci case (4 << 2): break; 3162306a36Sopenharmony_ci case (1 << 2): freq *= 2; break; 3262306a36Sopenharmony_ci case (2 << 2): freq *= 4; break; 3362306a36Sopenharmony_ci case (5 << 2): freq /= 2; break; 3462306a36Sopenharmony_ci case (6 << 2): freq /= 4; break; 3562306a36Sopenharmony_ci default: return 0; /* error */ 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* ajustement */ 3962306a36Sopenharmony_ci switch (coded & 0x60) { 4062306a36Sopenharmony_ci case (0 << 5): break; 4162306a36Sopenharmony_ci case (1 << 5): freq = (freq * 999) / 1000; break; 4262306a36Sopenharmony_ci case (2 << 5): freq = (freq * 1001) / 1000; break; 4362306a36Sopenharmony_ci default: return 0; /* error */ 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci return freq; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * Granualrity 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define LOLA_MAXFREQ_AT_GRANULARITY_MIN 48000 5362306a36Sopenharmony_ci#define LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX 96000 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic bool check_gran_clock_compatibility(struct lola *chip, 5662306a36Sopenharmony_ci unsigned int val, 5762306a36Sopenharmony_ci unsigned int freq) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci if (!chip->granularity) 6062306a36Sopenharmony_ci return true; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (val < LOLA_GRANULARITY_MIN || val > LOLA_GRANULARITY_MAX || 6362306a36Sopenharmony_ci (val % LOLA_GRANULARITY_STEP) != 0) 6462306a36Sopenharmony_ci return false; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (val == LOLA_GRANULARITY_MIN) { 6762306a36Sopenharmony_ci if (freq > LOLA_MAXFREQ_AT_GRANULARITY_MIN) 6862306a36Sopenharmony_ci return false; 6962306a36Sopenharmony_ci } else if (val < LOLA_GRANULARITY_MAX) { 7062306a36Sopenharmony_ci if (freq > LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX) 7162306a36Sopenharmony_ci return false; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci return true; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciint lola_set_granularity(struct lola *chip, unsigned int val, bool force) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci int err; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (!force) { 8162306a36Sopenharmony_ci if (val == chip->granularity) 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci#if 0 8462306a36Sopenharmony_ci /* change Gran only if there are no streams allocated ! */ 8562306a36Sopenharmony_ci if (chip->audio_in_alloc_mask || chip->audio_out_alloc_mask) 8662306a36Sopenharmony_ci return -EBUSY; 8762306a36Sopenharmony_ci#endif 8862306a36Sopenharmony_ci if (!check_gran_clock_compatibility(chip, val, 8962306a36Sopenharmony_ci chip->clock.cur_freq)) 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci chip->granularity = val; 9462306a36Sopenharmony_ci val /= LOLA_GRANULARITY_STEP; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* audio function group */ 9762306a36Sopenharmony_ci err = lola_codec_write(chip, 1, LOLA_VERB_SET_GRANULARITY_STEPS, 9862306a36Sopenharmony_ci val, 0); 9962306a36Sopenharmony_ci if (err < 0) 10062306a36Sopenharmony_ci return err; 10162306a36Sopenharmony_ci /* this can be a very slow function !!! */ 10262306a36Sopenharmony_ci usleep_range(400 * val, 20000); 10362306a36Sopenharmony_ci return lola_codec_flush(chip); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * Clock widget handling 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciint lola_init_clock_widget(struct lola *chip, int nid) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci unsigned int val; 11362306a36Sopenharmony_ci int i, j, nitems, nb_verbs, idx, idx_list; 11462306a36Sopenharmony_ci int err; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val); 11762306a36Sopenharmony_ci if (err < 0) { 11862306a36Sopenharmony_ci dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid); 11962306a36Sopenharmony_ci return err; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if ((val & 0xfff00000) != 0x01f00000) { /* test SubType and Type */ 12362306a36Sopenharmony_ci dev_dbg(chip->card->dev, "No valid clock widget\n"); 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci chip->clock.nid = nid; 12862306a36Sopenharmony_ci chip->clock.items = val & 0xff; 12962306a36Sopenharmony_ci dev_dbg(chip->card->dev, "clock_list nid=%x, entries=%d\n", nid, 13062306a36Sopenharmony_ci chip->clock.items); 13162306a36Sopenharmony_ci if (chip->clock.items > MAX_SAMPLE_CLOCK_COUNT) { 13262306a36Sopenharmony_ci dev_err(chip->card->dev, "CLOCK_LIST too big: %d\n", 13362306a36Sopenharmony_ci chip->clock.items); 13462306a36Sopenharmony_ci return -EINVAL; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci nitems = chip->clock.items; 13862306a36Sopenharmony_ci nb_verbs = DIV_ROUND_UP(nitems, 4); 13962306a36Sopenharmony_ci idx = 0; 14062306a36Sopenharmony_ci idx_list = 0; 14162306a36Sopenharmony_ci for (i = 0; i < nb_verbs; i++) { 14262306a36Sopenharmony_ci unsigned int res_ex; 14362306a36Sopenharmony_ci unsigned short items[4]; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci err = lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST, 14662306a36Sopenharmony_ci idx, 0, &val, &res_ex); 14762306a36Sopenharmony_ci if (err < 0) { 14862306a36Sopenharmony_ci dev_err(chip->card->dev, "Can't read CLOCK_LIST\n"); 14962306a36Sopenharmony_ci return -EINVAL; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci items[0] = val & 0xfff; 15362306a36Sopenharmony_ci items[1] = (val >> 16) & 0xfff; 15462306a36Sopenharmony_ci items[2] = res_ex & 0xfff; 15562306a36Sopenharmony_ci items[3] = (res_ex >> 16) & 0xfff; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci for (j = 0; j < 4; j++) { 15862306a36Sopenharmony_ci unsigned char type = items[j] >> 8; 15962306a36Sopenharmony_ci unsigned int freq = items[j] & 0xff; 16062306a36Sopenharmony_ci int format = LOLA_CLOCK_FORMAT_NONE; 16162306a36Sopenharmony_ci bool add_clock = true; 16262306a36Sopenharmony_ci if (type == LOLA_CLOCK_TYPE_INTERNAL) { 16362306a36Sopenharmony_ci freq = lola_sample_rate_convert(freq); 16462306a36Sopenharmony_ci if (freq < chip->sample_rate_min) 16562306a36Sopenharmony_ci add_clock = false; 16662306a36Sopenharmony_ci else if (freq == 48000) { 16762306a36Sopenharmony_ci chip->clock.cur_index = idx_list; 16862306a36Sopenharmony_ci chip->clock.cur_freq = 48000; 16962306a36Sopenharmony_ci chip->clock.cur_valid = true; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } else if (type == LOLA_CLOCK_TYPE_VIDEO) { 17262306a36Sopenharmony_ci freq = lola_sample_rate_convert(freq); 17362306a36Sopenharmony_ci if (freq < chip->sample_rate_min) 17462306a36Sopenharmony_ci add_clock = false; 17562306a36Sopenharmony_ci /* video clock has a format (0:NTSC, 1:PAL)*/ 17662306a36Sopenharmony_ci if (items[j] & 0x80) 17762306a36Sopenharmony_ci format = LOLA_CLOCK_FORMAT_NTSC; 17862306a36Sopenharmony_ci else 17962306a36Sopenharmony_ci format = LOLA_CLOCK_FORMAT_PAL; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci if (add_clock) { 18262306a36Sopenharmony_ci struct lola_sample_clock *sc; 18362306a36Sopenharmony_ci sc = &chip->clock.sample_clock[idx_list]; 18462306a36Sopenharmony_ci sc->type = type; 18562306a36Sopenharmony_ci sc->format = format; 18662306a36Sopenharmony_ci sc->freq = freq; 18762306a36Sopenharmony_ci /* keep the index used with the board */ 18862306a36Sopenharmony_ci chip->clock.idx_lookup[idx_list] = idx; 18962306a36Sopenharmony_ci idx_list++; 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci chip->clock.items--; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci if (++idx >= nitems) 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* enable unsolicited events of the clock widget */ 20162306a36Sopenharmony_ciint lola_enable_clock_events(struct lola *chip) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci unsigned int res; 20462306a36Sopenharmony_ci int err; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci err = lola_codec_read(chip, chip->clock.nid, 20762306a36Sopenharmony_ci LOLA_VERB_SET_UNSOLICITED_ENABLE, 20862306a36Sopenharmony_ci LOLA_UNSOLICITED_ENABLE | LOLA_UNSOLICITED_TAG, 20962306a36Sopenharmony_ci 0, &res, NULL); 21062306a36Sopenharmony_ci if (err < 0) 21162306a36Sopenharmony_ci return err; 21262306a36Sopenharmony_ci if (res) { 21362306a36Sopenharmony_ci dev_warn(chip->card->dev, "error in enable_clock_events %d\n", 21462306a36Sopenharmony_ci res); 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciint lola_set_clock_index(struct lola *chip, unsigned int idx) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci unsigned int res; 22362306a36Sopenharmony_ci int err; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci err = lola_codec_read(chip, chip->clock.nid, 22662306a36Sopenharmony_ci LOLA_VERB_SET_CLOCK_SELECT, 22762306a36Sopenharmony_ci chip->clock.idx_lookup[idx], 22862306a36Sopenharmony_ci 0, &res, NULL); 22962306a36Sopenharmony_ci if (err < 0) 23062306a36Sopenharmony_ci return err; 23162306a36Sopenharmony_ci if (res) { 23262306a36Sopenharmony_ci dev_warn(chip->card->dev, "error in set_clock %d\n", res); 23362306a36Sopenharmony_ci return -EINVAL; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cibool lola_update_ext_clock_freq(struct lola *chip, unsigned int val) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci unsigned int tag; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* the current EXTERNAL clock information gets updated by interrupt 24362306a36Sopenharmony_ci * with an unsolicited response 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci if (!val) 24662306a36Sopenharmony_ci return false; 24762306a36Sopenharmony_ci tag = (val >> LOLA_UNSOL_RESP_TAG_OFFSET) & LOLA_UNSOLICITED_TAG_MASK; 24862306a36Sopenharmony_ci if (tag != LOLA_UNSOLICITED_TAG) 24962306a36Sopenharmony_ci return false; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* only for current = external clocks */ 25262306a36Sopenharmony_ci if (chip->clock.sample_clock[chip->clock.cur_index].type != 25362306a36Sopenharmony_ci LOLA_CLOCK_TYPE_INTERNAL) { 25462306a36Sopenharmony_ci chip->clock.cur_freq = lola_sample_rate_convert(val & 0x7f); 25562306a36Sopenharmony_ci chip->clock.cur_valid = (val & 0x100) != 0; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci return true; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ciint lola_set_clock(struct lola *chip, int idx) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci int freq = 0; 26362306a36Sopenharmony_ci bool valid = false; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (idx == chip->clock.cur_index) { 26662306a36Sopenharmony_ci /* current clock is allowed */ 26762306a36Sopenharmony_ci freq = chip->clock.cur_freq; 26862306a36Sopenharmony_ci valid = chip->clock.cur_valid; 26962306a36Sopenharmony_ci } else if (chip->clock.sample_clock[idx].type == 27062306a36Sopenharmony_ci LOLA_CLOCK_TYPE_INTERNAL) { 27162306a36Sopenharmony_ci /* internal clocks allowed */ 27262306a36Sopenharmony_ci freq = chip->clock.sample_clock[idx].freq; 27362306a36Sopenharmony_ci valid = true; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (!freq || !valid) 27762306a36Sopenharmony_ci return -EINVAL; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (!check_gran_clock_compatibility(chip, chip->granularity, freq)) 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (idx != chip->clock.cur_index) { 28362306a36Sopenharmony_ci int err = lola_set_clock_index(chip, idx); 28462306a36Sopenharmony_ci if (err < 0) 28562306a36Sopenharmony_ci return err; 28662306a36Sopenharmony_ci /* update new settings */ 28762306a36Sopenharmony_ci chip->clock.cur_index = idx; 28862306a36Sopenharmony_ci chip->clock.cur_freq = freq; 28962306a36Sopenharmony_ci chip->clock.cur_valid = true; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ciint lola_set_sample_rate(struct lola *chip, int rate) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci int i; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (chip->clock.cur_freq == rate && chip->clock.cur_valid) 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci /* search for new dwClockIndex */ 30162306a36Sopenharmony_ci for (i = 0; i < chip->clock.items; i++) { 30262306a36Sopenharmony_ci if (chip->clock.sample_clock[i].type == LOLA_CLOCK_TYPE_INTERNAL && 30362306a36Sopenharmony_ci chip->clock.sample_clock[i].freq == rate) 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci if (i >= chip->clock.items) 30762306a36Sopenharmony_ci return -EINVAL; 30862306a36Sopenharmony_ci return lola_set_clock(chip, i); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 311