18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for Digigram Lola PCI-e boards 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <sound/core.h> 128c2ecf20Sopenharmony_ci#include <sound/pcm.h> 138c2ecf20Sopenharmony_ci#include "lola.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciunsigned int lola_sample_rate_convert(unsigned int coded) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci unsigned int freq; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci /* base frequency */ 208c2ecf20Sopenharmony_ci switch (coded & 0x3) { 218c2ecf20Sopenharmony_ci case 0: freq = 48000; break; 228c2ecf20Sopenharmony_ci case 1: freq = 44100; break; 238c2ecf20Sopenharmony_ci case 2: freq = 32000; break; 248c2ecf20Sopenharmony_ci default: return 0; /* error */ 258c2ecf20Sopenharmony_ci } 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci /* multiplier / devisor */ 288c2ecf20Sopenharmony_ci switch (coded & 0x1c) { 298c2ecf20Sopenharmony_ci case (0 << 2): break; 308c2ecf20Sopenharmony_ci case (4 << 2): break; 318c2ecf20Sopenharmony_ci case (1 << 2): freq *= 2; break; 328c2ecf20Sopenharmony_ci case (2 << 2): freq *= 4; break; 338c2ecf20Sopenharmony_ci case (5 << 2): freq /= 2; break; 348c2ecf20Sopenharmony_ci case (6 << 2): freq /= 4; break; 358c2ecf20Sopenharmony_ci default: return 0; /* error */ 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* ajustement */ 398c2ecf20Sopenharmony_ci switch (coded & 0x60) { 408c2ecf20Sopenharmony_ci case (0 << 5): break; 418c2ecf20Sopenharmony_ci case (1 << 5): freq = (freq * 999) / 1000; break; 428c2ecf20Sopenharmony_ci case (2 << 5): freq = (freq * 1001) / 1000; break; 438c2ecf20Sopenharmony_ci default: return 0; /* error */ 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci return freq; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * Granualrity 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define LOLA_MAXFREQ_AT_GRANULARITY_MIN 48000 538c2ecf20Sopenharmony_ci#define LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX 96000 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic bool check_gran_clock_compatibility(struct lola *chip, 568c2ecf20Sopenharmony_ci unsigned int val, 578c2ecf20Sopenharmony_ci unsigned int freq) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci if (!chip->granularity) 608c2ecf20Sopenharmony_ci return true; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (val < LOLA_GRANULARITY_MIN || val > LOLA_GRANULARITY_MAX || 638c2ecf20Sopenharmony_ci (val % LOLA_GRANULARITY_STEP) != 0) 648c2ecf20Sopenharmony_ci return false; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (val == LOLA_GRANULARITY_MIN) { 678c2ecf20Sopenharmony_ci if (freq > LOLA_MAXFREQ_AT_GRANULARITY_MIN) 688c2ecf20Sopenharmony_ci return false; 698c2ecf20Sopenharmony_ci } else if (val < LOLA_GRANULARITY_MAX) { 708c2ecf20Sopenharmony_ci if (freq > LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX) 718c2ecf20Sopenharmony_ci return false; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci return true; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciint lola_set_granularity(struct lola *chip, unsigned int val, bool force) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci int err; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (!force) { 818c2ecf20Sopenharmony_ci if (val == chip->granularity) 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci#if 0 848c2ecf20Sopenharmony_ci /* change Gran only if there are no streams allocated ! */ 858c2ecf20Sopenharmony_ci if (chip->audio_in_alloc_mask || chip->audio_out_alloc_mask) 868c2ecf20Sopenharmony_ci return -EBUSY; 878c2ecf20Sopenharmony_ci#endif 888c2ecf20Sopenharmony_ci if (!check_gran_clock_compatibility(chip, val, 898c2ecf20Sopenharmony_ci chip->clock.cur_freq)) 908c2ecf20Sopenharmony_ci return -EINVAL; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci chip->granularity = val; 948c2ecf20Sopenharmony_ci val /= LOLA_GRANULARITY_STEP; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* audio function group */ 978c2ecf20Sopenharmony_ci err = lola_codec_write(chip, 1, LOLA_VERB_SET_GRANULARITY_STEPS, 988c2ecf20Sopenharmony_ci val, 0); 998c2ecf20Sopenharmony_ci if (err < 0) 1008c2ecf20Sopenharmony_ci return err; 1018c2ecf20Sopenharmony_ci /* this can be a very slow function !!! */ 1028c2ecf20Sopenharmony_ci usleep_range(400 * val, 20000); 1038c2ecf20Sopenharmony_ci return lola_codec_flush(chip); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci * Clock widget handling 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciint lola_init_clock_widget(struct lola *chip, int nid) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci unsigned int val; 1138c2ecf20Sopenharmony_ci int i, j, nitems, nb_verbs, idx, idx_list; 1148c2ecf20Sopenharmony_ci int err; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val); 1178c2ecf20Sopenharmony_ci if (err < 0) { 1188c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid); 1198c2ecf20Sopenharmony_ci return err; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if ((val & 0xfff00000) != 0x01f00000) { /* test SubType and Type */ 1238c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "No valid clock widget\n"); 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci chip->clock.nid = nid; 1288c2ecf20Sopenharmony_ci chip->clock.items = val & 0xff; 1298c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "clock_list nid=%x, entries=%d\n", nid, 1308c2ecf20Sopenharmony_ci chip->clock.items); 1318c2ecf20Sopenharmony_ci if (chip->clock.items > MAX_SAMPLE_CLOCK_COUNT) { 1328c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "CLOCK_LIST too big: %d\n", 1338c2ecf20Sopenharmony_ci chip->clock.items); 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci nitems = chip->clock.items; 1388c2ecf20Sopenharmony_ci nb_verbs = (nitems + 3) / 4; 1398c2ecf20Sopenharmony_ci idx = 0; 1408c2ecf20Sopenharmony_ci idx_list = 0; 1418c2ecf20Sopenharmony_ci for (i = 0; i < nb_verbs; i++) { 1428c2ecf20Sopenharmony_ci unsigned int res_ex; 1438c2ecf20Sopenharmony_ci unsigned short items[4]; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci err = lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST, 1468c2ecf20Sopenharmony_ci idx, 0, &val, &res_ex); 1478c2ecf20Sopenharmony_ci if (err < 0) { 1488c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "Can't read CLOCK_LIST\n"); 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci items[0] = val & 0xfff; 1538c2ecf20Sopenharmony_ci items[1] = (val >> 16) & 0xfff; 1548c2ecf20Sopenharmony_ci items[2] = res_ex & 0xfff; 1558c2ecf20Sopenharmony_ci items[3] = (res_ex >> 16) & 0xfff; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (j = 0; j < 4; j++) { 1588c2ecf20Sopenharmony_ci unsigned char type = items[j] >> 8; 1598c2ecf20Sopenharmony_ci unsigned int freq = items[j] & 0xff; 1608c2ecf20Sopenharmony_ci int format = LOLA_CLOCK_FORMAT_NONE; 1618c2ecf20Sopenharmony_ci bool add_clock = true; 1628c2ecf20Sopenharmony_ci if (type == LOLA_CLOCK_TYPE_INTERNAL) { 1638c2ecf20Sopenharmony_ci freq = lola_sample_rate_convert(freq); 1648c2ecf20Sopenharmony_ci if (freq < chip->sample_rate_min) 1658c2ecf20Sopenharmony_ci add_clock = false; 1668c2ecf20Sopenharmony_ci else if (freq == 48000) { 1678c2ecf20Sopenharmony_ci chip->clock.cur_index = idx_list; 1688c2ecf20Sopenharmony_ci chip->clock.cur_freq = 48000; 1698c2ecf20Sopenharmony_ci chip->clock.cur_valid = true; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci } else if (type == LOLA_CLOCK_TYPE_VIDEO) { 1728c2ecf20Sopenharmony_ci freq = lola_sample_rate_convert(freq); 1738c2ecf20Sopenharmony_ci if (freq < chip->sample_rate_min) 1748c2ecf20Sopenharmony_ci add_clock = false; 1758c2ecf20Sopenharmony_ci /* video clock has a format (0:NTSC, 1:PAL)*/ 1768c2ecf20Sopenharmony_ci if (items[j] & 0x80) 1778c2ecf20Sopenharmony_ci format = LOLA_CLOCK_FORMAT_NTSC; 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci format = LOLA_CLOCK_FORMAT_PAL; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci if (add_clock) { 1828c2ecf20Sopenharmony_ci struct lola_sample_clock *sc; 1838c2ecf20Sopenharmony_ci sc = &chip->clock.sample_clock[idx_list]; 1848c2ecf20Sopenharmony_ci sc->type = type; 1858c2ecf20Sopenharmony_ci sc->format = format; 1868c2ecf20Sopenharmony_ci sc->freq = freq; 1878c2ecf20Sopenharmony_ci /* keep the index used with the board */ 1888c2ecf20Sopenharmony_ci chip->clock.idx_lookup[idx_list] = idx; 1898c2ecf20Sopenharmony_ci idx_list++; 1908c2ecf20Sopenharmony_ci } else { 1918c2ecf20Sopenharmony_ci chip->clock.items--; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci if (++idx >= nitems) 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* enable unsolicited events of the clock widget */ 2018c2ecf20Sopenharmony_ciint lola_enable_clock_events(struct lola *chip) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci unsigned int res; 2048c2ecf20Sopenharmony_ci int err; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci err = lola_codec_read(chip, chip->clock.nid, 2078c2ecf20Sopenharmony_ci LOLA_VERB_SET_UNSOLICITED_ENABLE, 2088c2ecf20Sopenharmony_ci LOLA_UNSOLICITED_ENABLE | LOLA_UNSOLICITED_TAG, 2098c2ecf20Sopenharmony_ci 0, &res, NULL); 2108c2ecf20Sopenharmony_ci if (err < 0) 2118c2ecf20Sopenharmony_ci return err; 2128c2ecf20Sopenharmony_ci if (res) { 2138c2ecf20Sopenharmony_ci dev_warn(chip->card->dev, "error in enable_clock_events %d\n", 2148c2ecf20Sopenharmony_ci res); 2158c2ecf20Sopenharmony_ci return -EINVAL; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ciint lola_set_clock_index(struct lola *chip, unsigned int idx) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci unsigned int res; 2238c2ecf20Sopenharmony_ci int err; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci err = lola_codec_read(chip, chip->clock.nid, 2268c2ecf20Sopenharmony_ci LOLA_VERB_SET_CLOCK_SELECT, 2278c2ecf20Sopenharmony_ci chip->clock.idx_lookup[idx], 2288c2ecf20Sopenharmony_ci 0, &res, NULL); 2298c2ecf20Sopenharmony_ci if (err < 0) 2308c2ecf20Sopenharmony_ci return err; 2318c2ecf20Sopenharmony_ci if (res) { 2328c2ecf20Sopenharmony_ci dev_warn(chip->card->dev, "error in set_clock %d\n", res); 2338c2ecf20Sopenharmony_ci return -EINVAL; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cibool lola_update_ext_clock_freq(struct lola *chip, unsigned int val) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci unsigned int tag; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* the current EXTERNAL clock information gets updated by interrupt 2438c2ecf20Sopenharmony_ci * with an unsolicited response 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci if (!val) 2468c2ecf20Sopenharmony_ci return false; 2478c2ecf20Sopenharmony_ci tag = (val >> LOLA_UNSOL_RESP_TAG_OFFSET) & LOLA_UNSOLICITED_TAG_MASK; 2488c2ecf20Sopenharmony_ci if (tag != LOLA_UNSOLICITED_TAG) 2498c2ecf20Sopenharmony_ci return false; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* only for current = external clocks */ 2528c2ecf20Sopenharmony_ci if (chip->clock.sample_clock[chip->clock.cur_index].type != 2538c2ecf20Sopenharmony_ci LOLA_CLOCK_TYPE_INTERNAL) { 2548c2ecf20Sopenharmony_ci chip->clock.cur_freq = lola_sample_rate_convert(val & 0x7f); 2558c2ecf20Sopenharmony_ci chip->clock.cur_valid = (val & 0x100) != 0; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci return true; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ciint lola_set_clock(struct lola *chip, int idx) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci int freq = 0; 2638c2ecf20Sopenharmony_ci bool valid = false; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (idx == chip->clock.cur_index) { 2668c2ecf20Sopenharmony_ci /* current clock is allowed */ 2678c2ecf20Sopenharmony_ci freq = chip->clock.cur_freq; 2688c2ecf20Sopenharmony_ci valid = chip->clock.cur_valid; 2698c2ecf20Sopenharmony_ci } else if (chip->clock.sample_clock[idx].type == 2708c2ecf20Sopenharmony_ci LOLA_CLOCK_TYPE_INTERNAL) { 2718c2ecf20Sopenharmony_ci /* internal clocks allowed */ 2728c2ecf20Sopenharmony_ci freq = chip->clock.sample_clock[idx].freq; 2738c2ecf20Sopenharmony_ci valid = true; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (!freq || !valid) 2778c2ecf20Sopenharmony_ci return -EINVAL; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (!check_gran_clock_compatibility(chip, chip->granularity, freq)) 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (idx != chip->clock.cur_index) { 2838c2ecf20Sopenharmony_ci int err = lola_set_clock_index(chip, idx); 2848c2ecf20Sopenharmony_ci if (err < 0) 2858c2ecf20Sopenharmony_ci return err; 2868c2ecf20Sopenharmony_ci /* update new settings */ 2878c2ecf20Sopenharmony_ci chip->clock.cur_index = idx; 2888c2ecf20Sopenharmony_ci chip->clock.cur_freq = freq; 2898c2ecf20Sopenharmony_ci chip->clock.cur_valid = true; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciint lola_set_sample_rate(struct lola *chip, int rate) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci int i; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (chip->clock.cur_freq == rate && chip->clock.cur_valid) 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci /* search for new dwClockIndex */ 3018c2ecf20Sopenharmony_ci for (i = 0; i < chip->clock.items; i++) { 3028c2ecf20Sopenharmony_ci if (chip->clock.sample_clock[i].type == LOLA_CLOCK_TYPE_INTERNAL && 3038c2ecf20Sopenharmony_ci chip->clock.sample_clock[i].freq == rate) 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci if (i >= chip->clock.items) 3078c2ecf20Sopenharmony_ci return -EINVAL; 3088c2ecf20Sopenharmony_ci return lola_set_clock(chip, i); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 311