1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 //Copyright(c) 2022 AMD. All rights reserved.
4 //
5 //Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com>
6 // Bala Kishore <balakishore.pati@amd.com>
7
8 #include <sof/audio/component.h>
9 #include <sof/drivers/acp_dai_dma.h>
10 #include <rtos/interrupt.h>
11 #include <rtos/alloc.h>
12 #include <sof/lib/dai.h>
13 #include <sof/lib/dma.h>
14 #include <sof/lib/uuid.h>
15 #include <ipc/dai.h>
16 #include <ipc/topology.h>
17 #include <platform/fw_scratch_mem.h>
18 #include <sof/lib/io.h>
19 #include <platform/chip_offset_byte.h>
20
21 /* 8f00c3bb-e835-4767-9a34-b8ec1041e56b */
22 DECLARE_SOF_UUID("hsdai", hsdai_uuid, 0x8f00c3bb, 0xe835, 0x4767,
23 0x9a, 0x34, 0xb8, 0xec, 0x10, 0x41, 0xe5, 0x6b);
24 DECLARE_TR_CTX(hsdai_tr, SOF_UUID(hsdai_uuid), LOG_LEVEL_INFO);
25
hsdai_set_config(struct dai * dai,struct ipc_config_dai * common_config,const void * spec_config)26 static inline int hsdai_set_config(struct dai *dai, struct ipc_config_dai *common_config,
27 const void *spec_config)
28 {
29 const struct sof_ipc_dai_config *config = spec_config;
30 struct acp_pdata *acpdata = dai_get_drvdata(dai);
31
32 acp_hstdm_iter_t hs_iter;
33 acp_hstdm_irer_t hs_irer;
34 acp_i2stdm_mstrclkgen_t i2stdm_mstrclkgen;
35
36 acpdata->config = *config;
37 acpdata->params = config->acphs;
38 i2stdm_mstrclkgen.u32all = io_reg_read(PU_REGISTER_BASE + ACP_I2STDM2_MSTRCLKGEN);
39 i2stdm_mstrclkgen.bits.i2stdm_master_mode = 1;
40 switch (config->format & SOF_DAI_FMT_FORMAT_MASK) {
41 case SOF_DAI_FMT_DSP_A:
42 i2stdm_mstrclkgen.bits.i2stdm_format_mode = 1;
43 switch (acpdata->params.tdm_slots) {
44 case 2:
45 i2stdm_mstrclkgen.bits.i2stdm_lrclk_div_val = 0x20;
46 i2stdm_mstrclkgen.bits.i2stdm_bclk_div_val = 0x80;
47 break;
48 case 4:
49 i2stdm_mstrclkgen.bits.i2stdm_lrclk_div_val = 0x40;
50 i2stdm_mstrclkgen.bits.i2stdm_bclk_div_val = 0x40;
51 break;
52 case 6:
53 i2stdm_mstrclkgen.bits.i2stdm_lrclk_div_val = 0x60;
54 i2stdm_mstrclkgen.bits.i2stdm_bclk_div_val = 0x30;
55 break;
56 case 8:
57 i2stdm_mstrclkgen.bits.i2stdm_lrclk_div_val = 0x80;
58 i2stdm_mstrclkgen.bits.i2stdm_bclk_div_val = 0x20;
59 break;
60 default:
61 dai_err(dai, "hsdai_set_config unsupported slots");
62 return -EINVAL;
63 }
64 break;
65 case SOF_DAI_FMT_I2S:
66 i2stdm_mstrclkgen.bits.i2stdm_format_mode = 0;
67 i2stdm_mstrclkgen.bits.i2stdm_lrclk_div_val = 0x20;
68 i2stdm_mstrclkgen.bits.i2stdm_bclk_div_val = 0x80;
69 break;
70 default:
71 dai_err(dai, "hsdai_set_config invalid format");
72 return -EINVAL;
73 }
74
75 hs_iter = (acp_hstdm_iter_t)io_reg_read((PU_REGISTER_BASE + ACP_HSTDM_ITER));
76 hs_irer = (acp_hstdm_irer_t)io_reg_read((PU_REGISTER_BASE + ACP_HSTDM_IRER));
77 /* set master clk for hs dai */
78 io_reg_write(PU_REGISTER_BASE + ACP_I2STDM2_MSTRCLKGEN, i2stdm_mstrclkgen.u32all);
79 switch (config->format & SOF_DAI_FMT_FORMAT_MASK) {
80 case SOF_DAI_FMT_DSP_A:
81 {
82 acp_hstdm_txfrmt_t i2stdm_txfrmt;
83 acp_hstdm_rxfrmt_t i2stdm_rxfrmt;
84
85 i2stdm_txfrmt.u32all = io_reg_read(PU_REGISTER_BASE + ACP_HSTDM_TXFRMT);
86 i2stdm_txfrmt.bits.hstdm_num_slots = acpdata->params.tdm_slots;
87 i2stdm_txfrmt.bits.hstdm_slot_len = 16;
88 io_reg_write((PU_REGISTER_BASE + ACP_HSTDM_TXFRMT), i2stdm_txfrmt.u32all);
89
90 hs_iter.bits.hstdm_tx_protocol_mode = 1;
91 io_reg_write((PU_REGISTER_BASE + ACP_HSTDM_ITER), hs_iter.u32all);
92
93 i2stdm_rxfrmt.u32all = io_reg_read(PU_REGISTER_BASE + ACP_HSTDM_RXFRMT);
94 i2stdm_rxfrmt.bits.hstdm_num_slots = acpdata->params.tdm_slots;
95 i2stdm_rxfrmt.bits.hstdm_slot_len = 16;
96 io_reg_write((PU_REGISTER_BASE + ACP_HSTDM_RXFRMT), i2stdm_rxfrmt.u32all);
97
98 hs_irer.bits.hstdm_rx_protocol_mode = 1;
99 io_reg_write((PU_REGISTER_BASE + ACP_HSTDM_IRER), hs_irer.u32all);
100 }
101 break;
102 case SOF_DAI_FMT_I2S:
103 hs_iter.bits.hstdm_tx_protocol_mode = 0;
104 io_reg_write((PU_REGISTER_BASE + ACP_HSTDM_ITER), hs_iter.u32all);
105
106 hs_irer.bits.hstdm_rx_protocol_mode = 0;
107 io_reg_write((PU_REGISTER_BASE + ACP_HSTDM_IRER), hs_irer.u32all);
108 break;
109 default:
110 dai_err(dai, "hsdai_set_config invalid format");
111 return -EINVAL;
112 }
113 return 0;
114 }
115
hsdai_trigger(struct dai * dai,int cmd,int direction)116 static int hsdai_trigger(struct dai *dai, int cmd, int direction)
117 {
118 /* nothing to do on rembrandt for HS dai */
119 return 0;
120 }
121
hsdai_probe(struct dai * dai)122 static int hsdai_probe(struct dai *dai)
123 {
124 struct acp_pdata *acp;
125
126 dai_info(dai, "HS dai probe");
127 /* allocate private data */
128 acp = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(*acp));
129 if (!acp) {
130 dai_err(dai, "HS dai probe alloc failed");
131 return -ENOMEM;
132 }
133 dai_set_drvdata(dai, acp);
134 return 0;
135 }
136
hsdai_remove(struct dai * dai)137 static int hsdai_remove(struct dai *dai)
138 {
139 struct acp_pdata *acp = dai_get_drvdata(dai);
140
141 rfree(acp);
142 dai_set_drvdata(dai, NULL);
143
144 return 0;
145 }
146
hsdai_get_fifo(struct dai * dai,int direction,int stream_id)147 static int hsdai_get_fifo(struct dai *dai, int direction, int stream_id)
148 {
149 switch (direction) {
150 case DAI_DIR_PLAYBACK:
151 case DAI_DIR_CAPTURE:
152 return dai_fifo(dai, direction);
153 default:
154 dai_err(dai, "Invalid direction");
155 return -EINVAL;
156 }
157 }
158
hsdai_get_handshake(struct dai * dai,int direction,int stream_id)159 static int hsdai_get_handshake(struct dai *dai, int direction, int stream_id)
160 {
161 return dai->plat_data.fifo[direction].handshake;
162 }
163
hsdai_get_hw_params(struct dai * dai,struct sof_ipc_stream_params * params,int dir)164 static int hsdai_get_hw_params(struct dai *dai,
165 struct sof_ipc_stream_params *params,
166 int dir)
167 {
168 struct acp_pdata *acpdata = dai_get_drvdata(dai);
169
170 if (dir == DAI_DIR_PLAYBACK) {
171 /* SP DAI currently supports only these parameters */
172 params->rate = ACP_DEFAULT_SAMPLE_RATE;
173 params->channels = acpdata->params.tdm_slots;
174 params->buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
175 params->frame_fmt = SOF_IPC_FRAME_S16_LE;
176 } else if (dir == DAI_DIR_CAPTURE) {
177 /* SP DAI currently supports only these parameters */
178 params->rate = ACP_DEFAULT_SAMPLE_RATE;
179 params->channels = acpdata->params.tdm_slots;
180 params->buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
181 params->frame_fmt = SOF_IPC_FRAME_S16_LE;
182 }
183
184 return 0;
185 }
186
187 const struct dai_driver acp_hsdai_driver = {
188 .type = SOF_DAI_AMD_HS,
189 .uid = SOF_UUID(hsdai_uuid),
190 .tctx = &hsdai_tr,
191 .dma_dev = DMA_DEV_SP,
192 .dma_caps = DMA_CAP_SP,
193 .ops = {
194 .trigger = hsdai_trigger,
195 .set_config = hsdai_set_config,
196 .probe = hsdai_probe,
197 .remove = hsdai_remove,
198 .get_fifo = hsdai_get_fifo,
199 .get_handshake = hsdai_get_handshake,
200 .get_hw_params = hsdai_get_hw_params,
201 },
202 };
203