18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for Atmel AC97C
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2005-2009 Atmel Corporation
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/atmel_pdc.h>
128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/mutex.h>
188c2ecf20Sopenharmony_ci#include <linux/types.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/of.h>
218c2ecf20Sopenharmony_ci#include <linux/of_device.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <sound/core.h>
248c2ecf20Sopenharmony_ci#include <sound/initval.h>
258c2ecf20Sopenharmony_ci#include <sound/pcm.h>
268c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
278c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h>
288c2ecf20Sopenharmony_ci#include <sound/memalloc.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "ac97c.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* Serialize access to opened variable */
338c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(opened_mutex);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct atmel_ac97c {
368c2ecf20Sopenharmony_ci	struct clk			*pclk;
378c2ecf20Sopenharmony_ci	struct platform_device		*pdev;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	struct snd_pcm_substream	*playback_substream;
408c2ecf20Sopenharmony_ci	struct snd_pcm_substream	*capture_substream;
418c2ecf20Sopenharmony_ci	struct snd_card			*card;
428c2ecf20Sopenharmony_ci	struct snd_pcm			*pcm;
438c2ecf20Sopenharmony_ci	struct snd_ac97			*ac97;
448c2ecf20Sopenharmony_ci	struct snd_ac97_bus		*ac97_bus;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	u64				cur_format;
478c2ecf20Sopenharmony_ci	unsigned int			cur_rate;
488c2ecf20Sopenharmony_ci	int				playback_period, capture_period;
498c2ecf20Sopenharmony_ci	/* Serialize access to opened variable */
508c2ecf20Sopenharmony_ci	spinlock_t			lock;
518c2ecf20Sopenharmony_ci	void __iomem			*regs;
528c2ecf20Sopenharmony_ci	int				irq;
538c2ecf20Sopenharmony_ci	int				opened;
548c2ecf20Sopenharmony_ci	struct gpio_desc		*reset_pin;
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define get_chip(card) ((struct atmel_ac97c *)(card)->private_data)
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define ac97c_writel(chip, reg, val)			\
608c2ecf20Sopenharmony_ci	__raw_writel((val), (chip)->regs + AC97C_##reg)
618c2ecf20Sopenharmony_ci#define ac97c_readl(chip, reg)				\
628c2ecf20Sopenharmony_ci	__raw_readl((chip)->regs + AC97C_##reg)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware atmel_ac97c_hw = {
658c2ecf20Sopenharmony_ci	.info			= (SNDRV_PCM_INFO_MMAP
668c2ecf20Sopenharmony_ci				  | SNDRV_PCM_INFO_MMAP_VALID
678c2ecf20Sopenharmony_ci				  | SNDRV_PCM_INFO_INTERLEAVED
688c2ecf20Sopenharmony_ci				  | SNDRV_PCM_INFO_BLOCK_TRANSFER
698c2ecf20Sopenharmony_ci				  | SNDRV_PCM_INFO_JOINT_DUPLEX
708c2ecf20Sopenharmony_ci				  | SNDRV_PCM_INFO_RESUME
718c2ecf20Sopenharmony_ci				  | SNDRV_PCM_INFO_PAUSE),
728c2ecf20Sopenharmony_ci	.formats		= (SNDRV_PCM_FMTBIT_S16_BE
738c2ecf20Sopenharmony_ci				  | SNDRV_PCM_FMTBIT_S16_LE),
748c2ecf20Sopenharmony_ci	.rates			= (SNDRV_PCM_RATE_CONTINUOUS),
758c2ecf20Sopenharmony_ci	.rate_min		= 4000,
768c2ecf20Sopenharmony_ci	.rate_max		= 48000,
778c2ecf20Sopenharmony_ci	.channels_min		= 1,
788c2ecf20Sopenharmony_ci	.channels_max		= 2,
798c2ecf20Sopenharmony_ci	.buffer_bytes_max	= 2 * 2 * 64 * 2048,
808c2ecf20Sopenharmony_ci	.period_bytes_min	= 4096,
818c2ecf20Sopenharmony_ci	.period_bytes_max	= 4096,
828c2ecf20Sopenharmony_ci	.periods_min		= 6,
838c2ecf20Sopenharmony_ci	.periods_max		= 64,
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int atmel_ac97c_playback_open(struct snd_pcm_substream *substream)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
898c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	mutex_lock(&opened_mutex);
928c2ecf20Sopenharmony_ci	chip->opened++;
938c2ecf20Sopenharmony_ci	runtime->hw = atmel_ac97c_hw;
948c2ecf20Sopenharmony_ci	if (chip->cur_rate) {
958c2ecf20Sopenharmony_ci		runtime->hw.rate_min = chip->cur_rate;
968c2ecf20Sopenharmony_ci		runtime->hw.rate_max = chip->cur_rate;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci	if (chip->cur_format)
998c2ecf20Sopenharmony_ci		runtime->hw.formats = pcm_format_to_bits(chip->cur_format);
1008c2ecf20Sopenharmony_ci	mutex_unlock(&opened_mutex);
1018c2ecf20Sopenharmony_ci	chip->playback_substream = substream;
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int atmel_ac97c_capture_open(struct snd_pcm_substream *substream)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
1088c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	mutex_lock(&opened_mutex);
1118c2ecf20Sopenharmony_ci	chip->opened++;
1128c2ecf20Sopenharmony_ci	runtime->hw = atmel_ac97c_hw;
1138c2ecf20Sopenharmony_ci	if (chip->cur_rate) {
1148c2ecf20Sopenharmony_ci		runtime->hw.rate_min = chip->cur_rate;
1158c2ecf20Sopenharmony_ci		runtime->hw.rate_max = chip->cur_rate;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	if (chip->cur_format)
1188c2ecf20Sopenharmony_ci		runtime->hw.formats = pcm_format_to_bits(chip->cur_format);
1198c2ecf20Sopenharmony_ci	mutex_unlock(&opened_mutex);
1208c2ecf20Sopenharmony_ci	chip->capture_substream = substream;
1218c2ecf20Sopenharmony_ci	return 0;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int atmel_ac97c_playback_close(struct snd_pcm_substream *substream)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	mutex_lock(&opened_mutex);
1298c2ecf20Sopenharmony_ci	chip->opened--;
1308c2ecf20Sopenharmony_ci	if (!chip->opened) {
1318c2ecf20Sopenharmony_ci		chip->cur_rate = 0;
1328c2ecf20Sopenharmony_ci		chip->cur_format = 0;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	mutex_unlock(&opened_mutex);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	chip->playback_substream = NULL;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return 0;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic int atmel_ac97c_capture_close(struct snd_pcm_substream *substream)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	mutex_lock(&opened_mutex);
1468c2ecf20Sopenharmony_ci	chip->opened--;
1478c2ecf20Sopenharmony_ci	if (!chip->opened) {
1488c2ecf20Sopenharmony_ci		chip->cur_rate = 0;
1498c2ecf20Sopenharmony_ci		chip->cur_format = 0;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci	mutex_unlock(&opened_mutex);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	chip->capture_substream = NULL;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	return 0;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream,
1598c2ecf20Sopenharmony_ci		struct snd_pcm_hw_params *hw_params)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* Set restrictions to params. */
1648c2ecf20Sopenharmony_ci	mutex_lock(&opened_mutex);
1658c2ecf20Sopenharmony_ci	chip->cur_rate = params_rate(hw_params);
1668c2ecf20Sopenharmony_ci	chip->cur_format = params_format(hw_params);
1678c2ecf20Sopenharmony_ci	mutex_unlock(&opened_mutex);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return 0;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
1738c2ecf20Sopenharmony_ci		struct snd_pcm_hw_params *hw_params)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* Set restrictions to params. */
1788c2ecf20Sopenharmony_ci	mutex_lock(&opened_mutex);
1798c2ecf20Sopenharmony_ci	chip->cur_rate = params_rate(hw_params);
1808c2ecf20Sopenharmony_ci	chip->cur_format = params_format(hw_params);
1818c2ecf20Sopenharmony_ci	mutex_unlock(&opened_mutex);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return 0;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
1898c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1908c2ecf20Sopenharmony_ci	int block_size = frames_to_bytes(runtime, runtime->period_size);
1918c2ecf20Sopenharmony_ci	unsigned long word = ac97c_readl(chip, OCA);
1928c2ecf20Sopenharmony_ci	int retval;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	chip->playback_period = 0;
1958c2ecf20Sopenharmony_ci	word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* assign channels to AC97C channel A */
1988c2ecf20Sopenharmony_ci	switch (runtime->channels) {
1998c2ecf20Sopenharmony_ci	case 1:
2008c2ecf20Sopenharmony_ci		word |= AC97C_CH_ASSIGN(PCM_LEFT, A);
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci	case 2:
2038c2ecf20Sopenharmony_ci		word |= AC97C_CH_ASSIGN(PCM_LEFT, A)
2048c2ecf20Sopenharmony_ci			| AC97C_CH_ASSIGN(PCM_RIGHT, A);
2058c2ecf20Sopenharmony_ci		break;
2068c2ecf20Sopenharmony_ci	default:
2078c2ecf20Sopenharmony_ci		/* TODO: support more than two channels */
2088c2ecf20Sopenharmony_ci		return -EINVAL;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci	ac97c_writel(chip, OCA, word);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* configure sample format and size */
2138c2ecf20Sopenharmony_ci	word = ac97c_readl(chip, CAMR);
2148c2ecf20Sopenharmony_ci	if (chip->opened <= 1)
2158c2ecf20Sopenharmony_ci		word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
2168c2ecf20Sopenharmony_ci	else
2178c2ecf20Sopenharmony_ci		word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	switch (runtime->format) {
2208c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
2218c2ecf20Sopenharmony_ci		break;
2228c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_BE:
2238c2ecf20Sopenharmony_ci		word &= ~(AC97C_CMR_CEM_LITTLE);
2248c2ecf20Sopenharmony_ci		break;
2258c2ecf20Sopenharmony_ci	default:
2268c2ecf20Sopenharmony_ci		word = ac97c_readl(chip, OCA);
2278c2ecf20Sopenharmony_ci		word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
2288c2ecf20Sopenharmony_ci		ac97c_writel(chip, OCA, word);
2298c2ecf20Sopenharmony_ci		return -EINVAL;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* Enable underrun interrupt on channel A */
2338c2ecf20Sopenharmony_ci	word |= AC97C_CSR_UNRUN;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	ac97c_writel(chip, CAMR, word);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* Enable channel A event interrupt */
2388c2ecf20Sopenharmony_ci	word = ac97c_readl(chip, IMR);
2398c2ecf20Sopenharmony_ci	word |= AC97C_SR_CAEVT;
2408c2ecf20Sopenharmony_ci	ac97c_writel(chip, IER, word);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* set variable rate if needed */
2438c2ecf20Sopenharmony_ci	if (runtime->rate != 48000) {
2448c2ecf20Sopenharmony_ci		word = ac97c_readl(chip, MR);
2458c2ecf20Sopenharmony_ci		word |= AC97C_MR_VRA;
2468c2ecf20Sopenharmony_ci		ac97c_writel(chip, MR, word);
2478c2ecf20Sopenharmony_ci	} else {
2488c2ecf20Sopenharmony_ci		word = ac97c_readl(chip, MR);
2498c2ecf20Sopenharmony_ci		word &= ~(AC97C_MR_VRA);
2508c2ecf20Sopenharmony_ci		ac97c_writel(chip, MR, word);
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE,
2548c2ecf20Sopenharmony_ci			runtime->rate);
2558c2ecf20Sopenharmony_ci	if (retval)
2568c2ecf20Sopenharmony_ci		dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
2578c2ecf20Sopenharmony_ci				runtime->rate);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* Initialize and start the PDC */
2608c2ecf20Sopenharmony_ci	writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR);
2618c2ecf20Sopenharmony_ci	writel(block_size / 2, chip->regs + ATMEL_PDC_TCR);
2628c2ecf20Sopenharmony_ci	writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_TNPR);
2638c2ecf20Sopenharmony_ci	writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return retval;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
2718c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2728c2ecf20Sopenharmony_ci	int block_size = frames_to_bytes(runtime, runtime->period_size);
2738c2ecf20Sopenharmony_ci	unsigned long word = ac97c_readl(chip, ICA);
2748c2ecf20Sopenharmony_ci	int retval;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	chip->capture_period = 0;
2778c2ecf20Sopenharmony_ci	word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* assign channels to AC97C channel A */
2808c2ecf20Sopenharmony_ci	switch (runtime->channels) {
2818c2ecf20Sopenharmony_ci	case 1:
2828c2ecf20Sopenharmony_ci		word |= AC97C_CH_ASSIGN(PCM_LEFT, A);
2838c2ecf20Sopenharmony_ci		break;
2848c2ecf20Sopenharmony_ci	case 2:
2858c2ecf20Sopenharmony_ci		word |= AC97C_CH_ASSIGN(PCM_LEFT, A)
2868c2ecf20Sopenharmony_ci			| AC97C_CH_ASSIGN(PCM_RIGHT, A);
2878c2ecf20Sopenharmony_ci		break;
2888c2ecf20Sopenharmony_ci	default:
2898c2ecf20Sopenharmony_ci		/* TODO: support more than two channels */
2908c2ecf20Sopenharmony_ci		return -EINVAL;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci	ac97c_writel(chip, ICA, word);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* configure sample format and size */
2958c2ecf20Sopenharmony_ci	word = ac97c_readl(chip, CAMR);
2968c2ecf20Sopenharmony_ci	if (chip->opened <= 1)
2978c2ecf20Sopenharmony_ci		word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
2988c2ecf20Sopenharmony_ci	else
2998c2ecf20Sopenharmony_ci		word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	switch (runtime->format) {
3028c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
3038c2ecf20Sopenharmony_ci		break;
3048c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_BE:
3058c2ecf20Sopenharmony_ci		word &= ~(AC97C_CMR_CEM_LITTLE);
3068c2ecf20Sopenharmony_ci		break;
3078c2ecf20Sopenharmony_ci	default:
3088c2ecf20Sopenharmony_ci		word = ac97c_readl(chip, ICA);
3098c2ecf20Sopenharmony_ci		word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
3108c2ecf20Sopenharmony_ci		ac97c_writel(chip, ICA, word);
3118c2ecf20Sopenharmony_ci		return -EINVAL;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* Enable overrun interrupt on channel A */
3158c2ecf20Sopenharmony_ci	word |= AC97C_CSR_OVRUN;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	ac97c_writel(chip, CAMR, word);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* Enable channel A event interrupt */
3208c2ecf20Sopenharmony_ci	word = ac97c_readl(chip, IMR);
3218c2ecf20Sopenharmony_ci	word |= AC97C_SR_CAEVT;
3228c2ecf20Sopenharmony_ci	ac97c_writel(chip, IER, word);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/* set variable rate if needed */
3258c2ecf20Sopenharmony_ci	if (runtime->rate != 48000) {
3268c2ecf20Sopenharmony_ci		word = ac97c_readl(chip, MR);
3278c2ecf20Sopenharmony_ci		word |= AC97C_MR_VRA;
3288c2ecf20Sopenharmony_ci		ac97c_writel(chip, MR, word);
3298c2ecf20Sopenharmony_ci	} else {
3308c2ecf20Sopenharmony_ci		word = ac97c_readl(chip, MR);
3318c2ecf20Sopenharmony_ci		word &= ~(AC97C_MR_VRA);
3328c2ecf20Sopenharmony_ci		ac97c_writel(chip, MR, word);
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE,
3368c2ecf20Sopenharmony_ci			runtime->rate);
3378c2ecf20Sopenharmony_ci	if (retval)
3388c2ecf20Sopenharmony_ci		dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
3398c2ecf20Sopenharmony_ci				runtime->rate);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* Initialize and start the PDC */
3428c2ecf20Sopenharmony_ci	writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR);
3438c2ecf20Sopenharmony_ci	writel(block_size / 2, chip->regs + ATMEL_PDC_RCR);
3448c2ecf20Sopenharmony_ci	writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_RNPR);
3458c2ecf20Sopenharmony_ci	writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return retval;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int
3518c2ecf20Sopenharmony_ciatmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
3548c2ecf20Sopenharmony_ci	unsigned long camr, ptcr = 0;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	camr = ac97c_readl(chip, CAMR);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	switch (cmd) {
3598c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3608c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
3618c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
3628c2ecf20Sopenharmony_ci		ptcr = ATMEL_PDC_TXTEN;
3638c2ecf20Sopenharmony_ci		camr |= AC97C_CMR_CENA | AC97C_CSR_ENDTX;
3648c2ecf20Sopenharmony_ci		break;
3658c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3668c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
3678c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
3688c2ecf20Sopenharmony_ci		ptcr |= ATMEL_PDC_TXTDIS;
3698c2ecf20Sopenharmony_ci		if (chip->opened <= 1)
3708c2ecf20Sopenharmony_ci			camr &= ~AC97C_CMR_CENA;
3718c2ecf20Sopenharmony_ci		break;
3728c2ecf20Sopenharmony_ci	default:
3738c2ecf20Sopenharmony_ci		return -EINVAL;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	ac97c_writel(chip, CAMR, camr);
3778c2ecf20Sopenharmony_ci	writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
3788c2ecf20Sopenharmony_ci	return 0;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic int
3828c2ecf20Sopenharmony_ciatmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
3858c2ecf20Sopenharmony_ci	unsigned long camr, ptcr = 0;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	camr = ac97c_readl(chip, CAMR);
3888c2ecf20Sopenharmony_ci	ptcr = readl(chip->regs + ATMEL_PDC_PTSR);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	switch (cmd) {
3918c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3928c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
3938c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
3948c2ecf20Sopenharmony_ci		ptcr = ATMEL_PDC_RXTEN;
3958c2ecf20Sopenharmony_ci		camr |= AC97C_CMR_CENA | AC97C_CSR_ENDRX;
3968c2ecf20Sopenharmony_ci		break;
3978c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3988c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
3998c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
4008c2ecf20Sopenharmony_ci		ptcr |= ATMEL_PDC_RXTDIS;
4018c2ecf20Sopenharmony_ci		if (chip->opened <= 1)
4028c2ecf20Sopenharmony_ci			camr &= ~AC97C_CMR_CENA;
4038c2ecf20Sopenharmony_ci		break;
4048c2ecf20Sopenharmony_ci	default:
4058c2ecf20Sopenharmony_ci		return -EINVAL;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	ac97c_writel(chip, CAMR, camr);
4098c2ecf20Sopenharmony_ci	writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
4148c2ecf20Sopenharmony_ciatmel_ac97c_playback_pointer(struct snd_pcm_substream *substream)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct atmel_ac97c	*chip = snd_pcm_substream_chip(substream);
4178c2ecf20Sopenharmony_ci	struct snd_pcm_runtime	*runtime = substream->runtime;
4188c2ecf20Sopenharmony_ci	snd_pcm_uframes_t	frames;
4198c2ecf20Sopenharmony_ci	unsigned long		bytes;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	bytes = readl(chip->regs + ATMEL_PDC_TPR);
4228c2ecf20Sopenharmony_ci	bytes -= runtime->dma_addr;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	frames = bytes_to_frames(runtime, bytes);
4258c2ecf20Sopenharmony_ci	if (frames >= runtime->buffer_size)
4268c2ecf20Sopenharmony_ci		frames -= runtime->buffer_size;
4278c2ecf20Sopenharmony_ci	return frames;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
4318c2ecf20Sopenharmony_ciatmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct atmel_ac97c	*chip = snd_pcm_substream_chip(substream);
4348c2ecf20Sopenharmony_ci	struct snd_pcm_runtime	*runtime = substream->runtime;
4358c2ecf20Sopenharmony_ci	snd_pcm_uframes_t	frames;
4368c2ecf20Sopenharmony_ci	unsigned long		bytes;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	bytes = readl(chip->regs + ATMEL_PDC_RPR);
4398c2ecf20Sopenharmony_ci	bytes -= runtime->dma_addr;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	frames = bytes_to_frames(runtime, bytes);
4428c2ecf20Sopenharmony_ci	if (frames >= runtime->buffer_size)
4438c2ecf20Sopenharmony_ci		frames -= runtime->buffer_size;
4448c2ecf20Sopenharmony_ci	return frames;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops atmel_ac97_playback_ops = {
4488c2ecf20Sopenharmony_ci	.open		= atmel_ac97c_playback_open,
4498c2ecf20Sopenharmony_ci	.close		= atmel_ac97c_playback_close,
4508c2ecf20Sopenharmony_ci	.hw_params	= atmel_ac97c_playback_hw_params,
4518c2ecf20Sopenharmony_ci	.prepare	= atmel_ac97c_playback_prepare,
4528c2ecf20Sopenharmony_ci	.trigger	= atmel_ac97c_playback_trigger,
4538c2ecf20Sopenharmony_ci	.pointer	= atmel_ac97c_playback_pointer,
4548c2ecf20Sopenharmony_ci};
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops atmel_ac97_capture_ops = {
4578c2ecf20Sopenharmony_ci	.open		= atmel_ac97c_capture_open,
4588c2ecf20Sopenharmony_ci	.close		= atmel_ac97c_capture_close,
4598c2ecf20Sopenharmony_ci	.hw_params	= atmel_ac97c_capture_hw_params,
4608c2ecf20Sopenharmony_ci	.prepare	= atmel_ac97c_capture_prepare,
4618c2ecf20Sopenharmony_ci	.trigger	= atmel_ac97c_capture_trigger,
4628c2ecf20Sopenharmony_ci	.pointer	= atmel_ac97c_capture_pointer,
4638c2ecf20Sopenharmony_ci};
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct atmel_ac97c	*chip  = (struct atmel_ac97c *)dev;
4688c2ecf20Sopenharmony_ci	irqreturn_t		retval = IRQ_NONE;
4698c2ecf20Sopenharmony_ci	u32			sr     = ac97c_readl(chip, SR);
4708c2ecf20Sopenharmony_ci	u32			casr   = ac97c_readl(chip, CASR);
4718c2ecf20Sopenharmony_ci	u32			cosr   = ac97c_readl(chip, COSR);
4728c2ecf20Sopenharmony_ci	u32			camr   = ac97c_readl(chip, CAMR);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (sr & AC97C_SR_CAEVT) {
4758c2ecf20Sopenharmony_ci		struct snd_pcm_runtime *runtime;
4768c2ecf20Sopenharmony_ci		int offset, next_period, block_size;
4778c2ecf20Sopenharmony_ci		dev_dbg(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
4788c2ecf20Sopenharmony_ci			(casr & AC97C_CSR_OVRUN)   ? " OVRUN"   : "",
4798c2ecf20Sopenharmony_ci			(casr & AC97C_CSR_RXRDY)   ? " RXRDY"   : "",
4808c2ecf20Sopenharmony_ci			(casr & AC97C_CSR_UNRUN)   ? " UNRUN"   : "",
4818c2ecf20Sopenharmony_ci			(casr & AC97C_CSR_TXEMPTY) ? " TXEMPTY" : "",
4828c2ecf20Sopenharmony_ci			(casr & AC97C_CSR_TXRDY)   ? " TXRDY"   : "",
4838c2ecf20Sopenharmony_ci			!casr                      ? " NONE"    : "");
4848c2ecf20Sopenharmony_ci		if ((casr & camr) & AC97C_CSR_ENDTX) {
4858c2ecf20Sopenharmony_ci			runtime = chip->playback_substream->runtime;
4868c2ecf20Sopenharmony_ci			block_size = frames_to_bytes(runtime, runtime->period_size);
4878c2ecf20Sopenharmony_ci			chip->playback_period++;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci			if (chip->playback_period == runtime->periods)
4908c2ecf20Sopenharmony_ci				chip->playback_period = 0;
4918c2ecf20Sopenharmony_ci			next_period = chip->playback_period + 1;
4928c2ecf20Sopenharmony_ci			if (next_period == runtime->periods)
4938c2ecf20Sopenharmony_ci				next_period = 0;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci			offset = block_size * next_period;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci			writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_TNPR);
4988c2ecf20Sopenharmony_ci			writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci			snd_pcm_period_elapsed(chip->playback_substream);
5018c2ecf20Sopenharmony_ci		}
5028c2ecf20Sopenharmony_ci		if ((casr & camr) & AC97C_CSR_ENDRX) {
5038c2ecf20Sopenharmony_ci			runtime = chip->capture_substream->runtime;
5048c2ecf20Sopenharmony_ci			block_size = frames_to_bytes(runtime, runtime->period_size);
5058c2ecf20Sopenharmony_ci			chip->capture_period++;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci			if (chip->capture_period == runtime->periods)
5088c2ecf20Sopenharmony_ci				chip->capture_period = 0;
5098c2ecf20Sopenharmony_ci			next_period = chip->capture_period + 1;
5108c2ecf20Sopenharmony_ci			if (next_period == runtime->periods)
5118c2ecf20Sopenharmony_ci				next_period = 0;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci			offset = block_size * next_period;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci			writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_RNPR);
5168c2ecf20Sopenharmony_ci			writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
5178c2ecf20Sopenharmony_ci			snd_pcm_period_elapsed(chip->capture_substream);
5188c2ecf20Sopenharmony_ci		}
5198c2ecf20Sopenharmony_ci		retval = IRQ_HANDLED;
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (sr & AC97C_SR_COEVT) {
5238c2ecf20Sopenharmony_ci		dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n",
5248c2ecf20Sopenharmony_ci			 (cosr & AC97C_CSR_OVRUN)   ? " OVRUN"   : "",
5258c2ecf20Sopenharmony_ci			 (cosr & AC97C_CSR_RXRDY)   ? " RXRDY"   : "",
5268c2ecf20Sopenharmony_ci			 (cosr & AC97C_CSR_TXEMPTY) ? " TXEMPTY" : "",
5278c2ecf20Sopenharmony_ci			 (cosr & AC97C_CSR_TXRDY)   ? " TXRDY"   : "",
5288c2ecf20Sopenharmony_ci			 !cosr                      ? " NONE"    : "");
5298c2ecf20Sopenharmony_ci		retval = IRQ_HANDLED;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (retval == IRQ_NONE) {
5338c2ecf20Sopenharmony_ci		dev_err(&chip->pdev->dev, "spurious interrupt sr 0x%08x "
5348c2ecf20Sopenharmony_ci				"casr 0x%08x cosr 0x%08x\n", sr, casr, cosr);
5358c2ecf20Sopenharmony_ci	}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	return retval;
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic const struct ac97_pcm at91_ac97_pcm_defs[] = {
5418c2ecf20Sopenharmony_ci	/* Playback */
5428c2ecf20Sopenharmony_ci	{
5438c2ecf20Sopenharmony_ci		.exclusive = 1,
5448c2ecf20Sopenharmony_ci		.r = { {
5458c2ecf20Sopenharmony_ci			.slots = ((1 << AC97_SLOT_PCM_LEFT)
5468c2ecf20Sopenharmony_ci				  | (1 << AC97_SLOT_PCM_RIGHT)),
5478c2ecf20Sopenharmony_ci		} },
5488c2ecf20Sopenharmony_ci	},
5498c2ecf20Sopenharmony_ci	/* PCM in */
5508c2ecf20Sopenharmony_ci	{
5518c2ecf20Sopenharmony_ci		.stream = 1,
5528c2ecf20Sopenharmony_ci		.exclusive = 1,
5538c2ecf20Sopenharmony_ci		.r = { {
5548c2ecf20Sopenharmony_ci			.slots = ((1 << AC97_SLOT_PCM_LEFT)
5558c2ecf20Sopenharmony_ci					| (1 << AC97_SLOT_PCM_RIGHT)),
5568c2ecf20Sopenharmony_ci		} }
5578c2ecf20Sopenharmony_ci	},
5588c2ecf20Sopenharmony_ci	/* Mic in */
5598c2ecf20Sopenharmony_ci	{
5608c2ecf20Sopenharmony_ci		.stream = 1,
5618c2ecf20Sopenharmony_ci		.exclusive = 1,
5628c2ecf20Sopenharmony_ci		.r = { {
5638c2ecf20Sopenharmony_ci			.slots = (1<<AC97_SLOT_MIC),
5648c2ecf20Sopenharmony_ci		} }
5658c2ecf20Sopenharmony_ci	},
5668c2ecf20Sopenharmony_ci};
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic int atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	struct snd_pcm		*pcm;
5718c2ecf20Sopenharmony_ci	struct snd_pcm_hardware	hw = atmel_ac97c_hw;
5728c2ecf20Sopenharmony_ci	int			retval;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	retval = snd_ac97_pcm_assign(chip->ac97_bus,
5758c2ecf20Sopenharmony_ci				     ARRAY_SIZE(at91_ac97_pcm_defs),
5768c2ecf20Sopenharmony_ci				     at91_ac97_pcm_defs);
5778c2ecf20Sopenharmony_ci	if (retval)
5788c2ecf20Sopenharmony_ci		return retval;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	retval = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm);
5818c2ecf20Sopenharmony_ci	if (retval)
5828c2ecf20Sopenharmony_ci		return retval;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &atmel_ac97_capture_ops);
5858c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_ac97_playback_ops);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
5888c2ecf20Sopenharmony_ci			&chip->pdev->dev, hw.periods_min * hw.period_bytes_min,
5898c2ecf20Sopenharmony_ci			hw.buffer_bytes_max);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	pcm->private_data = chip;
5928c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
5938c2ecf20Sopenharmony_ci	strcpy(pcm->name, chip->card->shortname);
5948c2ecf20Sopenharmony_ci	chip->pcm = pcm;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	return 0;
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic int atmel_ac97c_mixer_new(struct atmel_ac97c *chip)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	struct snd_ac97_template template;
6028c2ecf20Sopenharmony_ci	memset(&template, 0, sizeof(template));
6038c2ecf20Sopenharmony_ci	template.private_data = chip;
6048c2ecf20Sopenharmony_ci	return snd_ac97_mixer(chip->ac97_bus, &template, &chip->ac97);
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic void atmel_ac97c_write(struct snd_ac97 *ac97, unsigned short reg,
6088c2ecf20Sopenharmony_ci		unsigned short val)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = get_chip(ac97);
6118c2ecf20Sopenharmony_ci	unsigned long word;
6128c2ecf20Sopenharmony_ci	int timeout = 40;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	word = (reg & 0x7f) << 16 | val;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	do {
6178c2ecf20Sopenharmony_ci		if (ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) {
6188c2ecf20Sopenharmony_ci			ac97c_writel(chip, COTHR, word);
6198c2ecf20Sopenharmony_ci			return;
6208c2ecf20Sopenharmony_ci		}
6218c2ecf20Sopenharmony_ci		udelay(1);
6228c2ecf20Sopenharmony_ci	} while (--timeout);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	dev_dbg(&chip->pdev->dev, "codec write timeout\n");
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic unsigned short atmel_ac97c_read(struct snd_ac97 *ac97,
6288c2ecf20Sopenharmony_ci		unsigned short reg)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = get_chip(ac97);
6318c2ecf20Sopenharmony_ci	unsigned long word;
6328c2ecf20Sopenharmony_ci	int timeout = 40;
6338c2ecf20Sopenharmony_ci	int write = 10;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	word = (0x80 | (reg & 0x7f)) << 16;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0)
6388c2ecf20Sopenharmony_ci		ac97c_readl(chip, CORHR);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ciretry_write:
6418c2ecf20Sopenharmony_ci	timeout = 40;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	do {
6448c2ecf20Sopenharmony_ci		if ((ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) != 0) {
6458c2ecf20Sopenharmony_ci			ac97c_writel(chip, COTHR, word);
6468c2ecf20Sopenharmony_ci			goto read_reg;
6478c2ecf20Sopenharmony_ci		}
6488c2ecf20Sopenharmony_ci		udelay(10);
6498c2ecf20Sopenharmony_ci	} while (--timeout);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (!--write)
6528c2ecf20Sopenharmony_ci		goto timed_out;
6538c2ecf20Sopenharmony_ci	goto retry_write;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ciread_reg:
6568c2ecf20Sopenharmony_ci	do {
6578c2ecf20Sopenharmony_ci		if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) {
6588c2ecf20Sopenharmony_ci			unsigned short val = ac97c_readl(chip, CORHR);
6598c2ecf20Sopenharmony_ci			return val;
6608c2ecf20Sopenharmony_ci		}
6618c2ecf20Sopenharmony_ci		udelay(10);
6628c2ecf20Sopenharmony_ci	} while (--timeout);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	if (!--write)
6658c2ecf20Sopenharmony_ci		goto timed_out;
6668c2ecf20Sopenharmony_ci	goto retry_write;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_citimed_out:
6698c2ecf20Sopenharmony_ci	dev_dbg(&chip->pdev->dev, "codec read timeout\n");
6708c2ecf20Sopenharmony_ci	return 0xffff;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_cistatic void atmel_ac97c_reset(struct atmel_ac97c *chip)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	ac97c_writel(chip, MR,   0);
6768c2ecf20Sopenharmony_ci	ac97c_writel(chip, MR,   AC97C_MR_ENA);
6778c2ecf20Sopenharmony_ci	ac97c_writel(chip, CAMR, 0);
6788c2ecf20Sopenharmony_ci	ac97c_writel(chip, COMR, 0);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (!IS_ERR(chip->reset_pin)) {
6818c2ecf20Sopenharmony_ci		gpiod_set_value(chip->reset_pin, 0);
6828c2ecf20Sopenharmony_ci		/* AC97 v2.2 specifications says minimum 1 us. */
6838c2ecf20Sopenharmony_ci		udelay(2);
6848c2ecf20Sopenharmony_ci		gpiod_set_value(chip->reset_pin, 1);
6858c2ecf20Sopenharmony_ci	} else {
6868c2ecf20Sopenharmony_ci		ac97c_writel(chip, MR, AC97C_MR_WRST | AC97C_MR_ENA);
6878c2ecf20Sopenharmony_ci		udelay(2);
6888c2ecf20Sopenharmony_ci		ac97c_writel(chip, MR, AC97C_MR_ENA);
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_ac97c_dt_ids[] = {
6938c2ecf20Sopenharmony_ci	{ .compatible = "atmel,at91sam9263-ac97c", },
6948c2ecf20Sopenharmony_ci	{ }
6958c2ecf20Sopenharmony_ci};
6968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_ac97c_dt_ids);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic int atmel_ac97c_probe(struct platform_device *pdev)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct device			*dev = &pdev->dev;
7018c2ecf20Sopenharmony_ci	struct snd_card			*card;
7028c2ecf20Sopenharmony_ci	struct atmel_ac97c		*chip;
7038c2ecf20Sopenharmony_ci	struct resource			*regs;
7048c2ecf20Sopenharmony_ci	struct clk			*pclk;
7058c2ecf20Sopenharmony_ci	static const struct snd_ac97_bus_ops	ops = {
7068c2ecf20Sopenharmony_ci		.write	= atmel_ac97c_write,
7078c2ecf20Sopenharmony_ci		.read	= atmel_ac97c_read,
7088c2ecf20Sopenharmony_ci	};
7098c2ecf20Sopenharmony_ci	int				retval;
7108c2ecf20Sopenharmony_ci	int				irq;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
7138c2ecf20Sopenharmony_ci	if (!regs) {
7148c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "no memory resource\n");
7158c2ecf20Sopenharmony_ci		return -ENXIO;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
7198c2ecf20Sopenharmony_ci	if (irq < 0) {
7208c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "could not get irq: %d\n", irq);
7218c2ecf20Sopenharmony_ci		return irq;
7228c2ecf20Sopenharmony_ci	}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	pclk = clk_get(&pdev->dev, "ac97_clk");
7258c2ecf20Sopenharmony_ci	if (IS_ERR(pclk)) {
7268c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "no peripheral clock\n");
7278c2ecf20Sopenharmony_ci		return PTR_ERR(pclk);
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci	retval = clk_prepare_enable(pclk);
7308c2ecf20Sopenharmony_ci	if (retval)
7318c2ecf20Sopenharmony_ci		goto err_prepare_enable;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1,
7348c2ecf20Sopenharmony_ci			      SNDRV_DEFAULT_STR1, THIS_MODULE,
7358c2ecf20Sopenharmony_ci			      sizeof(struct atmel_ac97c), &card);
7368c2ecf20Sopenharmony_ci	if (retval) {
7378c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "could not create sound card device\n");
7388c2ecf20Sopenharmony_ci		goto err_snd_card_new;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	chip = get_chip(card);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip);
7448c2ecf20Sopenharmony_ci	if (retval) {
7458c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "unable to request irq %d\n", irq);
7468c2ecf20Sopenharmony_ci		goto err_request_irq;
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci	chip->irq = irq;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	spin_lock_init(&chip->lock);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	strcpy(card->driver, "Atmel AC97C");
7538c2ecf20Sopenharmony_ci	strcpy(card->shortname, "Atmel AC97C");
7548c2ecf20Sopenharmony_ci	sprintf(card->longname, "Atmel AC97 controller");
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	chip->card = card;
7578c2ecf20Sopenharmony_ci	chip->pclk = pclk;
7588c2ecf20Sopenharmony_ci	chip->pdev = pdev;
7598c2ecf20Sopenharmony_ci	chip->regs = ioremap(regs->start, resource_size(regs));
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	if (!chip->regs) {
7628c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "could not remap register memory\n");
7638c2ecf20Sopenharmony_ci		retval = -ENOMEM;
7648c2ecf20Sopenharmony_ci		goto err_ioremap;
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	chip->reset_pin = devm_gpiod_get_index(dev, "ac97", 2, GPIOD_OUT_HIGH);
7688c2ecf20Sopenharmony_ci	if (IS_ERR(chip->reset_pin))
7698c2ecf20Sopenharmony_ci		dev_dbg(dev, "reset pin not available\n");
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	atmel_ac97c_reset(chip);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	/* Enable overrun interrupt from codec channel */
7748c2ecf20Sopenharmony_ci	ac97c_writel(chip, COMR, AC97C_CSR_OVRUN);
7758c2ecf20Sopenharmony_ci	ac97c_writel(chip, IER, ac97c_readl(chip, IMR) | AC97C_SR_COEVT);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
7788c2ecf20Sopenharmony_ci	if (retval) {
7798c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "could not register on ac97 bus\n");
7808c2ecf20Sopenharmony_ci		goto err_ac97_bus;
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	retval = atmel_ac97c_mixer_new(chip);
7848c2ecf20Sopenharmony_ci	if (retval) {
7858c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "could not register ac97 mixer\n");
7868c2ecf20Sopenharmony_ci		goto err_ac97_bus;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	retval = atmel_ac97c_pcm_new(chip);
7908c2ecf20Sopenharmony_ci	if (retval) {
7918c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "could not register ac97 pcm device\n");
7928c2ecf20Sopenharmony_ci		goto err_ac97_bus;
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	retval = snd_card_register(card);
7968c2ecf20Sopenharmony_ci	if (retval) {
7978c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "could not register sound card\n");
7988c2ecf20Sopenharmony_ci		goto err_ac97_bus;
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, card);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p, irq = %d\n",
8048c2ecf20Sopenharmony_ci			chip->regs, irq);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	return 0;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cierr_ac97_bus:
8098c2ecf20Sopenharmony_ci	iounmap(chip->regs);
8108c2ecf20Sopenharmony_cierr_ioremap:
8118c2ecf20Sopenharmony_ci	free_irq(irq, chip);
8128c2ecf20Sopenharmony_cierr_request_irq:
8138c2ecf20Sopenharmony_ci	snd_card_free(card);
8148c2ecf20Sopenharmony_cierr_snd_card_new:
8158c2ecf20Sopenharmony_ci	clk_disable_unprepare(pclk);
8168c2ecf20Sopenharmony_cierr_prepare_enable:
8178c2ecf20Sopenharmony_ci	clk_put(pclk);
8188c2ecf20Sopenharmony_ci	return retval;
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
8228c2ecf20Sopenharmony_cistatic int atmel_ac97c_suspend(struct device *pdev)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(pdev);
8258c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = card->private_data;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	clk_disable_unprepare(chip->pclk);
8288c2ecf20Sopenharmony_ci	return 0;
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_cistatic int atmel_ac97c_resume(struct device *pdev)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(pdev);
8348c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = card->private_data;
8358c2ecf20Sopenharmony_ci	int ret = clk_prepare_enable(chip->pclk);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	return ret;
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(atmel_ac97c_pm, atmel_ac97c_suspend, atmel_ac97c_resume);
8418c2ecf20Sopenharmony_ci#define ATMEL_AC97C_PM_OPS	&atmel_ac97c_pm
8428c2ecf20Sopenharmony_ci#else
8438c2ecf20Sopenharmony_ci#define ATMEL_AC97C_PM_OPS	NULL
8448c2ecf20Sopenharmony_ci#endif
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cistatic int atmel_ac97c_remove(struct platform_device *pdev)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct snd_card *card = platform_get_drvdata(pdev);
8498c2ecf20Sopenharmony_ci	struct atmel_ac97c *chip = get_chip(card);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	ac97c_writel(chip, CAMR, 0);
8528c2ecf20Sopenharmony_ci	ac97c_writel(chip, COMR, 0);
8538c2ecf20Sopenharmony_ci	ac97c_writel(chip, MR,   0);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	clk_disable_unprepare(chip->pclk);
8568c2ecf20Sopenharmony_ci	clk_put(chip->pclk);
8578c2ecf20Sopenharmony_ci	iounmap(chip->regs);
8588c2ecf20Sopenharmony_ci	free_irq(chip->irq, chip);
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	snd_card_free(card);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	return 0;
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic struct platform_driver atmel_ac97c_driver = {
8668c2ecf20Sopenharmony_ci	.probe		= atmel_ac97c_probe,
8678c2ecf20Sopenharmony_ci	.remove		= atmel_ac97c_remove,
8688c2ecf20Sopenharmony_ci	.driver		= {
8698c2ecf20Sopenharmony_ci		.name	= "atmel_ac97c",
8708c2ecf20Sopenharmony_ci		.pm	= ATMEL_AC97C_PM_OPS,
8718c2ecf20Sopenharmony_ci		.of_match_table = atmel_ac97c_dt_ids,
8728c2ecf20Sopenharmony_ci	},
8738c2ecf20Sopenharmony_ci};
8748c2ecf20Sopenharmony_cimodule_platform_driver(atmel_ac97c_driver);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
8778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Atmel AC97 controller");
8788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
879