18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (c) 2020 BayLibre, SAS. 48c2ecf20Sopenharmony_ci// Author: Jerome Brunet <jbrunet@baylibre.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 98c2ecf20Sopenharmony_ci#include <sound/pcm_iec958.h> 108c2ecf20Sopenharmony_ci#include <sound/soc.h> 118c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "aiu.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define AIU_958_MISC_NON_PCM BIT(0) 168c2ecf20Sopenharmony_ci#define AIU_958_MISC_MODE_16BITS BIT(1) 178c2ecf20Sopenharmony_ci#define AIU_958_MISC_16BITS_ALIGN GENMASK(6, 5) 188c2ecf20Sopenharmony_ci#define AIU_958_MISC_MODE_32BITS BIT(7) 198c2ecf20Sopenharmony_ci#define AIU_958_MISC_U_FROM_STREAM BIT(12) 208c2ecf20Sopenharmony_ci#define AIU_958_MISC_FORCE_LR BIT(13) 218c2ecf20Sopenharmony_ci#define AIU_958_CTRL_HOLD_EN BIT(0) 228c2ecf20Sopenharmony_ci#define AIU_CLK_CTRL_958_DIV_EN BIT(1) 238c2ecf20Sopenharmony_ci#define AIU_CLK_CTRL_958_DIV GENMASK(5, 4) 248c2ecf20Sopenharmony_ci#define AIU_CLK_CTRL_958_DIV_MORE BIT(12) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define AIU_CS_WORD_LEN 4 278c2ecf20Sopenharmony_ci#define AIU_958_INTERNAL_DIV 2 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void 308c2ecf20Sopenharmony_ciaiu_encoder_spdif_divider_enable(struct snd_soc_component *component, 318c2ecf20Sopenharmony_ci bool enable) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AIU_CLK_CTRL, 348c2ecf20Sopenharmony_ci AIU_CLK_CTRL_958_DIV_EN, 358c2ecf20Sopenharmony_ci enable ? AIU_CLK_CTRL_958_DIV_EN : 0); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void aiu_encoder_spdif_hold(struct snd_soc_component *component, 398c2ecf20Sopenharmony_ci bool enable) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AIU_958_CTRL, 428c2ecf20Sopenharmony_ci AIU_958_CTRL_HOLD_EN, 438c2ecf20Sopenharmony_ci enable ? AIU_958_CTRL_HOLD_EN : 0); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int 478c2ecf20Sopenharmony_ciaiu_encoder_spdif_trigger(struct snd_pcm_substream *substream, int cmd, 488c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci switch (cmd) { 538c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 548c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 558c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 568c2ecf20Sopenharmony_ci aiu_encoder_spdif_hold(component, false); 578c2ecf20Sopenharmony_ci return 0; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 608c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 618c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 628c2ecf20Sopenharmony_ci aiu_encoder_spdif_hold(component, true); 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci default: 668c2ecf20Sopenharmony_ci return -EINVAL; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int aiu_encoder_spdif_setup_cs_word(struct snd_soc_component *component, 718c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci u8 cs[AIU_CS_WORD_LEN]; 748c2ecf20Sopenharmony_ci unsigned int val; 758c2ecf20Sopenharmony_ci int ret; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, 788c2ecf20Sopenharmony_ci AIU_CS_WORD_LEN); 798c2ecf20Sopenharmony_ci if (ret < 0) 808c2ecf20Sopenharmony_ci return ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Write the 1st half word */ 838c2ecf20Sopenharmony_ci val = cs[1] | cs[0] << 8; 848c2ecf20Sopenharmony_ci snd_soc_component_write(component, AIU_958_CHSTAT_L0, val); 858c2ecf20Sopenharmony_ci snd_soc_component_write(component, AIU_958_CHSTAT_R0, val); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* Write the 2nd half word */ 888c2ecf20Sopenharmony_ci val = cs[3] | cs[2] << 8; 898c2ecf20Sopenharmony_ci snd_soc_component_write(component, AIU_958_CHSTAT_L1, val); 908c2ecf20Sopenharmony_ci snd_soc_component_write(component, AIU_958_CHSTAT_R1, val); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int aiu_encoder_spdif_hw_params(struct snd_pcm_substream *substream, 968c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 978c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 1008c2ecf20Sopenharmony_ci struct aiu *aiu = snd_soc_component_get_drvdata(component); 1018c2ecf20Sopenharmony_ci unsigned int val = 0, mrate; 1028c2ecf20Sopenharmony_ci int ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Disable the clock while changing the settings */ 1058c2ecf20Sopenharmony_ci aiu_encoder_spdif_divider_enable(component, false); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci switch (params_physical_width(params)) { 1088c2ecf20Sopenharmony_ci case 16: 1098c2ecf20Sopenharmony_ci val |= AIU_958_MISC_MODE_16BITS; 1108c2ecf20Sopenharmony_ci val |= FIELD_PREP(AIU_958_MISC_16BITS_ALIGN, 2); 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci case 32: 1138c2ecf20Sopenharmony_ci val |= AIU_958_MISC_MODE_32BITS; 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci default: 1168c2ecf20Sopenharmony_ci dev_err(dai->dev, "Unsupport physical width\n"); 1178c2ecf20Sopenharmony_ci return -EINVAL; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AIU_958_MISC, 1218c2ecf20Sopenharmony_ci AIU_958_MISC_NON_PCM | 1228c2ecf20Sopenharmony_ci AIU_958_MISC_MODE_16BITS | 1238c2ecf20Sopenharmony_ci AIU_958_MISC_16BITS_ALIGN | 1248c2ecf20Sopenharmony_ci AIU_958_MISC_MODE_32BITS | 1258c2ecf20Sopenharmony_ci AIU_958_MISC_FORCE_LR | 1268c2ecf20Sopenharmony_ci AIU_958_MISC_U_FROM_STREAM, 1278c2ecf20Sopenharmony_ci val); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Set the stream channel status word */ 1308c2ecf20Sopenharmony_ci ret = aiu_encoder_spdif_setup_cs_word(component, params); 1318c2ecf20Sopenharmony_ci if (ret) { 1328c2ecf20Sopenharmony_ci dev_err(dai->dev, "failed to set channel status word\n"); 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AIU_CLK_CTRL, 1378c2ecf20Sopenharmony_ci AIU_CLK_CTRL_958_DIV | 1388c2ecf20Sopenharmony_ci AIU_CLK_CTRL_958_DIV_MORE, 1398c2ecf20Sopenharmony_ci FIELD_PREP(AIU_CLK_CTRL_958_DIV, 1408c2ecf20Sopenharmony_ci __ffs(AIU_958_INTERNAL_DIV))); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 2 * 32bits per subframe * 2 channels = 128 */ 1438c2ecf20Sopenharmony_ci mrate = params_rate(params) * 128 * AIU_958_INTERNAL_DIV; 1448c2ecf20Sopenharmony_ci ret = clk_set_rate(aiu->spdif.clks[MCLK].clk, mrate); 1458c2ecf20Sopenharmony_ci if (ret) { 1468c2ecf20Sopenharmony_ci dev_err(dai->dev, "failed to set mclk rate\n"); 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci aiu_encoder_spdif_divider_enable(component, true); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int aiu_encoder_spdif_hw_free(struct snd_pcm_substream *substream, 1568c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci aiu_encoder_spdif_divider_enable(component, false); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int aiu_encoder_spdif_startup(struct snd_pcm_substream *substream, 1668c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); 1698c2ecf20Sopenharmony_ci int ret; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * NOTE: Make sure the spdif block is on its own divider. 1738c2ecf20Sopenharmony_ci * 1748c2ecf20Sopenharmony_ci * The spdif can be clocked by the i2s master clock or its own 1758c2ecf20Sopenharmony_ci * clock. We should (in theory) change the source depending on the 1768c2ecf20Sopenharmony_ci * origin of the data. 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * However, considering the clocking scheme used on these platforms, 1798c2ecf20Sopenharmony_ci * the master clocks will pick the same PLL source when they are 1808c2ecf20Sopenharmony_ci * playing from the same FIFO. The clock should be in sync so, it 1818c2ecf20Sopenharmony_ci * should not be necessary to reparent the spdif master clock. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci ret = clk_set_parent(aiu->spdif.clks[MCLK].clk, 1848c2ecf20Sopenharmony_ci aiu->spdif_mclk); 1858c2ecf20Sopenharmony_ci if (ret) 1868c2ecf20Sopenharmony_ci return ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci ret = clk_bulk_prepare_enable(aiu->spdif.clk_num, aiu->spdif.clks); 1898c2ecf20Sopenharmony_ci if (ret) 1908c2ecf20Sopenharmony_ci dev_err(dai->dev, "failed to enable spdif clocks\n"); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void aiu_encoder_spdif_shutdown(struct snd_pcm_substream *substream, 1968c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci clk_bulk_disable_unprepare(aiu->spdif.clk_num, aiu->spdif.clks); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ciconst struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops = { 2048c2ecf20Sopenharmony_ci .trigger = aiu_encoder_spdif_trigger, 2058c2ecf20Sopenharmony_ci .hw_params = aiu_encoder_spdif_hw_params, 2068c2ecf20Sopenharmony_ci .hw_free = aiu_encoder_spdif_hw_free, 2078c2ecf20Sopenharmony_ci .startup = aiu_encoder_spdif_startup, 2088c2ecf20Sopenharmony_ci .shutdown = aiu_encoder_spdif_shutdown, 2098c2ecf20Sopenharmony_ci}; 210