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