18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Analog Devices ADV7511 HDMI transmitter driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2012 Analog Devices Inc. 68c2ecf20Sopenharmony_ci * Copyright (c) 2016, Linaro Limited 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <sound/core.h> 108c2ecf20Sopenharmony_ci#include <sound/hdmi-codec.h> 118c2ecf20Sopenharmony_ci#include <sound/pcm.h> 128c2ecf20Sopenharmony_ci#include <sound/soc.h> 138c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "adv7511.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic void adv7511_calc_cts_n(unsigned int f_tmds, unsigned int fs, 188c2ecf20Sopenharmony_ci unsigned int *cts, unsigned int *n) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci switch (fs) { 218c2ecf20Sopenharmony_ci case 32000: 228c2ecf20Sopenharmony_ci case 48000: 238c2ecf20Sopenharmony_ci case 96000: 248c2ecf20Sopenharmony_ci case 192000: 258c2ecf20Sopenharmony_ci *n = fs * 128 / 1000; 268c2ecf20Sopenharmony_ci break; 278c2ecf20Sopenharmony_ci case 44100: 288c2ecf20Sopenharmony_ci case 88200: 298c2ecf20Sopenharmony_ci case 176400: 308c2ecf20Sopenharmony_ci *n = fs * 128 / 900; 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci *cts = ((f_tmds * *n) / (128 * fs)) * 1000; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int adv7511_update_cts_n(struct adv7511 *adv7511) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci unsigned int cts = 0; 408c2ecf20Sopenharmony_ci unsigned int n = 0; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci adv7511_calc_cts_n(adv7511->f_tmds, adv7511->f_audio, &cts, &n); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci regmap_write(adv7511->regmap, ADV7511_REG_N0, (n >> 16) & 0xf); 458c2ecf20Sopenharmony_ci regmap_write(adv7511->regmap, ADV7511_REG_N1, (n >> 8) & 0xff); 468c2ecf20Sopenharmony_ci regmap_write(adv7511->regmap, ADV7511_REG_N2, n & 0xff); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL0, 498c2ecf20Sopenharmony_ci (cts >> 16) & 0xf); 508c2ecf20Sopenharmony_ci regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL1, 518c2ecf20Sopenharmony_ci (cts >> 8) & 0xff); 528c2ecf20Sopenharmony_ci regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL2, 538c2ecf20Sopenharmony_ci cts & 0xff); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ciint adv7511_hdmi_hw_params(struct device *dev, void *data, 598c2ecf20Sopenharmony_ci struct hdmi_codec_daifmt *fmt, 608c2ecf20Sopenharmony_ci struct hdmi_codec_params *hparms) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct adv7511 *adv7511 = dev_get_drvdata(dev); 638c2ecf20Sopenharmony_ci unsigned int audio_source, i2s_format = 0; 648c2ecf20Sopenharmony_ci unsigned int invert_clock; 658c2ecf20Sopenharmony_ci unsigned int rate; 668c2ecf20Sopenharmony_ci unsigned int len; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci switch (hparms->sample_rate) { 698c2ecf20Sopenharmony_ci case 32000: 708c2ecf20Sopenharmony_ci rate = ADV7511_SAMPLE_FREQ_32000; 718c2ecf20Sopenharmony_ci break; 728c2ecf20Sopenharmony_ci case 44100: 738c2ecf20Sopenharmony_ci rate = ADV7511_SAMPLE_FREQ_44100; 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci case 48000: 768c2ecf20Sopenharmony_ci rate = ADV7511_SAMPLE_FREQ_48000; 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci case 88200: 798c2ecf20Sopenharmony_ci rate = ADV7511_SAMPLE_FREQ_88200; 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci case 96000: 828c2ecf20Sopenharmony_ci rate = ADV7511_SAMPLE_FREQ_96000; 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci case 176400: 858c2ecf20Sopenharmony_ci rate = ADV7511_SAMPLE_FREQ_176400; 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci case 192000: 888c2ecf20Sopenharmony_ci rate = ADV7511_SAMPLE_FREQ_192000; 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci default: 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci switch (hparms->sample_width) { 958c2ecf20Sopenharmony_ci case 16: 968c2ecf20Sopenharmony_ci len = ADV7511_I2S_SAMPLE_LEN_16; 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci case 18: 998c2ecf20Sopenharmony_ci len = ADV7511_I2S_SAMPLE_LEN_18; 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci case 20: 1028c2ecf20Sopenharmony_ci len = ADV7511_I2S_SAMPLE_LEN_20; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci case 24: 1058c2ecf20Sopenharmony_ci len = ADV7511_I2S_SAMPLE_LEN_24; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci default: 1088c2ecf20Sopenharmony_ci return -EINVAL; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci switch (fmt->fmt) { 1128c2ecf20Sopenharmony_ci case HDMI_I2S: 1138c2ecf20Sopenharmony_ci audio_source = ADV7511_AUDIO_SOURCE_I2S; 1148c2ecf20Sopenharmony_ci i2s_format = ADV7511_I2S_FORMAT_I2S; 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci case HDMI_RIGHT_J: 1178c2ecf20Sopenharmony_ci audio_source = ADV7511_AUDIO_SOURCE_I2S; 1188c2ecf20Sopenharmony_ci i2s_format = ADV7511_I2S_FORMAT_RIGHT_J; 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci case HDMI_LEFT_J: 1218c2ecf20Sopenharmony_ci audio_source = ADV7511_AUDIO_SOURCE_I2S; 1228c2ecf20Sopenharmony_ci i2s_format = ADV7511_I2S_FORMAT_LEFT_J; 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci case HDMI_SPDIF: 1258c2ecf20Sopenharmony_ci audio_source = ADV7511_AUDIO_SOURCE_SPDIF; 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci default: 1288c2ecf20Sopenharmony_ci return -EINVAL; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci invert_clock = fmt->bit_clk_inv; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_SOURCE, 0x70, 1348c2ecf20Sopenharmony_ci audio_source << 4); 1358c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(6), 1368c2ecf20Sopenharmony_ci invert_clock << 6); 1378c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_I2S_CONFIG, 0x03, 1388c2ecf20Sopenharmony_ci i2s_format); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci adv7511->audio_source = audio_source; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci adv7511->f_audio = hparms->sample_rate; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci adv7511_update_cts_n(adv7511); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG3, 1478c2ecf20Sopenharmony_ci ADV7511_AUDIO_CFG3_LEN_MASK, len); 1488c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG, 1498c2ecf20Sopenharmony_ci ADV7511_I2C_FREQ_ID_CFG_RATE_MASK, rate << 4); 1508c2ecf20Sopenharmony_ci regmap_write(adv7511->regmap, 0x73, 0x1); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int audio_startup(struct device *dev, void *data) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct adv7511 *adv7511 = dev_get_drvdata(dev); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, 1608c2ecf20Sopenharmony_ci BIT(7), 0); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* hide Audio infoframe updates */ 1638c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE, 1648c2ecf20Sopenharmony_ci BIT(5), BIT(5)); 1658c2ecf20Sopenharmony_ci /* enable N/CTS, enable Audio sample packets */ 1668c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1, 1678c2ecf20Sopenharmony_ci BIT(5), BIT(5)); 1688c2ecf20Sopenharmony_ci /* enable N/CTS */ 1698c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1, 1708c2ecf20Sopenharmony_ci BIT(6), BIT(6)); 1718c2ecf20Sopenharmony_ci /* not copyrighted */ 1728c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG1, 1738c2ecf20Sopenharmony_ci BIT(5), BIT(5)); 1748c2ecf20Sopenharmony_ci /* enable audio infoframes */ 1758c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1, 1768c2ecf20Sopenharmony_ci BIT(3), BIT(3)); 1778c2ecf20Sopenharmony_ci /* AV mute disable */ 1788c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(0), 1798c2ecf20Sopenharmony_ci BIT(7) | BIT(6), BIT(7)); 1808c2ecf20Sopenharmony_ci /* use Audio infoframe updated info */ 1818c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(1), 1828c2ecf20Sopenharmony_ci BIT(5), 0); 1838c2ecf20Sopenharmony_ci /* enable SPDIF receiver */ 1848c2ecf20Sopenharmony_ci if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF) 1858c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, 1868c2ecf20Sopenharmony_ci BIT(7), BIT(7)); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void audio_shutdown(struct device *dev, void *data) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct adv7511 *adv7511 = dev_get_drvdata(dev); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF) 1968c2ecf20Sopenharmony_ci regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, 1978c2ecf20Sopenharmony_ci BIT(7), 0); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int adv7511_hdmi_i2s_get_dai_id(struct snd_soc_component *component, 2018c2ecf20Sopenharmony_ci struct device_node *endpoint) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct of_endpoint of_ep; 2048c2ecf20Sopenharmony_ci int ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci ret = of_graph_parse_endpoint(endpoint, &of_ep); 2078c2ecf20Sopenharmony_ci if (ret < 0) 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * HDMI sound should be located as reg = <2> 2128c2ecf20Sopenharmony_ci * Then, it is sound port 0 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci if (of_ep.port == 2) 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return -EINVAL; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic const struct hdmi_codec_ops adv7511_codec_ops = { 2218c2ecf20Sopenharmony_ci .hw_params = adv7511_hdmi_hw_params, 2228c2ecf20Sopenharmony_ci .audio_shutdown = audio_shutdown, 2238c2ecf20Sopenharmony_ci .audio_startup = audio_startup, 2248c2ecf20Sopenharmony_ci .get_dai_id = adv7511_hdmi_i2s_get_dai_id, 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic const struct hdmi_codec_pdata codec_data = { 2288c2ecf20Sopenharmony_ci .ops = &adv7511_codec_ops, 2298c2ecf20Sopenharmony_ci .max_i2s_channels = 2, 2308c2ecf20Sopenharmony_ci .i2s = 1, 2318c2ecf20Sopenharmony_ci .spdif = 1, 2328c2ecf20Sopenharmony_ci}; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ciint adv7511_audio_init(struct device *dev, struct adv7511 *adv7511) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci adv7511->audio_pdev = platform_device_register_data(dev, 2378c2ecf20Sopenharmony_ci HDMI_CODEC_DRV_NAME, 2388c2ecf20Sopenharmony_ci PLATFORM_DEVID_AUTO, 2398c2ecf20Sopenharmony_ci &codec_data, 2408c2ecf20Sopenharmony_ci sizeof(codec_data)); 2418c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(adv7511->audio_pdev); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_civoid adv7511_audio_exit(struct adv7511 *adv7511) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci if (adv7511->audio_pdev) { 2478c2ecf20Sopenharmony_ci platform_device_unregister(adv7511->audio_pdev); 2488c2ecf20Sopenharmony_ci adv7511->audio_pdev = NULL; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci} 251