brintos

brintos / linux-shallow public Read only

0
0
Text · 13.1 KiB · a603dd1 Raw
529 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * jh7110_pwmdac.c -- StarFive JH7110 PWM-DAC driver4 *5 * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.6 *7 * Authors: Jenny Zhang8 *	    Curry Zhang9 *	    Xingyu Wu <xingyu.wu@starfivetech.com>10 *	    Hal Feng <hal.feng@starfivetech.com>11 */12 13#include <linux/clk.h>14#include <linux/device.h>15#include <linux/init.h>16#include <linux/interrupt.h>17#include <linux/io.h>18#include <linux/module.h>19#include <linux/pm_runtime.h>20#include <linux/reset.h>21#include <linux/slab.h>22#include <linux/types.h>23#include <sound/dmaengine_pcm.h>24#include <sound/pcm.h>25#include <sound/pcm_params.h>26#include <sound/soc.h>27 28#define JH7110_PWMDAC_WDATA				0x0029#define JH7110_PWMDAC_CTRL				0x0430	#define JH7110_PWMDAC_ENABLE			BIT(0)31	#define JH7110_PWMDAC_SHIFT			BIT(1)32	#define JH7110_PWMDAC_DUTY_CYCLE_SHIFT		233	#define JH7110_PWMDAC_DUTY_CYCLE_MASK		GENMASK(3, 2)34	#define JH7110_PWMDAC_CNT_N_SHIFT		435	#define JH7110_PWMDAC_CNT_N_MASK		GENMASK(12, 4)36	#define JH7110_PWMDAC_DATA_CHANGE		BIT(13)37	#define JH7110_PWMDAC_DATA_MODE			BIT(14)38	#define JH7110_PWMDAC_DATA_SHIFT_SHIFT		1539	#define JH7110_PWMDAC_DATA_SHIFT_MASK		GENMASK(17, 15)40 41enum JH7110_PWMDAC_SHIFT_VAL {42	PWMDAC_SHIFT_8 = 0,43	PWMDAC_SHIFT_10,44};45 46enum JH7110_PWMDAC_DUTY_CYCLE_VAL {47	PWMDAC_CYCLE_LEFT = 0,48	PWMDAC_CYCLE_RIGHT,49	PWMDAC_CYCLE_CENTER,50};51 52enum JH7110_PWMDAC_CNT_N_VAL {53	PWMDAC_SAMPLE_CNT_1 = 1,54	PWMDAC_SAMPLE_CNT_2,55	PWMDAC_SAMPLE_CNT_3,56	PWMDAC_SAMPLE_CNT_512 = 512, /* max */57};58 59enum JH7110_PWMDAC_DATA_CHANGE_VAL {60	NO_CHANGE = 0,61	CHANGE,62};63 64enum JH7110_PWMDAC_DATA_MODE_VAL {65	UNSIGNED_DATA = 0,66	INVERTER_DATA_MSB,67};68 69enum JH7110_PWMDAC_DATA_SHIFT_VAL {70	PWMDAC_DATA_LEFT_SHIFT_BIT_0 = 0,71	PWMDAC_DATA_LEFT_SHIFT_BIT_1,72	PWMDAC_DATA_LEFT_SHIFT_BIT_2,73	PWMDAC_DATA_LEFT_SHIFT_BIT_3,74	PWMDAC_DATA_LEFT_SHIFT_BIT_4,75	PWMDAC_DATA_LEFT_SHIFT_BIT_5,76	PWMDAC_DATA_LEFT_SHIFT_BIT_6,77	PWMDAC_DATA_LEFT_SHIFT_BIT_7,78};79 80struct jh7110_pwmdac_cfg {81	enum JH7110_PWMDAC_SHIFT_VAL shift;82	enum JH7110_PWMDAC_DUTY_CYCLE_VAL duty_cycle;83	u16 cnt_n;84	enum JH7110_PWMDAC_DATA_CHANGE_VAL data_change;85	enum JH7110_PWMDAC_DATA_MODE_VAL data_mode;86	enum JH7110_PWMDAC_DATA_SHIFT_VAL data_shift;87};88 89struct jh7110_pwmdac_dev {90	void __iomem *base;91	resource_size_t	mapbase;92	struct jh7110_pwmdac_cfg cfg;93 94	struct clk_bulk_data clks[2];95	struct reset_control *rst_apb;96	struct device *dev;97	struct snd_dmaengine_dai_dma_data play_dma_data;98	u32 saved_ctrl;99};100 101static inline void jh7110_pwmdac_write_reg(void __iomem *io_base, int reg, u32 val)102{103	writel(val, io_base + reg);104}105 106static inline u32 jh7110_pwmdac_read_reg(void __iomem *io_base, int reg)107{108	return readl(io_base + reg);109}110 111static void jh7110_pwmdac_set_enable(struct jh7110_pwmdac_dev *dev, bool enable)112{113	u32 value;114 115	value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);116	if (enable)117		value |= JH7110_PWMDAC_ENABLE;118	else119		value &= ~JH7110_PWMDAC_ENABLE;120 121	jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);122}123 124static void jh7110_pwmdac_set_shift(struct jh7110_pwmdac_dev *dev)125{126	u32 value;127 128	value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);129	if (dev->cfg.shift == PWMDAC_SHIFT_8)130		value &= ~JH7110_PWMDAC_SHIFT;131	else if (dev->cfg.shift == PWMDAC_SHIFT_10)132		value |= JH7110_PWMDAC_SHIFT;133 134	jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);135}136 137static void jh7110_pwmdac_set_duty_cycle(struct jh7110_pwmdac_dev *dev)138{139	u32 value;140 141	value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);142	value &= ~JH7110_PWMDAC_DUTY_CYCLE_MASK;143	value |= (dev->cfg.duty_cycle & 0x3) << JH7110_PWMDAC_DUTY_CYCLE_SHIFT;144 145	jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);146}147 148static void jh7110_pwmdac_set_cnt_n(struct jh7110_pwmdac_dev *dev)149{150	u32 value;151 152	value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);153	value &= ~JH7110_PWMDAC_CNT_N_MASK;154	value |= ((dev->cfg.cnt_n - 1) & 0x1ff) << JH7110_PWMDAC_CNT_N_SHIFT;155 156	jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);157}158 159static void jh7110_pwmdac_set_data_change(struct jh7110_pwmdac_dev *dev)160{161	u32 value;162 163	value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);164	if (dev->cfg.data_change == NO_CHANGE)165		value &= ~JH7110_PWMDAC_DATA_CHANGE;166	else if (dev->cfg.data_change == CHANGE)167		value |= JH7110_PWMDAC_DATA_CHANGE;168 169	jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);170}171 172static void jh7110_pwmdac_set_data_mode(struct jh7110_pwmdac_dev *dev)173{174	u32 value;175 176	value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);177	if (dev->cfg.data_mode == UNSIGNED_DATA)178		value &= ~JH7110_PWMDAC_DATA_MODE;179	else if (dev->cfg.data_mode == INVERTER_DATA_MSB)180		value |= JH7110_PWMDAC_DATA_MODE;181 182	jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);183}184 185static void jh7110_pwmdac_set_data_shift(struct jh7110_pwmdac_dev *dev)186{187	u32 value;188 189	value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);190	value &= ~JH7110_PWMDAC_DATA_SHIFT_MASK;191	value |= (dev->cfg.data_shift & 0x7) << JH7110_PWMDAC_DATA_SHIFT_SHIFT;192 193	jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);194}195 196static void jh7110_pwmdac_set(struct jh7110_pwmdac_dev *dev)197{198	jh7110_pwmdac_set_shift(dev);199	jh7110_pwmdac_set_duty_cycle(dev);200	jh7110_pwmdac_set_cnt_n(dev);201	jh7110_pwmdac_set_enable(dev, true);202 203	jh7110_pwmdac_set_data_change(dev);204	jh7110_pwmdac_set_data_mode(dev);205	jh7110_pwmdac_set_data_shift(dev);206}207 208static void jh7110_pwmdac_stop(struct jh7110_pwmdac_dev *dev)209{210	jh7110_pwmdac_set_enable(dev, false);211}212 213static int jh7110_pwmdac_startup(struct snd_pcm_substream *substream,214				 struct snd_soc_dai *dai)215{216	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);217	struct snd_soc_dai_link *dai_link = rtd->dai_link;218 219	dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC;220 221	return 0;222}223 224static int jh7110_pwmdac_hw_params(struct snd_pcm_substream *substream,225				   struct snd_pcm_hw_params *params,226				   struct snd_soc_dai *dai)227{228	struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev);229	unsigned long core_clk_rate;230	int ret;231 232	switch (params_rate(params)) {233	case 8000:234		dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3;235		core_clk_rate = 6144000;236		break;237	case 11025:238		dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_2;239		core_clk_rate = 5644800;240		break;241	case 16000:242		dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3;243		core_clk_rate = 12288000;244		break;245	case 22050:246		dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;247		core_clk_rate = 5644800;248		break;249	case 32000:250		dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;251		core_clk_rate = 8192000;252		break;253	case 44100:254		dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;255		core_clk_rate = 11289600;256		break;257	case 48000:258		dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;259		core_clk_rate = 12288000;260		break;261	default:262		dev_err(dai->dev, "%d rate not supported\n",263			params_rate(params));264		return -EINVAL;265	}266 267	switch (params_channels(params)) {268	case 1:269		dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;270		break;271	case 2:272		dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;273		break;274	default:275		dev_err(dai->dev, "%d channels not supported\n",276			params_channels(params));277		return -EINVAL;278	}279 280	/*281	 * The clock rate always rounds down when using clk_set_rate()282	 * so increase the rate a bit283	 */284	core_clk_rate += 64;285	jh7110_pwmdac_set(dev);286 287	ret = clk_set_rate(dev->clks[1].clk, core_clk_rate);288	if (ret)289		return dev_err_probe(dai->dev, ret,290				     "failed to set rate %lu for core clock\n",291				     core_clk_rate);292 293	return 0;294}295 296static int jh7110_pwmdac_trigger(struct snd_pcm_substream *substream, int cmd,297				 struct snd_soc_dai *dai)298{299	struct jh7110_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai);300	int ret = 0;301 302	switch (cmd) {303	case SNDRV_PCM_TRIGGER_START:304	case SNDRV_PCM_TRIGGER_RESUME:305	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:306		jh7110_pwmdac_set(dev);307		break;308 309	case SNDRV_PCM_TRIGGER_STOP:310	case SNDRV_PCM_TRIGGER_SUSPEND:311	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:312		jh7110_pwmdac_stop(dev);313		break;314	default:315		ret = -EINVAL;316		break;317	}318 319	return ret;320}321 322static int jh7110_pwmdac_crg_enable(struct jh7110_pwmdac_dev *dev, bool enable)323{324	int ret;325 326	if (enable) {327		ret = clk_bulk_prepare_enable(ARRAY_SIZE(dev->clks), dev->clks);328		if (ret)329			return dev_err_probe(dev->dev, ret,330					     "failed to enable pwmdac clocks\n");331 332		ret = reset_control_deassert(dev->rst_apb);333		if (ret) {334			dev_err(dev->dev, "failed to deassert pwmdac apb reset\n");335			goto err_rst_apb;336		}337	} else {338		clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks);339	}340 341	return 0;342 343err_rst_apb:344	clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks);345 346	return ret;347}348 349static int jh7110_pwmdac_dai_probe(struct snd_soc_dai *dai)350{351	struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev);352 353	snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL);354	snd_soc_dai_set_drvdata(dai, dev);355 356	return 0;357}358 359static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = {360	.probe		= jh7110_pwmdac_dai_probe,361	.startup	= jh7110_pwmdac_startup,362	.hw_params	= jh7110_pwmdac_hw_params,363	.trigger	= jh7110_pwmdac_trigger,364};365 366static const struct snd_soc_component_driver jh7110_pwmdac_component = {367	.name		= "jh7110-pwmdac",368};369 370static struct snd_soc_dai_driver jh7110_pwmdac_dai = {371	.name		= "jh7110-pwmdac",372	.id		= 0,373	.playback = {374		.channels_min = 1,375		.channels_max = 2,376		.rates = SNDRV_PCM_RATE_8000_48000,377		.formats = SNDRV_PCM_FMTBIT_S16_LE,378	},379	.ops = &jh7110_pwmdac_dai_ops,380};381 382static int jh7110_pwmdac_runtime_suspend(struct device *dev)383{384	struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);385 386	return jh7110_pwmdac_crg_enable(pwmdac, false);387}388 389static int jh7110_pwmdac_runtime_resume(struct device *dev)390{391	struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);392 393	return jh7110_pwmdac_crg_enable(pwmdac, true);394}395 396static int jh7110_pwmdac_system_suspend(struct device *dev)397{398	struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);399 400	/* save the CTRL register value */401	pwmdac->saved_ctrl = jh7110_pwmdac_read_reg(pwmdac->base,402						    JH7110_PWMDAC_CTRL);403	return pm_runtime_force_suspend(dev);404}405 406static int jh7110_pwmdac_system_resume(struct device *dev)407{408	struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);409	int ret;410 411	ret = pm_runtime_force_resume(dev);412	if (ret)413		return ret;414 415	/* restore the CTRL register value */416	jh7110_pwmdac_write_reg(pwmdac->base, JH7110_PWMDAC_CTRL,417				pwmdac->saved_ctrl);418	return 0;419}420 421static const struct dev_pm_ops jh7110_pwmdac_pm_ops = {422	RUNTIME_PM_OPS(jh7110_pwmdac_runtime_suspend,423		       jh7110_pwmdac_runtime_resume, NULL)424	SYSTEM_SLEEP_PM_OPS(jh7110_pwmdac_system_suspend,425			    jh7110_pwmdac_system_resume)426};427 428static void jh7110_pwmdac_init_params(struct jh7110_pwmdac_dev *dev)429{430	dev->cfg.shift = PWMDAC_SHIFT_8;431	dev->cfg.duty_cycle = PWMDAC_CYCLE_CENTER;432	dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;433	dev->cfg.data_change = NO_CHANGE;434	dev->cfg.data_mode = INVERTER_DATA_MSB;435	dev->cfg.data_shift = PWMDAC_DATA_LEFT_SHIFT_BIT_0;436 437	dev->play_dma_data.addr = dev->mapbase + JH7110_PWMDAC_WDATA;438	dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;439	dev->play_dma_data.fifo_size = 1;440	dev->play_dma_data.maxburst = 16;441}442 443static int jh7110_pwmdac_probe(struct platform_device *pdev)444{445	struct jh7110_pwmdac_dev *dev;446	struct resource *res;447	int ret;448 449	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);450	if (!dev)451		return -ENOMEM;452 453	dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);454	if (IS_ERR(dev->base))455		return PTR_ERR(dev->base);456 457	dev->mapbase = res->start;458 459	dev->clks[0].id = "apb";460	dev->clks[1].id = "core";461 462	ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(dev->clks), dev->clks);463	if (ret)464		return dev_err_probe(&pdev->dev, ret,465				     "failed to get pwmdac clocks\n");466 467	dev->rst_apb = devm_reset_control_get_exclusive(&pdev->dev, NULL);468	if (IS_ERR(dev->rst_apb))469		return dev_err_probe(&pdev->dev, PTR_ERR(dev->rst_apb),470				     "failed to get pwmdac apb reset\n");471 472	jh7110_pwmdac_init_params(dev);473 474	dev->dev = &pdev->dev;475	dev_set_drvdata(&pdev->dev, dev);476	ret = devm_snd_soc_register_component(&pdev->dev,477					      &jh7110_pwmdac_component,478					      &jh7110_pwmdac_dai, 1);479	if (ret)480		return dev_err_probe(&pdev->dev, ret, "failed to register dai\n");481 482	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);483	if (ret)484		return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n");485 486	pm_runtime_enable(dev->dev);487	if (!pm_runtime_enabled(&pdev->dev)) {488		ret = jh7110_pwmdac_runtime_resume(&pdev->dev);489		if (ret)490			goto err_pm_disable;491	}492 493	return 0;494 495err_pm_disable:496	pm_runtime_disable(&pdev->dev);497 498	return ret;499}500 501static void jh7110_pwmdac_remove(struct platform_device *pdev)502{503	pm_runtime_disable(&pdev->dev);504}505 506static const struct of_device_id jh7110_pwmdac_of_match[] = {507	{ .compatible = "starfive,jh7110-pwmdac" },508	{ /* sentinel */ }509};510MODULE_DEVICE_TABLE(of, jh7110_pwmdac_of_match);511 512static struct platform_driver jh7110_pwmdac_driver = {513	.driver		= {514		.name	= "jh7110-pwmdac",515		.of_match_table = jh7110_pwmdac_of_match,516		.pm = pm_ptr(&jh7110_pwmdac_pm_ops),517	},518	.probe		= jh7110_pwmdac_probe,519	.remove		= jh7110_pwmdac_remove,520};521module_platform_driver(jh7110_pwmdac_driver);522 523MODULE_AUTHOR("Jenny Zhang");524MODULE_AUTHOR("Curry Zhang");525MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");526MODULE_AUTHOR("Hal Feng <hal.feng@starfivetech.com>");527MODULE_DESCRIPTION("StarFive JH7110 PWM-DAC driver");528MODULE_LICENSE("GPL");529