• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 HiHope Open Source Organization .
3  *
4  * HDF is dual licensed: you can use it either under the terms of
5  * the GPL, or the BSD license, at your option.
6  * See the LICENSE file in the root of this repository for complete details.
7  */
8 
9 #include <sound/pcm_params.h>
10 #include <sound/dmaengine_pcm.h>
11 #include <linux/module.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/delay.h>
14 #include <linux/of_gpio.h>
15 #include <linux/of_device.h>
16 #include <linux/of_address.h>
17 #include <linux/clk.h>
18 #include <linux/clk/rockchip.h>
19 #include <linux/pm_runtime.h>
20 #include <linux/regmap.h>
21 #include <linux/reset.h>
22 #include <linux/spinlock.h>
23 
24 #include "audio_driver_log.h"
25 #include "rk3568_dai_linux.h"
26 
27 #define DRV_NAME "rockchip-i2s-tdm"
28 
29 struct txrx_config {
30     u32 addr;
31     u32 reg;
32     u32 txonly;
33     u32 rxonly;
34 };
35 struct rk_i2s_soc_data {
36     u32 softrst_offset;
37     u32 grf_reg_offset;
38     u32 grf_shift;
39     int config_count;
40     const struct txrx_config *configs;
41     int (*init)(struct device *dev, u32 addr);
42 };
43 
44 static const struct txrx_config rk3568_txrx_config[] = {
45     // base addr, reg, txonly, rxonly
46     { 0xfe410000, 0x504, RK3568_I2S1_CLK_TXONLY, RK3568_I2S1_CLK_RXONLY },
47 };
48 
common_soc_init(struct device * dev,u32 addr)49 static int common_soc_init(struct device *dev, u32 addr)
50 {
51     struct rk3568_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
52     AUDIO_DEVICE_LOG_DEBUG("i2s_tdm addr = %p ", i2s_tdm);
53     regmap_write(i2s_tdm->grf, rk3568_txrx_config[0].reg, rk3568_txrx_config[0].txonly);
54     return 0;
55 }
56 
57 static const struct rk_i2s_soc_data rk3568_i2s_soc_data = {
58     .softrst_offset = 0x0400,     // offset
59     .configs = rk3568_txrx_config,
60     .config_count = 1,
61     .init = common_soc_init,
62 };
63 static const struct of_device_id rockchip_i2s_tdm_match[] = {
64 
65     { .compatible = "rockchip,rk3568-i2s-tdm", .data = &rk3568_i2s_soc_data },
66     {},
67 };
rockchip_i2s_tdm_wr_reg(struct device * dev,unsigned int reg)68 static bool rockchip_i2s_tdm_wr_reg(struct device *dev, unsigned int reg)
69 {
70     switch (reg) {
71         case I2S_TXCR:
72         case I2S_RXCR:
73         case I2S_CKR:
74         case I2S_DMACR:
75         case I2S_INTCR:
76         case I2S_XFER:
77         case I2S_CLR:
78         case I2S_TXDR:
79         case I2S_TDM_TXCR:
80         case I2S_TDM_RXCR:
81         case I2S_CLKDIV:
82             return true;
83         default:
84             return false;
85     }
86 }
87 
rockchip_i2s_tdm_rd_reg(struct device * dev,unsigned int reg)88 static bool rockchip_i2s_tdm_rd_reg(struct device *dev, unsigned int reg)
89 {
90     switch (reg) {
91         case I2S_TXCR:
92         case I2S_RXCR:
93         case I2S_CKR:
94         case I2S_DMACR:
95         case I2S_INTCR:
96         case I2S_XFER:
97         case I2S_CLR:
98         case I2S_TXDR:
99         case I2S_RXDR:
100         case I2S_TXFIFOLR:
101         case I2S_INTSR:
102         case I2S_RXFIFOLR:
103         case I2S_TDM_TXCR:
104         case I2S_TDM_RXCR:
105         case I2S_CLKDIV:
106             return true;
107         default:
108             return false;
109     }
110 }
111 
rockchip_i2s_tdm_volatile_reg(struct device * dev,unsigned int reg)112 static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg)
113 {
114     switch (reg) {
115         case I2S_TXFIFOLR:
116         case I2S_INTSR:
117         case I2S_CLR:
118         case I2S_TXDR:
119         case I2S_RXDR:
120         case I2S_RXFIFOLR:
121             return true;
122         default:
123             return false;
124     }
125 }
126 
rockchip_i2s_tdm_precious_reg(struct device * dev,unsigned int reg)127 static bool rockchip_i2s_tdm_precious_reg(struct device *dev, unsigned int reg)
128 {
129     switch (reg) {
130         case I2S_RXDR:
131             return true;
132         default:
133             return false;
134     }
135 }
136 
137 // regs init value map
138 static const struct reg_default rockchip_i2s_tdm_reg_defaults[] = {
139     {0x00, 0x7200000f},
140     {0x04, 0x01c8000f},
141     {0x08, 0x00001f1f},
142     {0x10, 0x001f0000},
143     {0x14, 0x01f00000},
144     {0x30, 0x00003eff},
145     {0x34, 0x00003eff},
146     {0x38, 0x00000707},
147 };
148 
149 static const struct regmap_config rockchip_i2s_tdm_regmap_config = {
150     .reg_bits = 32,
151     .reg_stride = 4,
152     .val_bits = 32,
153     .max_register = I2S_CLKDIV,
154     .reg_defaults = rockchip_i2s_tdm_reg_defaults,
155     .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_tdm_reg_defaults),
156     .writeable_reg = rockchip_i2s_tdm_wr_reg,
157     .readable_reg = rockchip_i2s_tdm_rd_reg,
158     .volatile_reg = rockchip_i2s_tdm_volatile_reg,
159     .precious_reg = rockchip_i2s_tdm_precious_reg,
160     .cache_type = REGCACHE_FLAT,
161 };
162 
i2s_tdm_runtime_suspend(struct rk3568_i2s_tdm_dev * i2s_tdm)163 static int i2s_tdm_runtime_suspend(struct rk3568_i2s_tdm_dev *i2s_tdm)
164 {
165     regcache_cache_only(i2s_tdm->regmap, true);
166     if (!IS_ERR(i2s_tdm->mclk_tx))
167         clk_disable_unprepare(i2s_tdm->mclk_tx);
168     if (!IS_ERR(i2s_tdm->mclk_rx))
169         clk_disable_unprepare(i2s_tdm->mclk_rx);
170     return 0;
171 }
172 
i2s_tdm_runtime_resume(struct rk3568_i2s_tdm_dev * i2s_tdm)173 static int i2s_tdm_runtime_resume(struct rk3568_i2s_tdm_dev *i2s_tdm)
174 {
175     int ret;
176     if (!IS_ERR(i2s_tdm->mclk_tx))
177         clk_prepare_enable(i2s_tdm->mclk_tx);
178     if (!IS_ERR(i2s_tdm->mclk_rx))
179         clk_prepare_enable(i2s_tdm->mclk_rx);
180 
181     regcache_cache_only(i2s_tdm->regmap, false);
182     regcache_mark_dirty(i2s_tdm->regmap);
183 
184     ret = regcache_sync(i2s_tdm->regmap);
185     if (ret) {
186         if (!IS_ERR(i2s_tdm->mclk_tx))
187             clk_disable_unprepare(i2s_tdm->mclk_tx);
188         if (!IS_ERR(i2s_tdm->mclk_rx))
189             clk_disable_unprepare(i2s_tdm->mclk_rx);
190     }
191     return ret;
192 }
193 
rockchip_i2s_tdm_probe(struct platform_device * pdev)194 static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
195 {
196     struct device_node *node = pdev->dev.of_node;
197     const struct of_device_id *of_id;
198     struct rk3568_i2s_tdm_dev *i2s_tdm;
199     struct resource *res;
200     struct device *temp_i2s_dev;
201 
202     int ret;
203     int val;
204     AUDIO_DEVICE_LOG_INFO("enter ");
205     temp_i2s_dev = &pdev->dev;
206     AUDIO_DEVICE_LOG_DEBUG("dmaDevice->name %s ", dev_name(temp_i2s_dev));
207     i2s_tdm = devm_kzalloc(&pdev->dev, sizeof(*i2s_tdm), GFP_KERNEL);
208     if (!i2s_tdm) {
209         return -ENOMEM;
210     }
211     i2s_tdm->dev = &pdev->dev;
212 
213     of_id = of_match_device(rockchip_i2s_tdm_match, &pdev->dev);
214     if (!of_id || !of_id->data) {
215         return -EINVAL;
216     }
217 
218     spin_lock_init(&i2s_tdm->lock);
219     i2s_tdm->soc_data = (const struct rk_i2s_soc_data *)of_id->data;
220 
221     i2s_tdm->bclk_fs = 64; // default-freq div factor is 64
222     if (!of_property_read_u32(node, "rockchip,bclk-fs", &val)) {
223         if ((val >= 32) && (val % 2 == 0)) // min-freq div factor is 32, and it is an integer multiple of 2
224             i2s_tdm->bclk_fs = val;
225     }
226 
227     i2s_tdm->clk_trcm = I2S_CKR_TRCM_TXRX;
228     if (!of_property_read_u32(node, "rockchip,clk-trcm", &val)) {
229         if (val >= 0 && val <= 2) { // clk-trcm factor should between 0 and 2
230             i2s_tdm->clk_trcm = val << I2S_CKR_TRCM_SHIFT;
231         }
232     }
233 
234     i2s_tdm->tdm_fsync_half_frame =
235         of_property_read_bool(node, "rockchip,tdm-fsync-half-frame");
236 
237     i2s_tdm->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
238     if (IS_ERR(i2s_tdm->grf)) {
239         return PTR_ERR(i2s_tdm->grf);
240     }
241 
242     i2s_tdm->tx_reset = devm_reset_control_get(&pdev->dev, "tx-m");
243     if (IS_ERR(i2s_tdm->tx_reset)) {
244         ret = PTR_ERR(i2s_tdm->tx_reset);
245         if (ret != -ENOENT) {
246             return ret;
247         }
248     }
249 
250     i2s_tdm->rx_reset = devm_reset_control_get(&pdev->dev, "rx-m");
251     if (IS_ERR(i2s_tdm->rx_reset)) {
252         ret = PTR_ERR(i2s_tdm->rx_reset);
253         if (ret != -ENOENT) {
254             return ret;
255         }
256     }
257 
258     i2s_tdm->hclk = devm_clk_get(&pdev->dev, "hclk");
259     if (IS_ERR(i2s_tdm->hclk)) {
260         return PTR_ERR(i2s_tdm->hclk);
261     }
262 
263     ret = clk_prepare_enable(i2s_tdm->hclk);
264     if (ret) {
265         return ret;
266     }
267 
268     i2s_tdm->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx");
269     if (IS_ERR(i2s_tdm->mclk_tx)) {
270         return PTR_ERR(i2s_tdm->mclk_tx);
271     }
272 
273     i2s_tdm->mclk_rx = devm_clk_get(&pdev->dev, "mclk_rx");
274     if (IS_ERR(i2s_tdm->mclk_rx)) {
275         return PTR_ERR(i2s_tdm->mclk_rx);
276     }
277 
278     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
279 
280     i2s_tdm->regmap = devm_regmap_init_mmio(&pdev->dev, devm_ioremap_resource(&pdev->dev, res),
281         &rockchip_i2s_tdm_regmap_config);
282     if (IS_ERR(i2s_tdm->regmap)) {
283         return PTR_ERR(i2s_tdm->regmap);
284     }
285 
286     atomic_set(&i2s_tdm->refcount, 0);
287     dev_set_drvdata(&pdev->dev, i2s_tdm);
288 
289     ret = i2s_tdm_runtime_resume(i2s_tdm);
290     if (ret) {
291         return ret;
292     }
293 
294     regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
295         I2S_DMACR_TDL(16)); // Transmit Data Level MASK with 16bit
296     regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
297         I2S_DMACR_RDL(16)); // Receive Data Level MASK with 16bit
298     regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
299         I2S_CKR_TRCM_MASK, i2s_tdm->clk_trcm);
300     return 0;
301 }
302 
rockchip_i2s_tdm_remove(struct platform_device * pdev)303 static int rockchip_i2s_tdm_remove(struct platform_device *pdev)
304 {
305     struct rk3568_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(&pdev->dev);
306 
307     i2s_tdm_runtime_suspend(i2s_tdm);
308 
309     if (!IS_ERR(i2s_tdm->mclk_tx))
310         clk_prepare_enable(i2s_tdm->mclk_tx);
311     if (!IS_ERR(i2s_tdm->mclk_rx))
312         clk_prepare_enable(i2s_tdm->mclk_rx);
313     if (!IS_ERR(i2s_tdm->hclk))
314         clk_disable_unprepare(i2s_tdm->hclk);
315 
316     return 0;
317 }
318 
319 static struct platform_driver rockchip_i2s_tdm_driver = {
320     .probe = rockchip_i2s_tdm_probe,
321     .remove = rockchip_i2s_tdm_remove,
322     .driver = {
323         .name = DRV_NAME,
324         .of_match_table = of_match_ptr(rockchip_i2s_tdm_match),
325         .pm = NULL,
326     },
327 };
328 module_platform_driver(rockchip_i2s_tdm_driver);
329