162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/***************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and 562306a36Sopenharmony_ci * Jean-Christian Hassler <jhassler@free.fr> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file is part of the Audiowerk2 ALSA driver 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci *****************************************************************************/ 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <sound/core.h> 1962306a36Sopenharmony_ci#include <sound/initval.h> 2062306a36Sopenharmony_ci#include <sound/pcm.h> 2162306a36Sopenharmony_ci#include <sound/pcm_params.h> 2262306a36Sopenharmony_ci#include <sound/control.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "saa7146.h" 2562306a36Sopenharmony_ci#include "aw2-saa7146.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciMODULE_AUTHOR("Cedric Bregardis <cedric.bregardis@free.fr>, " 2862306a36Sopenharmony_ci "Jean-Christian Hassler <jhassler@free.fr>"); 2962306a36Sopenharmony_ciMODULE_DESCRIPTION("Emagic Audiowerk 2 sound driver"); 3062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/********************************* 3362306a36Sopenharmony_ci * DEFINES 3462306a36Sopenharmony_ci ********************************/ 3562306a36Sopenharmony_ci#define CTL_ROUTE_ANALOG 0 3662306a36Sopenharmony_ci#define CTL_ROUTE_DIGITAL 1 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/********************************* 3962306a36Sopenharmony_ci * TYPEDEFS 4062306a36Sopenharmony_ci ********************************/ 4162306a36Sopenharmony_ci /* hardware definition */ 4262306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_aw2_playback_hw = { 4362306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 4462306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 4562306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), 4662306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 4762306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100, 4862306a36Sopenharmony_ci .rate_min = 44100, 4962306a36Sopenharmony_ci .rate_max = 44100, 5062306a36Sopenharmony_ci .channels_min = 2, 5162306a36Sopenharmony_ci .channels_max = 4, 5262306a36Sopenharmony_ci .buffer_bytes_max = 32768, 5362306a36Sopenharmony_ci .period_bytes_min = 4096, 5462306a36Sopenharmony_ci .period_bytes_max = 32768, 5562306a36Sopenharmony_ci .periods_min = 1, 5662306a36Sopenharmony_ci .periods_max = 1024, 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_aw2_capture_hw = { 6062306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 6162306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 6262306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), 6362306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 6462306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100, 6562306a36Sopenharmony_ci .rate_min = 44100, 6662306a36Sopenharmony_ci .rate_max = 44100, 6762306a36Sopenharmony_ci .channels_min = 2, 6862306a36Sopenharmony_ci .channels_max = 2, 6962306a36Sopenharmony_ci .buffer_bytes_max = 32768, 7062306a36Sopenharmony_ci .period_bytes_min = 4096, 7162306a36Sopenharmony_ci .period_bytes_max = 32768, 7262306a36Sopenharmony_ci .periods_min = 1, 7362306a36Sopenharmony_ci .periods_max = 1024, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct aw2_pcm_device { 7762306a36Sopenharmony_ci struct snd_pcm *pcm; 7862306a36Sopenharmony_ci unsigned int stream_number; 7962306a36Sopenharmony_ci struct aw2 *chip; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistruct aw2 { 8362306a36Sopenharmony_ci struct snd_aw2_saa7146 saa7146; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci struct pci_dev *pci; 8662306a36Sopenharmony_ci int irq; 8762306a36Sopenharmony_ci spinlock_t reg_lock; 8862306a36Sopenharmony_ci struct mutex mtx; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci unsigned long iobase_phys; 9162306a36Sopenharmony_ci void __iomem *iobase_virt; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci struct snd_card *card; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci struct aw2_pcm_device device_playback[NB_STREAM_PLAYBACK]; 9662306a36Sopenharmony_ci struct aw2_pcm_device device_capture[NB_STREAM_CAPTURE]; 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/********************************* 10062306a36Sopenharmony_ci * FUNCTION DECLARATIONS 10162306a36Sopenharmony_ci ********************************/ 10262306a36Sopenharmony_cistatic int snd_aw2_create(struct snd_card *card, struct pci_dev *pci); 10362306a36Sopenharmony_cistatic int snd_aw2_probe(struct pci_dev *pci, 10462306a36Sopenharmony_ci const struct pci_device_id *pci_id); 10562306a36Sopenharmony_cistatic int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream); 10662306a36Sopenharmony_cistatic int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream); 10762306a36Sopenharmony_cistatic int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream); 10862306a36Sopenharmony_cistatic int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream); 10962306a36Sopenharmony_cistatic int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream); 11062306a36Sopenharmony_cistatic int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream); 11162306a36Sopenharmony_cistatic int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream, 11262306a36Sopenharmony_ci int cmd); 11362306a36Sopenharmony_cistatic int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream, 11462306a36Sopenharmony_ci int cmd); 11562306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream 11662306a36Sopenharmony_ci *substream); 11762306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream 11862306a36Sopenharmony_ci *substream); 11962306a36Sopenharmony_cistatic int snd_aw2_new_pcm(struct aw2 *chip); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol, 12262306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo); 12362306a36Sopenharmony_cistatic int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol, 12462306a36Sopenharmony_ci struct snd_ctl_elem_value 12562306a36Sopenharmony_ci *ucontrol); 12662306a36Sopenharmony_cistatic int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol, 12762306a36Sopenharmony_ci struct snd_ctl_elem_value 12862306a36Sopenharmony_ci *ucontrol); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/********************************* 13162306a36Sopenharmony_ci * VARIABLES 13262306a36Sopenharmony_ci ********************************/ 13362306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 13462306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 13562306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 13862306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Audiowerk2 soundcard."); 13962306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 14062306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for the Audiowerk2 soundcard."); 14162306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 14262306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Audiowerk2 soundcard."); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic const struct pci_device_id snd_aw2_ids[] = { 14562306a36Sopenharmony_ci {PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0, 0, 14662306a36Sopenharmony_ci 0, 0, 0}, 14762306a36Sopenharmony_ci {0} 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_aw2_ids); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* pci_driver definition */ 15362306a36Sopenharmony_cistatic struct pci_driver aw2_driver = { 15462306a36Sopenharmony_ci .name = KBUILD_MODNAME, 15562306a36Sopenharmony_ci .id_table = snd_aw2_ids, 15662306a36Sopenharmony_ci .probe = snd_aw2_probe, 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cimodule_pci_driver(aw2_driver); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* operators for playback PCM alsa interface */ 16262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_aw2_playback_ops = { 16362306a36Sopenharmony_ci .open = snd_aw2_pcm_playback_open, 16462306a36Sopenharmony_ci .close = snd_aw2_pcm_playback_close, 16562306a36Sopenharmony_ci .prepare = snd_aw2_pcm_prepare_playback, 16662306a36Sopenharmony_ci .trigger = snd_aw2_pcm_trigger_playback, 16762306a36Sopenharmony_ci .pointer = snd_aw2_pcm_pointer_playback, 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* operators for capture PCM alsa interface */ 17162306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_aw2_capture_ops = { 17262306a36Sopenharmony_ci .open = snd_aw2_pcm_capture_open, 17362306a36Sopenharmony_ci .close = snd_aw2_pcm_capture_close, 17462306a36Sopenharmony_ci .prepare = snd_aw2_pcm_prepare_capture, 17562306a36Sopenharmony_ci .trigger = snd_aw2_pcm_trigger_capture, 17662306a36Sopenharmony_ci .pointer = snd_aw2_pcm_pointer_capture, 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic const struct snd_kcontrol_new aw2_control = { 18062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 18162306a36Sopenharmony_ci .name = "PCM Capture Route", 18262306a36Sopenharmony_ci .index = 0, 18362306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 18462306a36Sopenharmony_ci .private_value = 0xffff, 18562306a36Sopenharmony_ci .info = snd_aw2_control_switch_capture_info, 18662306a36Sopenharmony_ci .get = snd_aw2_control_switch_capture_get, 18762306a36Sopenharmony_ci .put = snd_aw2_control_switch_capture_put 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/********************************* 19162306a36Sopenharmony_ci * FUNCTION IMPLEMENTATIONS 19262306a36Sopenharmony_ci ********************************/ 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* component-destructor */ 19562306a36Sopenharmony_cistatic void snd_aw2_free(struct snd_card *card) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct aw2 *chip = card->private_data; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Free hardware */ 20062306a36Sopenharmony_ci snd_aw2_saa7146_free(&chip->saa7146); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci/* chip-specific constructor */ 20462306a36Sopenharmony_cistatic int snd_aw2_create(struct snd_card *card, 20562306a36Sopenharmony_ci struct pci_dev *pci) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct aw2 *chip = card->private_data; 20862306a36Sopenharmony_ci int err; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* initialize the PCI entry */ 21162306a36Sopenharmony_ci err = pcim_enable_device(pci); 21262306a36Sopenharmony_ci if (err < 0) 21362306a36Sopenharmony_ci return err; 21462306a36Sopenharmony_ci pci_set_master(pci); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* check PCI availability (32bit DMA) */ 21762306a36Sopenharmony_ci if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) { 21862306a36Sopenharmony_ci dev_err(card->dev, "Impossible to set 32bit mask DMA\n"); 21962306a36Sopenharmony_ci return -ENXIO; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* initialize the stuff */ 22362306a36Sopenharmony_ci chip->card = card; 22462306a36Sopenharmony_ci chip->pci = pci; 22562306a36Sopenharmony_ci chip->irq = -1; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* (1) PCI resource allocation */ 22862306a36Sopenharmony_ci err = pcim_iomap_regions(pci, 1 << 0, "Audiowerk2"); 22962306a36Sopenharmony_ci if (err < 0) 23062306a36Sopenharmony_ci return err; 23162306a36Sopenharmony_ci chip->iobase_phys = pci_resource_start(pci, 0); 23262306a36Sopenharmony_ci chip->iobase_virt = pcim_iomap_table(pci)[0]; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* (2) initialization of the chip hardware */ 23562306a36Sopenharmony_ci snd_aw2_saa7146_setup(&chip->saa7146, chip->iobase_virt); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (devm_request_irq(&pci->dev, pci->irq, snd_aw2_saa7146_interrupt, 23862306a36Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip)) { 23962306a36Sopenharmony_ci dev_err(card->dev, "Cannot grab irq %d\n", pci->irq); 24062306a36Sopenharmony_ci return -EBUSY; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci chip->irq = pci->irq; 24362306a36Sopenharmony_ci card->sync_irq = chip->irq; 24462306a36Sopenharmony_ci card->private_free = snd_aw2_free; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci dev_info(card->dev, 24762306a36Sopenharmony_ci "Audiowerk 2 sound card (saa7146 chipset) detected and managed\n"); 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/* constructor */ 25262306a36Sopenharmony_cistatic int snd_aw2_probe(struct pci_dev *pci, 25362306a36Sopenharmony_ci const struct pci_device_id *pci_id) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci static int dev; 25662306a36Sopenharmony_ci struct snd_card *card; 25762306a36Sopenharmony_ci struct aw2 *chip; 25862306a36Sopenharmony_ci int err; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* (1) Continue if device is not enabled, else inc dev */ 26162306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 26262306a36Sopenharmony_ci return -ENODEV; 26362306a36Sopenharmony_ci if (!enable[dev]) { 26462306a36Sopenharmony_ci dev++; 26562306a36Sopenharmony_ci return -ENOENT; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* (2) Create card instance */ 26962306a36Sopenharmony_ci err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 27062306a36Sopenharmony_ci sizeof(*chip), &card); 27162306a36Sopenharmony_ci if (err < 0) 27262306a36Sopenharmony_ci return err; 27362306a36Sopenharmony_ci chip = card->private_data; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* (3) Create main component */ 27662306a36Sopenharmony_ci err = snd_aw2_create(card, pci); 27762306a36Sopenharmony_ci if (err < 0) 27862306a36Sopenharmony_ci goto error; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* initialize mutex */ 28162306a36Sopenharmony_ci mutex_init(&chip->mtx); 28262306a36Sopenharmony_ci /* init spinlock */ 28362306a36Sopenharmony_ci spin_lock_init(&chip->reg_lock); 28462306a36Sopenharmony_ci /* (4) Define driver ID and name string */ 28562306a36Sopenharmony_ci strcpy(card->driver, "aw2"); 28662306a36Sopenharmony_ci strcpy(card->shortname, "Audiowerk2"); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci sprintf(card->longname, "%s with SAA7146 irq %i", 28962306a36Sopenharmony_ci card->shortname, chip->irq); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* (5) Create other components */ 29262306a36Sopenharmony_ci snd_aw2_new_pcm(chip); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* (6) Register card instance */ 29562306a36Sopenharmony_ci err = snd_card_register(card); 29662306a36Sopenharmony_ci if (err < 0) 29762306a36Sopenharmony_ci goto error; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* (7) Set PCI driver data */ 30062306a36Sopenharmony_ci pci_set_drvdata(pci, card); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci dev++; 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci error: 30662306a36Sopenharmony_ci snd_card_free(card); 30762306a36Sopenharmony_ci return err; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* open callback */ 31162306a36Sopenharmony_cistatic int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci dev_dbg(substream->pcm->card->dev, "Playback_open\n"); 31662306a36Sopenharmony_ci runtime->hw = snd_aw2_playback_hw; 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/* close callback */ 32162306a36Sopenharmony_cistatic int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci dev_dbg(substream->pcm->card->dev, "Capture_open\n"); 33262306a36Sopenharmony_ci runtime->hw = snd_aw2_capture_hw; 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/* close callback */ 33762306a36Sopenharmony_cistatic int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci /* TODO: something to do ? */ 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci/* prepare callback for playback */ 34462306a36Sopenharmony_cistatic int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); 34762306a36Sopenharmony_ci struct aw2 *chip = pcm_device->chip; 34862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 34962306a36Sopenharmony_ci unsigned long period_size, buffer_size; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci mutex_lock(&chip->mtx); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci period_size = snd_pcm_lib_period_bytes(substream); 35462306a36Sopenharmony_ci buffer_size = snd_pcm_lib_buffer_bytes(substream); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci snd_aw2_saa7146_pcm_init_playback(&chip->saa7146, 35762306a36Sopenharmony_ci pcm_device->stream_number, 35862306a36Sopenharmony_ci runtime->dma_addr, period_size, 35962306a36Sopenharmony_ci buffer_size); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Define Interrupt callback */ 36262306a36Sopenharmony_ci snd_aw2_saa7146_define_it_playback_callback(pcm_device->stream_number, 36362306a36Sopenharmony_ci (snd_aw2_saa7146_it_cb) 36462306a36Sopenharmony_ci snd_pcm_period_elapsed, 36562306a36Sopenharmony_ci (void *)substream); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci mutex_unlock(&chip->mtx); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/* prepare callback for capture */ 37362306a36Sopenharmony_cistatic int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); 37662306a36Sopenharmony_ci struct aw2 *chip = pcm_device->chip; 37762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 37862306a36Sopenharmony_ci unsigned long period_size, buffer_size; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci mutex_lock(&chip->mtx); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci period_size = snd_pcm_lib_period_bytes(substream); 38362306a36Sopenharmony_ci buffer_size = snd_pcm_lib_buffer_bytes(substream); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci snd_aw2_saa7146_pcm_init_capture(&chip->saa7146, 38662306a36Sopenharmony_ci pcm_device->stream_number, 38762306a36Sopenharmony_ci runtime->dma_addr, period_size, 38862306a36Sopenharmony_ci buffer_size); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Define Interrupt callback */ 39162306a36Sopenharmony_ci snd_aw2_saa7146_define_it_capture_callback(pcm_device->stream_number, 39262306a36Sopenharmony_ci (snd_aw2_saa7146_it_cb) 39362306a36Sopenharmony_ci snd_pcm_period_elapsed, 39462306a36Sopenharmony_ci (void *)substream); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci mutex_unlock(&chip->mtx); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/* playback trigger callback */ 40262306a36Sopenharmony_cistatic int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream, 40362306a36Sopenharmony_ci int cmd) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci int status = 0; 40662306a36Sopenharmony_ci struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); 40762306a36Sopenharmony_ci struct aw2 *chip = pcm_device->chip; 40862306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 40962306a36Sopenharmony_ci switch (cmd) { 41062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 41162306a36Sopenharmony_ci snd_aw2_saa7146_pcm_trigger_start_playback(&chip->saa7146, 41262306a36Sopenharmony_ci pcm_device-> 41362306a36Sopenharmony_ci stream_number); 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 41662306a36Sopenharmony_ci snd_aw2_saa7146_pcm_trigger_stop_playback(&chip->saa7146, 41762306a36Sopenharmony_ci pcm_device-> 41862306a36Sopenharmony_ci stream_number); 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci default: 42162306a36Sopenharmony_ci status = -EINVAL; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 42462306a36Sopenharmony_ci return status; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/* capture trigger callback */ 42862306a36Sopenharmony_cistatic int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream, 42962306a36Sopenharmony_ci int cmd) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci int status = 0; 43262306a36Sopenharmony_ci struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); 43362306a36Sopenharmony_ci struct aw2 *chip = pcm_device->chip; 43462306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 43562306a36Sopenharmony_ci switch (cmd) { 43662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 43762306a36Sopenharmony_ci snd_aw2_saa7146_pcm_trigger_start_capture(&chip->saa7146, 43862306a36Sopenharmony_ci pcm_device-> 43962306a36Sopenharmony_ci stream_number); 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 44262306a36Sopenharmony_ci snd_aw2_saa7146_pcm_trigger_stop_capture(&chip->saa7146, 44362306a36Sopenharmony_ci pcm_device-> 44462306a36Sopenharmony_ci stream_number); 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci default: 44762306a36Sopenharmony_ci status = -EINVAL; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 45062306a36Sopenharmony_ci return status; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* playback pointer callback */ 45462306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream 45562306a36Sopenharmony_ci *substream) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); 45862306a36Sopenharmony_ci struct aw2 *chip = pcm_device->chip; 45962306a36Sopenharmony_ci unsigned int current_ptr; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* get the current hardware pointer */ 46262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 46362306a36Sopenharmony_ci current_ptr = 46462306a36Sopenharmony_ci snd_aw2_saa7146_get_hw_ptr_playback(&chip->saa7146, 46562306a36Sopenharmony_ci pcm_device->stream_number, 46662306a36Sopenharmony_ci runtime->dma_area, 46762306a36Sopenharmony_ci runtime->buffer_size); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, current_ptr); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* capture pointer callback */ 47362306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream 47462306a36Sopenharmony_ci *substream) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); 47762306a36Sopenharmony_ci struct aw2 *chip = pcm_device->chip; 47862306a36Sopenharmony_ci unsigned int current_ptr; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* get the current hardware pointer */ 48162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 48262306a36Sopenharmony_ci current_ptr = 48362306a36Sopenharmony_ci snd_aw2_saa7146_get_hw_ptr_capture(&chip->saa7146, 48462306a36Sopenharmony_ci pcm_device->stream_number, 48562306a36Sopenharmony_ci runtime->dma_area, 48662306a36Sopenharmony_ci runtime->buffer_size); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, current_ptr); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/* create a pcm device */ 49262306a36Sopenharmony_cistatic int snd_aw2_new_pcm(struct aw2 *chip) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct snd_pcm *pcm_playback_ana; 49562306a36Sopenharmony_ci struct snd_pcm *pcm_playback_num; 49662306a36Sopenharmony_ci struct snd_pcm *pcm_capture; 49762306a36Sopenharmony_ci struct aw2_pcm_device *pcm_device; 49862306a36Sopenharmony_ci int err = 0; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Create new Alsa PCM device */ 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci err = snd_pcm_new(chip->card, "Audiowerk2 analog playback", 0, 1, 0, 50362306a36Sopenharmony_ci &pcm_playback_ana); 50462306a36Sopenharmony_ci if (err < 0) { 50562306a36Sopenharmony_ci dev_err(chip->card->dev, "snd_pcm_new error (0x%X)\n", err); 50662306a36Sopenharmony_ci return err; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Creation ok */ 51062306a36Sopenharmony_ci pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA]; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Set PCM device name */ 51362306a36Sopenharmony_ci strcpy(pcm_playback_ana->name, "Analog playback"); 51462306a36Sopenharmony_ci /* Associate private data to PCM device */ 51562306a36Sopenharmony_ci pcm_playback_ana->private_data = pcm_device; 51662306a36Sopenharmony_ci /* set operators of PCM device */ 51762306a36Sopenharmony_ci snd_pcm_set_ops(pcm_playback_ana, SNDRV_PCM_STREAM_PLAYBACK, 51862306a36Sopenharmony_ci &snd_aw2_playback_ops); 51962306a36Sopenharmony_ci /* store PCM device */ 52062306a36Sopenharmony_ci pcm_device->pcm = pcm_playback_ana; 52162306a36Sopenharmony_ci /* give base chip pointer to our internal pcm device 52262306a36Sopenharmony_ci structure */ 52362306a36Sopenharmony_ci pcm_device->chip = chip; 52462306a36Sopenharmony_ci /* Give stream number to PCM device */ 52562306a36Sopenharmony_ci pcm_device->stream_number = NUM_STREAM_PLAYBACK_ANA; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* pre-allocation of buffers */ 52862306a36Sopenharmony_ci /* Preallocate continuous pages. */ 52962306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm_playback_ana, 53062306a36Sopenharmony_ci SNDRV_DMA_TYPE_DEV, 53162306a36Sopenharmony_ci &chip->pci->dev, 53262306a36Sopenharmony_ci 64 * 1024, 64 * 1024); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci err = snd_pcm_new(chip->card, "Audiowerk2 digital playback", 1, 1, 0, 53562306a36Sopenharmony_ci &pcm_playback_num); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (err < 0) { 53862306a36Sopenharmony_ci dev_err(chip->card->dev, "snd_pcm_new error (0x%X)\n", err); 53962306a36Sopenharmony_ci return err; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci /* Creation ok */ 54262306a36Sopenharmony_ci pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG]; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* Set PCM device name */ 54562306a36Sopenharmony_ci strcpy(pcm_playback_num->name, "Digital playback"); 54662306a36Sopenharmony_ci /* Associate private data to PCM device */ 54762306a36Sopenharmony_ci pcm_playback_num->private_data = pcm_device; 54862306a36Sopenharmony_ci /* set operators of PCM device */ 54962306a36Sopenharmony_ci snd_pcm_set_ops(pcm_playback_num, SNDRV_PCM_STREAM_PLAYBACK, 55062306a36Sopenharmony_ci &snd_aw2_playback_ops); 55162306a36Sopenharmony_ci /* store PCM device */ 55262306a36Sopenharmony_ci pcm_device->pcm = pcm_playback_num; 55362306a36Sopenharmony_ci /* give base chip pointer to our internal pcm device 55462306a36Sopenharmony_ci structure */ 55562306a36Sopenharmony_ci pcm_device->chip = chip; 55662306a36Sopenharmony_ci /* Give stream number to PCM device */ 55762306a36Sopenharmony_ci pcm_device->stream_number = NUM_STREAM_PLAYBACK_DIG; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* pre-allocation of buffers */ 56062306a36Sopenharmony_ci /* Preallocate continuous pages. */ 56162306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm_playback_num, 56262306a36Sopenharmony_ci SNDRV_DMA_TYPE_DEV, 56362306a36Sopenharmony_ci &chip->pci->dev, 56462306a36Sopenharmony_ci 64 * 1024, 64 * 1024); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci err = snd_pcm_new(chip->card, "Audiowerk2 capture", 2, 0, 1, 56762306a36Sopenharmony_ci &pcm_capture); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (err < 0) { 57062306a36Sopenharmony_ci dev_err(chip->card->dev, "snd_pcm_new error (0x%X)\n", err); 57162306a36Sopenharmony_ci return err; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Creation ok */ 57562306a36Sopenharmony_ci pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA]; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* Set PCM device name */ 57862306a36Sopenharmony_ci strcpy(pcm_capture->name, "Capture"); 57962306a36Sopenharmony_ci /* Associate private data to PCM device */ 58062306a36Sopenharmony_ci pcm_capture->private_data = pcm_device; 58162306a36Sopenharmony_ci /* set operators of PCM device */ 58262306a36Sopenharmony_ci snd_pcm_set_ops(pcm_capture, SNDRV_PCM_STREAM_CAPTURE, 58362306a36Sopenharmony_ci &snd_aw2_capture_ops); 58462306a36Sopenharmony_ci /* store PCM device */ 58562306a36Sopenharmony_ci pcm_device->pcm = pcm_capture; 58662306a36Sopenharmony_ci /* give base chip pointer to our internal pcm device 58762306a36Sopenharmony_ci structure */ 58862306a36Sopenharmony_ci pcm_device->chip = chip; 58962306a36Sopenharmony_ci /* Give stream number to PCM device */ 59062306a36Sopenharmony_ci pcm_device->stream_number = NUM_STREAM_CAPTURE_ANA; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* pre-allocation of buffers */ 59362306a36Sopenharmony_ci /* Preallocate continuous pages. */ 59462306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm_capture, 59562306a36Sopenharmony_ci SNDRV_DMA_TYPE_DEV, 59662306a36Sopenharmony_ci &chip->pci->dev, 59762306a36Sopenharmony_ci 64 * 1024, 64 * 1024); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* Create control */ 60062306a36Sopenharmony_ci err = snd_ctl_add(chip->card, snd_ctl_new1(&aw2_control, chip)); 60162306a36Sopenharmony_ci if (err < 0) { 60262306a36Sopenharmony_ci dev_err(chip->card->dev, "snd_ctl_add error (0x%X)\n", err); 60362306a36Sopenharmony_ci return err; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol, 61062306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci static const char * const texts[2] = { 61362306a36Sopenharmony_ci "Analog", "Digital" 61462306a36Sopenharmony_ci }; 61562306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, texts); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol, 61962306a36Sopenharmony_ci struct snd_ctl_elem_value 62062306a36Sopenharmony_ci *ucontrol) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct aw2 *chip = snd_kcontrol_chip(kcontrol); 62362306a36Sopenharmony_ci if (snd_aw2_saa7146_is_using_digital_input(&chip->saa7146)) 62462306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = CTL_ROUTE_DIGITAL; 62562306a36Sopenharmony_ci else 62662306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = CTL_ROUTE_ANALOG; 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol, 63162306a36Sopenharmony_ci struct snd_ctl_elem_value 63262306a36Sopenharmony_ci *ucontrol) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct aw2 *chip = snd_kcontrol_chip(kcontrol); 63562306a36Sopenharmony_ci int changed = 0; 63662306a36Sopenharmony_ci int is_disgital = 63762306a36Sopenharmony_ci snd_aw2_saa7146_is_using_digital_input(&chip->saa7146); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (((ucontrol->value.integer.value[0] == CTL_ROUTE_DIGITAL) 64062306a36Sopenharmony_ci && !is_disgital) 64162306a36Sopenharmony_ci || ((ucontrol->value.integer.value[0] == CTL_ROUTE_ANALOG) 64262306a36Sopenharmony_ci && is_disgital)) { 64362306a36Sopenharmony_ci snd_aw2_saa7146_use_digital_input(&chip->saa7146, !is_disgital); 64462306a36Sopenharmony_ci changed = 1; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci return changed; 64762306a36Sopenharmony_ci} 648