diff options
Diffstat (limited to 'sound')
27 files changed, 904 insertions, 342 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index faedb1481b2..8f312fa6c28 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -313,9 +313,22 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; snd_pcm_sframes_t hdelta, delta; unsigned long jdelta; + unsigned long curr_jiffies; + struct timespec curr_tstamp; old_hw_ptr = runtime->status->hw_ptr; + + /* + * group pointer, time and jiffies reads to allow for more + * accurate correlations/corrections. + * The values are stored at the end of this routine after + * corrections for hw_ptr position + */ pos = substream->ops->pointer(substream); + curr_jiffies = jiffies; + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) + snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); + if (pos == SNDRV_PCM_POS_XRUN) { xrun(substream); return -EPIPE; @@ -343,7 +356,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, delta = runtime->hw_ptr_interrupt + runtime->period_size; if (delta > new_hw_ptr) { /* check for double acknowledged interrupts */ - hdelta = jiffies - runtime->hw_ptr_jiffies; + hdelta = curr_jiffies - runtime->hw_ptr_jiffies; if (hdelta > runtime->hw_ptr_buffer_jiffies/2) { hw_base += runtime->buffer_size; if (hw_base >= runtime->boundary) @@ -388,7 +401,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, * Without regular period interrupts, we have to check * the elapsed time to detect xruns. */ - jdelta = jiffies - runtime->hw_ptr_jiffies; + jdelta = curr_jiffies - runtime->hw_ptr_jiffies; if (jdelta < runtime->hw_ptr_buffer_jiffies / 2) goto no_delta_check; hdelta = jdelta - delta * HZ / runtime->rate; @@ -430,7 +443,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, if (hdelta < runtime->delay) goto no_jiffies_check; hdelta -= runtime->delay; - jdelta = jiffies - runtime->hw_ptr_jiffies; + jdelta = curr_jiffies - runtime->hw_ptr_jiffies; if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { delta = jdelta / (((runtime->period_size * HZ) / runtime->rate) @@ -492,9 +505,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, } runtime->hw_ptr_base = hw_base; runtime->status->hw_ptr = new_hw_ptr; - runtime->hw_ptr_jiffies = jiffies; + runtime->hw_ptr_jiffies = curr_jiffies; if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) - snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); + runtime->status->tstamp = curr_tstamp; return snd_pcm_update_state(substream, runtime); } diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 76294f2ae47..645cb0ba429 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -84,7 +84,7 @@ static int pcr_modify(struct cmp_connection *c, return 0; io_error: - cmp_error(c, "transaction failed: %s\n", rcode_string(rcode)); + cmp_error(c, "transaction failed: %s\n", fw_rcode_string(rcode)); return -EIO; bus_reset: diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index 4750cea2210..14eb4149837 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -14,32 +14,6 @@ #define ERROR_RETRY_DELAY_MS 5 /** - * rcode_string - convert a firewire result code to a string - * @rcode: the result - */ -const char *rcode_string(unsigned int rcode) -{ - static const char *const names[] = { - [RCODE_COMPLETE] = "complete", - [RCODE_CONFLICT_ERROR] = "conflict error", - [RCODE_DATA_ERROR] = "data error", - [RCODE_TYPE_ERROR] = "type error", - [RCODE_ADDRESS_ERROR] = "address error", - [RCODE_SEND_ERROR] = "send error", - [RCODE_CANCELLED] = "cancelled", - [RCODE_BUSY] = "busy", - [RCODE_GENERATION] = "generation", - [RCODE_NO_ACK] = "no ack", - }; - - if (rcode < ARRAY_SIZE(names) && names[rcode]) - return names[rcode]; - else - return "unknown"; -} -EXPORT_SYMBOL(rcode_string); - -/** * snd_fw_transaction - send a request and wait for its completion * @unit: the driver's unit on the target device * @tcode: the transaction code @@ -71,7 +45,7 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode, if (rcode_is_permanent_error(rcode) || ++tries >= 3) { dev_err(&unit->device, "transaction failed: %s\n", - rcode_string(rcode)); + fw_rcode_string(rcode)); return -EIO; } diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h index 064f3fd9ab0..aef301476ea 100644 --- a/sound/firewire/lib.h +++ b/sound/firewire/lib.h @@ -8,7 +8,6 @@ struct fw_unit; int snd_fw_transaction(struct fw_unit *unit, int tcode, u64 offset, void *buffer, size_t length); -const char *rcode_string(unsigned int rcode); /* returns true if retrying the transaction would not make sense */ static inline bool rcode_is_permanent_error(int rcode) diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index a63faec5e7f..582aace20ea 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -375,6 +375,9 @@ int snd_tea575x_init(struct snd_tea575x *tea) tea->vd.v4l2_dev = tea->v4l2_dev; tea->vd.ctrl_handler = &tea->ctrl_handler; set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags); + /* disable hw_freq_seek if we can't use it */ + if (tea->cannot_read_data) + v4l2_disable_ioctl(&tea->vd, VIDIOC_S_HW_FREQ_SEEK); v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index eb09a334832..41ca803a1ff 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2239,24 +2239,50 @@ void snd_hda_ctls_clear(struct hda_codec *codec) /* pseudo device locking * toggle card->shutdown to allow/disallow the device access (as a hack) */ -static int hda_lock_devices(struct snd_card *card) +int snd_hda_lock_devices(struct hda_bus *bus) { + struct snd_card *card = bus->card; + struct hda_codec *codec; + spin_lock(&card->files_lock); - if (card->shutdown) { - spin_unlock(&card->files_lock); - return -EINVAL; - } + if (card->shutdown) + goto err_unlock; card->shutdown = 1; + if (!list_empty(&card->ctl_files)) + goto err_clear; + + list_for_each_entry(codec, &bus->codec_list, list) { + int pcm; + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + struct hda_pcm *cpcm = &codec->pcm_info[pcm]; + if (!cpcm->pcm) + continue; + if (cpcm->pcm->streams[0].substream_opened || + cpcm->pcm->streams[1].substream_opened) + goto err_clear; + } + } spin_unlock(&card->files_lock); return 0; + + err_clear: + card->shutdown = 0; + err_unlock: + spin_unlock(&card->files_lock); + return -EINVAL; } +EXPORT_SYMBOL_HDA(snd_hda_lock_devices); -static void hda_unlock_devices(struct snd_card *card) +void snd_hda_unlock_devices(struct hda_bus *bus) { + struct snd_card *card = bus->card; + + card = bus->card; spin_lock(&card->files_lock); card->shutdown = 0; spin_unlock(&card->files_lock); } +EXPORT_SYMBOL_HDA(snd_hda_unlock_devices); /** * snd_hda_codec_reset - Clear all objects assigned to the codec @@ -2270,26 +2296,12 @@ static void hda_unlock_devices(struct snd_card *card) */ int snd_hda_codec_reset(struct hda_codec *codec) { - struct snd_card *card = codec->bus->card; - int i, pcm; + struct hda_bus *bus = codec->bus; + struct snd_card *card = bus->card; + int i; - if (hda_lock_devices(card) < 0) - return -EBUSY; - /* check whether the codec isn't used by any mixer or PCM streams */ - if (!list_empty(&card->ctl_files)) { - hda_unlock_devices(card); + if (snd_hda_lock_devices(bus) < 0) return -EBUSY; - } - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - struct hda_pcm *cpcm = &codec->pcm_info[pcm]; - if (!cpcm->pcm) - continue; - if (cpcm->pcm->streams[0].substream_opened || - cpcm->pcm->streams[1].substream_opened) { - hda_unlock_devices(card); - return -EBUSY; - } - } /* OK, let it free */ @@ -2298,7 +2310,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) codec->power_on = 0; codec->power_transition = 0; codec->power_jiffies = jiffies; - flush_workqueue(codec->bus->workq); + flush_workqueue(bus->workq); #endif snd_hda_ctls_clear(codec); /* relase PCMs */ @@ -2306,7 +2318,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) if (codec->pcm_info[i].pcm) { snd_device_free(card, codec->pcm_info[i].pcm); clear_bit(codec->pcm_info[i].device, - codec->bus->pcm_dev_bits); + bus->pcm_dev_bits); } } if (codec->patch_ops.free) @@ -2331,7 +2343,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) codec->owner = NULL; /* allow device access again */ - hda_unlock_devices(card); + snd_hda_unlock_devices(bus); return 0; } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 54b52819fb4..4fc3960c859 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -1023,6 +1023,9 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state, bool eapd_workaround); +int snd_hda_lock_devices(struct hda_bus *bus); +void snd_hda_unlock_devices(struct hda_bus *bus); + /* * power management */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4ab8102f87e..2b6392be451 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -53,6 +53,8 @@ #endif #include <sound/core.h> #include <sound/initval.h> +#include <linux/vgaarb.h> +#include <linux/vga_switcheroo.h> #include "hda_codec.h" @@ -175,6 +177,13 @@ MODULE_DESCRIPTION("Intel HDA driver"); #define SFX "hda-intel: " #endif +#if defined(CONFIG_PM) && defined(CONFIG_VGA_SWITCHEROO) +#ifdef CONFIG_SND_HDA_CODEC_HDMI +#define SUPPORT_VGA_SWITCHEROO +#endif +#endif + + /* * registers */ @@ -472,6 +481,12 @@ struct azx { unsigned int probing :1; /* codec probing phase */ unsigned int snoop:1; unsigned int align_buffer_size:1; + unsigned int region_requested:1; + + /* VGA-switcheroo setup */ + unsigned int use_vga_switcheroo:1; + unsigned int init_failed:1; /* delayed init failed */ + unsigned int disabled:1; /* disabled by VGA-switcher */ /* for debugging */ unsigned int last_cmd[AZX_MAX_CODECS]; @@ -538,7 +553,20 @@ enum { #define AZX_DCAPS_PRESET_CTHDA \ (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY) -static char *driver_short_names[] __devinitdata = { +/* + * VGA-switcher support + */ +#ifdef SUPPORT_VGA_SWITCHEROO +#define DELAYED_INIT_MARK +#define DELAYED_INITDATA_MARK +#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo) +#else +#define DELAYED_INIT_MARK __devinit +#define DELAYED_INITDATA_MARK __devinitdata +#define use_vga_switcheroo(chip) 0 +#endif + +static char *driver_short_names[] DELAYED_INITDATA_MARK = { [AZX_DRIVER_ICH] = "HDA Intel", [AZX_DRIVER_PCH] = "HDA Intel PCH", [AZX_DRIVER_SCH] = "HDA Intel MID", @@ -959,6 +987,8 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val) { struct azx *chip = bus->private_data; + if (chip->disabled) + return 0; chip->last_cmd[azx_command_addr(val)] = val; if (chip->single_cmd) return azx_single_send_cmd(bus, val); @@ -971,6 +1001,8 @@ static unsigned int azx_get_response(struct hda_bus *bus, unsigned int addr) { struct azx *chip = bus->private_data; + if (chip->disabled) + return 0; if (chip->single_cmd) return azx_single_get_response(bus, addr); else @@ -1236,6 +1268,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) spin_lock(&chip->reg_lock); + if (chip->disabled) { + spin_unlock(&chip->reg_lock); + return IRQ_NONE; + } + status = azx_readl(chip, INTSTS); if (status == 0) { spin_unlock(&chip->reg_lock); @@ -1521,12 +1558,12 @@ static void azx_bus_reset(struct hda_bus *bus) */ /* number of codec slots for each chipset: 0 = default slots (i.e. 4) */ -static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = { +static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] DELAYED_INITDATA_MARK = { [AZX_DRIVER_NVIDIA] = 8, [AZX_DRIVER_TERA] = 1, }; -static int __devinit azx_codec_create(struct azx *chip, const char *model) +static int DELAYED_INIT_MARK azx_codec_create(struct azx *chip, const char *model) { struct hda_bus_template bus_temp; int c, codecs, err; @@ -2444,6 +2481,105 @@ static void azx_notifier_unregister(struct azx *chip) unregister_reboot_notifier(&chip->reboot_notifier); } +static int DELAYED_INIT_MARK azx_first_init(struct azx *chip); +static int DELAYED_INIT_MARK azx_probe_continue(struct azx *chip); + +static struct pci_dev __devinit *get_bound_vga(struct pci_dev *pci); + +#ifdef SUPPORT_VGA_SWITCHEROO +static void azx_vs_set_state(struct pci_dev *pci, + enum vga_switcheroo_state state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct azx *chip = card->private_data; + bool disabled; + + if (chip->init_failed) + return; + + disabled = (state == VGA_SWITCHEROO_OFF); + if (chip->disabled == disabled) + return; + + if (!chip->bus) { + chip->disabled = disabled; + if (!disabled) { + snd_printk(KERN_INFO SFX + "%s: Start delayed initialization\n", + pci_name(chip->pci)); + if (azx_first_init(chip) < 0 || + azx_probe_continue(chip) < 0) { + snd_printk(KERN_ERR SFX + "%s: initialization error\n", + pci_name(chip->pci)); + chip->init_failed = true; + } + } + } else { + snd_printk(KERN_INFO SFX + "%s %s via VGA-switcheroo\n", + disabled ? "Disabling" : "Enabling", + pci_name(chip->pci)); + if (disabled) { + azx_suspend(pci, PMSG_FREEZE); + chip->disabled = true; + snd_hda_lock_devices(chip->bus); + } else { + snd_hda_unlock_devices(chip->bus); + chip->disabled = false; + azx_resume(pci); + } + } +} + +static bool azx_vs_can_switch(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct azx *chip = card->private_data; + + if (chip->init_failed) + return false; + if (chip->disabled || !chip->bus) + return true; + if (snd_hda_lock_devices(chip->bus)) + return false; + snd_hda_unlock_devices(chip->bus); + return true; +} + +static void __devinit init_vga_switcheroo(struct azx *chip) +{ + struct pci_dev *p = get_bound_vga(chip->pci); + if (p) { + snd_printk(KERN_INFO SFX + "%s: Handle VGA-switcheroo audio client\n", + pci_name(chip->pci)); + chip->use_vga_switcheroo = 1; + pci_dev_put(p); + } +} + +static const struct vga_switcheroo_client_ops azx_vs_ops = { + .set_gpu_state = azx_vs_set_state, + .can_switch = azx_vs_can_switch, +}; + +static int __devinit register_vga_switcheroo(struct azx *chip) +{ + if (!chip->use_vga_switcheroo) + return 0; + /* FIXME: currently only handling DIS controller + * is there any machine with two switchable HDMI audio controllers? + */ + return vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, + VGA_SWITCHEROO_DIS, + chip->bus != NULL); +} +#else +#define init_vga_switcheroo(chip) /* NOP */ +#define register_vga_switcheroo(chip) 0 +#endif /* SUPPORT_VGA_SWITCHER */ + /* * destructor */ @@ -2453,6 +2589,12 @@ static int azx_free(struct azx *chip) azx_notifier_unregister(chip); + if (use_vga_switcheroo(chip)) { + if (chip->disabled && chip->bus) + snd_hda_unlock_devices(chip->bus); + vga_switcheroo_unregister_client(chip->pci); + } + if (chip->initialized) { azx_clear_irq_pending(chip); for (i = 0; i < chip->num_streams; i++) @@ -2482,7 +2624,8 @@ static int azx_free(struct azx *chip) mark_pages_wc(chip, &chip->posbuf, false); snd_dma_free_pages(&chip->posbuf); } - pci_release_regions(chip->pci); + if (chip->region_requested) + pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip->azx_dev); kfree(chip); @@ -2496,6 +2639,45 @@ static int azx_dev_free(struct snd_device *device) } /* + * Check of disabled HDMI controller by vga-switcheroo + */ +static struct pci_dev __devinit *get_bound_vga(struct pci_dev *pci) +{ + struct pci_dev *p; + + /* check only discrete GPU */ + switch (pci->vendor) { + case PCI_VENDOR_ID_ATI: + case PCI_VENDOR_ID_AMD: + case PCI_VENDOR_ID_NVIDIA: + if (pci->devfn == 1) { + p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus), + pci->bus->number, 0); + if (p) { + if ((p->class >> 8) == PCI_CLASS_DISPLAY_VGA) + return p; + pci_dev_put(p); + } + } + break; + } + return NULL; +} + +static bool __devinit check_hdmi_disabled(struct pci_dev *pci) +{ + bool vga_inactive = false; + struct pci_dev *p = get_bound_vga(pci); + + if (p) { + if (vga_default_device() && p != vga_default_device()) + vga_inactive = true; + pci_dev_put(p); + } + return vga_inactive; +} + +/* * white/black-listing for position_fix */ static struct snd_pci_quirk position_fix_list[] __devinitdata = { @@ -2672,12 +2854,11 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, int dev, unsigned int driver_caps, struct azx **rchip) { - struct azx *chip; - int i, err; - unsigned short gcap; static struct snd_device_ops ops = { .dev_free = azx_dev_free, }; + struct azx *chip; + int err; *rchip = NULL; @@ -2703,6 +2884,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->dev_index = dev; INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work); INIT_LIST_HEAD(&chip->pcm_list); + init_vga_switcheroo(chip); chip->position_fix[0] = chip->position_fix[1] = check_position_fix(chip, position_fix[dev]); @@ -2730,6 +2912,53 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, } } + if (check_hdmi_disabled(pci)) { + snd_printk(KERN_INFO SFX "VGA controller for %s is disabled\n", + pci_name(pci)); + if (use_vga_switcheroo(chip)) { + snd_printk(KERN_INFO SFX "Delaying initialization\n"); + chip->disabled = true; + goto ok; + } + kfree(chip); + pci_disable_device(pci); + return -ENXIO; + } + + err = azx_first_init(chip); + if (err < 0) { + azx_free(chip); + return err; + } + + ok: + err = register_vga_switcheroo(chip); + if (err < 0) { + snd_printk(KERN_ERR SFX + "Error registering VGA-switcheroo client\n"); + azx_free(chip); + return err; + } + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + snd_printk(KERN_ERR SFX "Error creating device [card]!\n"); + azx_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static int DELAYED_INIT_MARK azx_first_init(struct azx *chip) +{ + int dev = chip->dev_index; + struct pci_dev *pci = chip->pci; + struct snd_card *card = chip->card; + int i, err; + unsigned short gcap; + #if BITS_PER_LONG != 64 /* Fix up base address on ULI M5461 */ if (chip->driver_type == AZX_DRIVER_ULI) { @@ -2741,28 +2970,23 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, #endif err = pci_request_regions(pci, "ICH HD audio"); - if (err < 0) { - kfree(chip); - pci_disable_device(pci); + if (err < 0) return err; - } + chip->region_requested = 1; chip->addr = pci_resource_start(pci, 0); chip->remap_addr = pci_ioremap_bar(pci, 0); if (chip->remap_addr == NULL) { snd_printk(KERN_ERR SFX "ioremap error\n"); - err = -ENXIO; - goto errout; + return -ENXIO; } if (chip->msi) if (pci_enable_msi(pci) < 0) chip->msi = 0; - if (azx_acquire_irq(chip, 0) < 0) { - err = -EBUSY; - goto errout; - } + if (azx_acquire_irq(chip, 0) < 0) + return -EBUSY; pci_set_master(pci); synchronize_irq(chip->irq); @@ -2841,7 +3065,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, GFP_KERNEL); if (!chip->azx_dev) { snd_printk(KERN_ERR SFX "cannot malloc azx_dev\n"); - goto errout; + return -ENOMEM; } for (i = 0; i < chip->num_streams; i++) { @@ -2851,7 +3075,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, BDL_SIZE, &chip->azx_dev[i].bdl); if (err < 0) { snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); - goto errout; + return -ENOMEM; } mark_pages_wc(chip, &chip->azx_dev[i].bdl, true); } @@ -2861,13 +3085,13 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->num_streams * 8, &chip->posbuf); if (err < 0) { snd_printk(KERN_ERR SFX "cannot allocate posbuf\n"); - goto errout; + return -ENOMEM; } mark_pages_wc(chip, &chip->posbuf, true); /* allocate CORB/RIRB */ err = azx_alloc_cmd_io(chip); if (err < 0) - goto errout; + return err; /* initialize streams */ azx_init_stream(chip); @@ -2879,14 +3103,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, /* codec detection */ if (!chip->codec_mask) { snd_printk(KERN_ERR SFX "no codecs found!\n"); - err = -ENODEV; - goto errout; - } - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); - if (err <0) { - snd_printk(KERN_ERR SFX "Error creating device [card]!\n"); - goto errout; + return -ENODEV; } strcpy(card->driver, "HDA-Intel"); @@ -2896,12 +3113,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, "%s at 0x%lx irq %i", card->shortname, chip->addr, chip->irq); - *rchip = chip; return 0; - - errout: - azx_free(chip); - return err; } static void power_down_all_codecs(struct azx *chip) @@ -2946,6 +3158,27 @@ static int __devinit azx_probe(struct pci_dev *pci, goto out_free; card->private_data = chip; + if (!chip->disabled) { + err = azx_probe_continue(chip); + if (err < 0) + goto out_free; + } + + pci_set_drvdata(pci, card); + + dev++; + return 0; + +out_free: + snd_card_free(card); + return err; +} + +static int DELAYED_INIT_MARK azx_probe_continue(struct azx *chip) +{ + int dev = chip->dev_index; + int err; + #ifdef CONFIG_SND_HDA_INPUT_BEEP chip->beep_mode = beep_mode[dev]; #endif @@ -2979,25 +3212,26 @@ static int __devinit azx_probe(struct pci_dev *pci, if (err < 0) goto out_free; - err = snd_card_register(card); + err = snd_card_register(chip->card); if (err < 0) goto out_free; - pci_set_drvdata(pci, card); chip->running = 1; power_down_all_codecs(chip); azx_notifier_register(chip); - dev++; - return err; + return 0; + out_free: - snd_card_free(card); + chip->init_failed = 1; return err; } static void __devexit azx_remove(struct pci_dev *pci) { - snd_card_free(pci_get_drvdata(pci)); + struct snd_card *card = pci_get_drvdata(pci); + if (card) + snd_card_free(card); pci_set_drvdata(pci, NULL); } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ff71dcef08e..224410e8e9e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2368,6 +2368,7 @@ static struct alc_codec_rename_table rename_tbl[] = { { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, + { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, @@ -5614,6 +5615,7 @@ enum { ALC269_TYPE_ALC269VA, ALC269_TYPE_ALC269VB, ALC269_TYPE_ALC269VC, + ALC269_TYPE_ALC269VD, }; /* @@ -5625,8 +5627,21 @@ static int alc269_parse_auto_config(struct hda_codec *codec) static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 }; static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 }; struct alc_spec *spec = codec->spec; - const hda_nid_t *ssids = spec->codec_variant == ALC269_TYPE_ALC269VA ? - alc269va_ssids : alc269_ssids; + const hda_nid_t *ssids; + + switch (spec->codec_variant) { + case ALC269_TYPE_ALC269VA: + case ALC269_TYPE_ALC269VC: + ssids = alc269va_ssids; + break; + case ALC269_TYPE_ALC269VB: + case ALC269_TYPE_ALC269VD: + ssids = alc269_ssids; + break; + default: + ssids = alc269_ssids; + break; + } return alc_parse_auto_config(codec, alc269_ignore, ssids); } @@ -5643,6 +5658,11 @@ static void alc269_toggle_power_output(struct hda_codec *codec, int power_up) static void alc269_shutup(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; + + if (spec->codec_variant != ALC269_TYPE_ALC269VB) + return; + if ((alc_get_coef0(codec) & 0x00ff) == 0x017) alc269_toggle_power_output(codec, 0); if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { @@ -5654,19 +5674,24 @@ static void alc269_shutup(struct hda_codec *codec) #ifdef CONFIG_PM static int alc269_resume(struct hda_codec *codec) { - if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { + struct alc_spec *spec = codec->spec; + + if (spec->codec_variant == ALC269_TYPE_ALC269VB || + (alc_get_coef0(codec) & 0x00ff) == 0x018) { alc269_toggle_power_output(codec, 0); msleep(150); } codec->patch_ops.init(codec); - if ((alc_get_coef0(codec) & 0x00ff) == 0x017) { + if (spec->codec_variant == ALC269_TYPE_ALC269VB || + (alc_get_coef0(codec) & 0x00ff) == 0x017) { alc269_toggle_power_output(codec, 1); msleep(200); } - if ((alc_get_coef0(codec) & 0x00ff) == 0x018) + if (spec->codec_variant == ALC269_TYPE_ALC269VB || + (alc_get_coef0(codec) & 0x00ff) == 0x018) alc269_toggle_power_output(codec, 1); snd_hda_codec_resume_amp(codec); @@ -6081,6 +6106,9 @@ static int patch_alc269(struct hda_codec *codec) err = alc_codec_rename(codec, "ALC3202"); spec->codec_variant = ALC269_TYPE_ALC269VC; break; + case 0x0030: + spec->codec_variant = ALC269_TYPE_ALC269VD; + break; default: alc_fix_pll_init(codec, 0x20, 0x04, 15); } diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 0a5027b9471..b8ac8710f47 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1988,6 +1988,13 @@ static int hdspm_get_system_sample_rate(struct hdspm *hdspm) period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); rate = hdspm_calc_dds_value(hdspm, period); + if (rate > 207000) { + /* Unreasonable high sample rate as seen on PCI MADI cards. + * Use the cached value instead. + */ + rate = hdspm->system_sample_rate; + } + return rate; } diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index cf3ed0362c9..28dd76c7cb1 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -543,7 +543,7 @@ static int imx_ssi_probe(struct platform_device *pdev) ret); goto failed_clk; } - clk_enable(ssi->clk); + clk_prepare_enable(ssi->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -641,7 +641,7 @@ failed_ac97: failed_ioremap: release_mem_region(res->start, resource_size(res)); failed_get_resource: - clk_disable(ssi->clk); + clk_disable_unprepare(ssi->clk); clk_put(ssi->clk); failed_clk: kfree(ssi); @@ -664,7 +664,7 @@ static int __devexit imx_ssi_remove(struct platform_device *pdev) iounmap(ssi->base); release_mem_region(res->start, resource_size(res)); - clk_disable(ssi->clk); + clk_disable_unprepare(ssi->clk); clk_put(ssi->clk); kfree(ssi); diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 3cb9aa4299d..fa455675045 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -17,6 +17,7 @@ #include <linux/slab.h> #include <linux/mbus.h> #include <linux/delay.h> +#include <linux/clk.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -449,6 +450,14 @@ static __devinit int kirkwood_i2s_dev_probe(struct platform_device *pdev) priv->burst = data->burst; + priv->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "no clock\n"); + err = PTR_ERR(priv->clk); + goto err_ioremap; + } + clk_prepare_enable(priv->clk); + return snd_soc_register_dai(&pdev->dev, &kirkwood_i2s_dai); err_ioremap: @@ -466,6 +475,10 @@ static __devexit int kirkwood_i2s_dev_remove(struct platform_device *pdev) struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev); snd_soc_unregister_dai(&pdev->dev); + + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + iounmap(priv->io); release_mem_region(priv->mem->start, SZ_16K); kfree(priv); diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h index 9047436b393..f9084d83e6b 100644 --- a/sound/soc/kirkwood/kirkwood.h +++ b/sound/soc/kirkwood/kirkwood.h @@ -123,6 +123,7 @@ struct kirkwood_dma_data { void __iomem *io; int irq; int burst; + struct clk *clk; }; #endif diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 9ccfa5e1c11..57a2fa75108 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -109,11 +109,12 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040 - PandaBoard (4430) - PandaBoardES (4460) -config SND_OMAP_SOC_OMAP4_HDMI - tristate "SoC Audio support for Texas Instruments OMAP4 HDMI" - depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4 +config SND_OMAP_SOC_OMAP_HDMI + tristate "SoC Audio support for Texas Instruments OMAP HDMI" + depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS select SND_OMAP_SOC_HDMI select SND_SOC_OMAP_HDMI_CODEC + select OMAP4_DSS_HDMI_AUDIO help Say Y if you want to add support for SoC HDMI audio on Texas Instruments OMAP4 chips diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 1d656bce01d..0e14dd32256 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -25,7 +25,7 @@ snd-soc-omap3pandora-objs := omap3pandora.o snd-soc-omap3beagle-objs := omap3beagle.o snd-soc-zoom2-objs := zoom2.o snd-soc-igep0020-objs := igep0020.o -snd-soc-omap4-hdmi-objs := omap4-hdmi-card.o +snd-soc-omap-hdmi-card-objs := omap-hdmi-card.o obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o @@ -41,4 +41,4 @@ obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o -obj-$(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) += snd-soc-omap4-hdmi.o +obj-$(CONFIG_SND_OMAP_SOC_OMAP_HDMI) += snd-soc-omap-hdmi-card.o diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index e5f44440d1b..34835e8a916 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -109,6 +109,47 @@ static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) dev_dbg(mcbsp->dev, "***********************\n"); } +static irqreturn_t omap_mcbsp_irq_handler(int irq, void *dev_id) +{ + struct omap_mcbsp *mcbsp = dev_id; + u16 irqst; + + irqst = MCBSP_READ(mcbsp, IRQST); + dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst); + + if (irqst & RSYNCERREN) + dev_err(mcbsp->dev, "RX Frame Sync Error!\n"); + if (irqst & RFSREN) + dev_dbg(mcbsp->dev, "RX Frame Sync\n"); + if (irqst & REOFEN) + dev_dbg(mcbsp->dev, "RX End Of Frame\n"); + if (irqst & RRDYEN) + dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n"); + if (irqst & RUNDFLEN) + dev_err(mcbsp->dev, "RX Buffer Underflow!\n"); + if (irqst & ROVFLEN) + dev_err(mcbsp->dev, "RX Buffer Overflow!\n"); + + if (irqst & XSYNCERREN) + dev_err(mcbsp->dev, "TX Frame Sync Error!\n"); + if (irqst & XFSXEN) + dev_dbg(mcbsp->dev, "TX Frame Sync\n"); + if (irqst & XEOFEN) + dev_dbg(mcbsp->dev, "TX End Of Frame\n"); + if (irqst & XRDYEN) + dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n"); + if (irqst & XUNDFLEN) + dev_err(mcbsp->dev, "TX Buffer Underflow!\n"); + if (irqst & XOVFLEN) + dev_err(mcbsp->dev, "TX Buffer Overflow!\n"); + if (irqst & XEMPTYEOFEN) + dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n"); + + MCBSP_WRITE(mcbsp, IRQST, irqst); + + return IRQ_HANDLED; +} + static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id) { struct omap_mcbsp *mcbsp_tx = dev_id; @@ -176,6 +217,10 @@ void omap_mcbsp_config(struct omap_mcbsp *mcbsp, /* Enable wakeup behavior */ if (mcbsp->pdata->has_wakeup) MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); + + /* Enable TX/RX sync error interrupts by default */ + if (mcbsp->irq) + MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN); } /** @@ -489,23 +534,25 @@ int omap_mcbsp_request(struct omap_mcbsp *mcbsp) MCBSP_WRITE(mcbsp, SPCR1, 0); MCBSP_WRITE(mcbsp, SPCR2, 0); - err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, - 0, "McBSP", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request TX IRQ %d " - "for McBSP%d\n", mcbsp->tx_irq, - mcbsp->id); - goto err_clk_disable; - } + if (mcbsp->irq) { + err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, + "McBSP", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request IRQ\n"); + goto err_clk_disable; + } + } else { + err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, + "McBSP TX", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); + goto err_clk_disable; + } - if (mcbsp->rx_irq) { - err = request_irq(mcbsp->rx_irq, - omap_mcbsp_rx_irq_handler, - 0, "McBSP", (void *)mcbsp); + err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, + "McBSP RX", (void *)mcbsp); if (err != 0) { - dev_err(mcbsp->dev, "Unable to request RX IRQ %d " - "for McBSP%d\n", mcbsp->rx_irq, - mcbsp->id); + dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); goto err_free_irq; } } @@ -542,9 +589,16 @@ void omap_mcbsp_free(struct omap_mcbsp *mcbsp) if (mcbsp->pdata->has_wakeup) MCBSP_WRITE(mcbsp, WAKEUPEN, 0); - if (mcbsp->rx_irq) + /* Disable interrupt requests */ + if (mcbsp->irq) + MCBSP_WRITE(mcbsp, IRQEN, 0); + + if (mcbsp->irq) { + free_irq(mcbsp->irq, (void *)mcbsp); + } else { free_irq(mcbsp->rx_irq, (void *)mcbsp); - free_irq(mcbsp->tx_irq, (void *)mcbsp); + free_irq(mcbsp->tx_irq, (void *)mcbsp); + } reg_cache = mcbsp->reg_cache; @@ -754,7 +808,7 @@ THRESHOLD_PROP_BUILDER(max_tx_thres); THRESHOLD_PROP_BUILDER(max_rx_thres); static const char *dma_op_modes[] = { - "element", "threshold", "frame", + "element", "threshold", }; static ssize_t dma_op_mode_show(struct device *dev, @@ -949,13 +1003,24 @@ int __devinit omap_mcbsp_init(struct platform_device *pdev) else mcbsp->phys_dma_base = res->start; - mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); - mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); - - /* From OMAP4 there will be a single irq line */ - if (mcbsp->tx_irq == -ENXIO) { - mcbsp->tx_irq = platform_get_irq(pdev, 0); - mcbsp->rx_irq = 0; + /* + * OMAP1, 2 uses two interrupt lines: TX, RX + * OMAP2430, OMAP3 SoC have combined IRQ line as well. + * OMAP4 and newer SoC only have the combined IRQ line. + * Use the combined IRQ if available since it gives better debugging + * possibilities. + */ + mcbsp->irq = platform_get_irq_byname(pdev, "common"); + if (mcbsp->irq == -ENXIO) { + mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); + + if (mcbsp->tx_irq == -ENXIO) { + mcbsp->irq = platform_get_irq(pdev, 0); + mcbsp->tx_irq = 0; + } else { + mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); + mcbsp->irq = 0; + } } res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/omap/mcbsp.h index a944fcc9073..262a6152111 100644 --- a/sound/soc/omap/mcbsp.h +++ b/sound/soc/omap/mcbsp.h @@ -217,17 +217,20 @@ enum { /********************** McBSP DMA operating modes **************************/ #define MCBSP_DMA_MODE_ELEMENT 0 #define MCBSP_DMA_MODE_THRESHOLD 1 -#define MCBSP_DMA_MODE_FRAME 2 -/********************** McBSP WAKEUPEN bit definitions *********************/ +/********************** McBSP WAKEUPEN/IRQST/IRQEN bit definitions *********/ #define RSYNCERREN BIT(0) #define RFSREN BIT(1) #define REOFEN BIT(2) #define RRDYEN BIT(3) +#define RUNDFLEN BIT(4) +#define ROVFLEN BIT(5) #define XSYNCERREN BIT(7) #define XFSXEN BIT(8) #define XEOFEN BIT(9) #define XRDYEN BIT(10) +#define XUNDFLEN BIT(11) +#define XOVFLEN BIT(12) #define XEMPTYEOFEN BIT(14) /* Clock signal muxing options */ @@ -295,6 +298,7 @@ struct omap_mcbsp { int configured; u8 free; + int irq; int rx_irq; int tx_irq; diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index 93bb8eee22b..9d93793d307 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -40,6 +40,11 @@ #include "omap-pcm.h" #include "../codecs/twl6040.h" +struct abe_twl6040 { + int jack_detection; /* board can detect jack events */ + int mclk_freq; /* MCLK frequency speed for twl6040 */ +}; + static int omap_abe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -47,13 +52,13 @@ static int omap_abe_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; - struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev); + struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); int clk_id, freq; int ret; clk_id = twl6040_get_clk_id(rtd->codec); if (clk_id == TWL6040_SYSCLK_SEL_HPPLL) - freq = pdata->mclk_freq; + freq = priv->mclk_freq; else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL) freq = 32768; else @@ -128,6 +133,9 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { SND_SOC_DAPM_MIC("Main Handset Mic", NULL), SND_SOC_DAPM_MIC("Sub Handset Mic", NULL), SND_SOC_DAPM_LINE("Line In", NULL), + + /* Digital microphones */ + SND_SOC_DAPM_MIC("Digital Mic", NULL), }; static const struct snd_soc_dapm_route audio_map[] = { @@ -173,6 +181,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_card *card = codec->card; struct snd_soc_dapm_context *dapm = &codec->dapm; struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev); + struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); int hs_trim; int ret = 0; @@ -196,7 +205,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) TWL6040_HSF_TRIM_RIGHT(hs_trim)); /* Headset jack detection only if it is supported */ - if (pdata->jack_detection) { + if (priv->jack_detection) { ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, &hs_jack); if (ret) @@ -210,10 +219,6 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) return ret; } -static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = { - SND_SOC_DAPM_MIC("Digital Mic", NULL), -}; - static const struct snd_soc_dapm_route dmic_audio_map[] = { {"DMic", NULL, "Digital Mic"}, {"Digital Mic", NULL, "Digital Mic1 Bias"}, @@ -223,19 +228,13 @@ static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; - int ret; - - ret = snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets, - ARRAY_SIZE(dmic_dapm_widgets)); - if (ret) - return ret; return snd_soc_dapm_add_routes(dapm, dmic_audio_map, ARRAY_SIZE(dmic_audio_map)); } /* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link twl6040_dmic_dai[] = { +static struct snd_soc_dai_link abe_twl6040_dai_links[] = { { .name = "TWL6040", .stream_name = "TWL6040", @@ -258,19 +257,6 @@ static struct snd_soc_dai_link twl6040_dmic_dai[] = { }, }; -static struct snd_soc_dai_link twl6040_only_dai[] = { - { - .name = "TWL6040", - .stream_name = "TWL6040", - .cpu_dai_name = "omap-mcpdm", - .codec_dai_name = "twl6040-legacy", - .platform_name = "omap-pcm-audio", - .codec_name = "twl6040-codec", - .init = omap_abe_twl6040_init, - .ops = &omap_abe_ops, - }, -}; - /* Audio machine driver */ static struct snd_soc_card omap_abe_card = { .owner = THIS_MODULE, @@ -285,6 +271,8 @@ static __devinit int omap_abe_probe(struct platform_device *pdev) { struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev); struct snd_soc_card *card = &omap_abe_card; + struct abe_twl6040 *priv; + int num_links = 0; int ret; card->dev = &pdev->dev; @@ -294,6 +282,10 @@ static __devinit int omap_abe_probe(struct platform_device *pdev) return -ENODEV; } + priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + if (pdata->card_name) { card->name = pdata->card_name; } else { @@ -301,18 +293,24 @@ static __devinit int omap_abe_probe(struct platform_device *pdev) return -ENODEV; } - if (!pdata->mclk_freq) { + priv->jack_detection = pdata->jack_detection; + priv->mclk_freq = pdata->mclk_freq; + + + if (!priv->mclk_freq) { dev_err(&pdev->dev, "MCLK frequency missing\n"); return -ENODEV; } - if (pdata->has_dmic) { - card->dai_link = twl6040_dmic_dai; - card->num_links = ARRAY_SIZE(twl6040_dmic_dai); - } else { - card->dai_link = twl6040_only_dai; - card->num_links = ARRAY_SIZE(twl6040_only_dai); - } + if (pdata->has_dmic) + num_links = 2; + else + num_links = 1; + + card->dai_link = abe_twl6040_dai_links; + card->num_links = num_links; + + snd_soc_card_set_drvdata(card, priv); ret = snd_soc_register_card(card); if (ret) diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index 4dcb5a7e40e..75f5dca0e8d 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -32,6 +32,7 @@ #include <linux/io.h> #include <linux/slab.h> #include <linux/pm_runtime.h> +#include <linux/of_device.h> #include <plat/dma.h> #include <sound/core.h> @@ -528,10 +529,17 @@ static int __devexit asoc_dmic_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id omap_dmic_of_match[] = { + { .compatible = "ti,omap4-dmic", }, + { } +}; +MODULE_DEVICE_TABLE(of, omap_dmic_of_match); + static struct platform_driver asoc_dmic_driver = { .driver = { .name = "omap-dmic", .owner = THIS_MODULE, + .of_match_table = omap_dmic_of_match, }, .probe = asoc_dmic_probe, .remove = __devexit_p(asoc_dmic_remove), diff --git a/sound/soc/omap/omap-hdmi-card.c b/sound/soc/omap/omap-hdmi-card.c new file mode 100644 index 00000000000..eaa2ea0e3f8 --- /dev/null +++ b/sound/soc/omap/omap-hdmi-card.c @@ -0,0 +1,87 @@ +/* + * omap-hdmi-card.c + * + * OMAP ALSA SoC machine driver for TI OMAP HDMI + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Ricardo Neri <ricardo.neri@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <asm/mach-types.h> +#include <video/omapdss.h> + +#define DRV_NAME "omap-hdmi-audio" + +static struct snd_soc_dai_link omap_hdmi_dai = { + .name = "HDMI", + .stream_name = "HDMI", + .cpu_dai_name = "omap-hdmi-audio-dai", + .platform_name = "omap-pcm-audio", + .codec_name = "hdmi-audio-codec", + .codec_dai_name = "omap-hdmi-hifi", +}; + +static struct snd_soc_card snd_soc_omap_hdmi = { + .name = "OMAPHDMI", + .owner = THIS_MODULE, + .dai_link = &omap_hdmi_dai, + .num_links = 1, +}; + +static __devinit int omap_hdmi_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_omap_hdmi; + int ret; + + card->dev = &pdev->dev; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + card->dev = NULL; + return ret; + } + return 0; +} + +static int __devexit omap_hdmi_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + card->dev = NULL; + return 0; +} + +static struct platform_driver omap_hdmi_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = omap_hdmi_probe, + .remove = __devexit_p(omap_hdmi_remove), +}; + +module_platform_driver(omap_hdmi_driver); + +MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>"); +MODULE_DESCRIPTION("OMAP HDMI machine ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c index 38e0defa707..a08245d9203 100644 --- a/sound/soc/omap/omap-hdmi.c +++ b/sound/soc/omap/omap-hdmi.c @@ -30,21 +30,28 @@ #include <sound/pcm_params.h> #include <sound/initval.h> #include <sound/soc.h> +#include <sound/asound.h> +#include <sound/asoundef.h> +#include <video/omapdss.h> #include <plat/dma.h> #include "omap-pcm.h" #include "omap-hdmi.h" -#define DRV_NAME "hdmi-audio-dai" +#define DRV_NAME "omap-hdmi-audio-dai" -static struct omap_pcm_dma_data omap_hdmi_dai_dma_params = { - .name = "HDMI playback", - .sync_mode = OMAP_DMA_SYNC_PACKET, +struct hdmi_priv { + struct omap_pcm_dma_data dma_params; + struct omap_dss_audio dss_audio; + struct snd_aes_iec958 iec; + struct snd_cea_861_aud_if cea; + struct omap_dss_device *dssdev; }; static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai); int err; /* * Make sure that the period bytes are multiple of the DMA packet size. @@ -52,46 +59,201 @@ static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream, */ err = snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); - if (err < 0) + if (err < 0) { + dev_err(dai->dev, "could not apply constraint\n"); return err; + } + if (!priv->dssdev->driver->audio_supported(priv->dssdev)) { + dev_err(dai->dev, "audio not supported\n"); + return -ENODEV; + } return 0; } +static int omap_hdmi_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai); + + return priv->dssdev->driver->audio_enable(priv->dssdev); +} + static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai); + struct snd_aes_iec958 *iec = &priv->iec; + struct snd_cea_861_aud_if *cea = &priv->cea; int err = 0; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - omap_hdmi_dai_dma_params.packet_size = 16; + priv->dma_params.packet_size = 16; break; case SNDRV_PCM_FORMAT_S24_LE: - omap_hdmi_dai_dma_params.packet_size = 32; + priv->dma_params.packet_size = 32; break; default: - err = -EINVAL; + dev_err(dai->dev, "format not supported!\n"); + return -EINVAL; } - omap_hdmi_dai_dma_params.data_type = OMAP_DMA_DATA_TYPE_S32; + priv->dma_params.data_type = OMAP_DMA_DATA_TYPE_S32; snd_soc_dai_set_dma_data(dai, substream, - &omap_hdmi_dai_dma_params); + &priv->dma_params); + + /* + * fill the IEC-60958 channel status word + */ + + /* specify IEC-60958-3 (commercial use) */ + iec->status[0] &= ~IEC958_AES0_PROFESSIONAL; + + /* specify that the audio is LPCM*/ + iec->status[0] &= ~IEC958_AES0_NONAUDIO; + + iec->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; + + iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; + + iec->status[0] |= IEC958_AES1_PRO_MODE_NOTID; + + iec->status[1] = IEC958_AES1_CON_GENERAL; + + iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC; + + iec->status[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC; + + switch (params_rate(params)) { + case 32000: + iec->status[3] |= IEC958_AES3_CON_FS_32000; + break; + case 44100: + iec->status[3] |= IEC958_AES3_CON_FS_44100; + break; + case 48000: + iec->status[3] |= IEC958_AES3_CON_FS_48000; + break; + case 88200: + iec->status[3] |= IEC958_AES3_CON_FS_88200; + break; + case 96000: + iec->status[3] |= IEC958_AES3_CON_FS_96000; + break; + case 176400: + iec->status[3] |= IEC958_AES3_CON_FS_176400; + break; + case 192000: + iec->status[3] |= IEC958_AES3_CON_FS_192000; + break; + default: + dev_err(dai->dev, "rate not supported!\n"); + return -EINVAL; + } + + /* specify the clock accuracy */ + iec->status[3] |= IEC958_AES3_CON_CLOCK_1000PPM; + + /* + * specify the word length. The same word length value can mean + * two different lengths. Hence, we need to specify the maximum + * word length as well. + */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + iec->status[4] |= IEC958_AES4_CON_WORDLEN_20_16; + iec->status[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iec->status[4] |= IEC958_AES4_CON_WORDLEN_24_20; + iec->status[4] |= IEC958_AES4_CON_MAX_WORDLEN_24; + break; + default: + dev_err(dai->dev, "format not supported!\n"); + return -EINVAL; + } + + /* + * Fill the CEA-861 audio infoframe (see spec for details) + */ + + cea->db1_ct_cc = (params_channels(params) - 1) + & CEA861_AUDIO_INFOFRAME_DB1CC; + cea->db1_ct_cc |= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM; + + cea->db2_sf_ss = CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM; + cea->db2_sf_ss |= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM; + + cea->db3 = 0; /* not used, all zeros */ + + /* + * The OMAP HDMI IP requires to use the 8-channel channel code when + * transmitting more than two channels. + */ + if (params_channels(params) == 2) + cea->db4_ca = 0x0; + else + cea->db4_ca = 0x13; + + cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED; + /* the expression is trivial but makes clear what we are doing */ + cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV); + + priv->dss_audio.iec = iec; + priv->dss_audio.cea = cea; + + err = priv->dssdev->driver->audio_config(priv->dssdev, + &priv->dss_audio); return err; } +static int omap_hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai); + int err = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + err = priv->dssdev->driver->audio_start(priv->dssdev); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + priv->dssdev->driver->audio_stop(priv->dssdev); + break; + default: + err = -EINVAL; + } + return err; +} + +static void omap_hdmi_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai); + + priv->dssdev->driver->audio_disable(priv->dssdev); +} + static const struct snd_soc_dai_ops omap_hdmi_dai_ops = { .startup = omap_hdmi_dai_startup, .hw_params = omap_hdmi_dai_hw_params, + .prepare = omap_hdmi_dai_prepare, + .trigger = omap_hdmi_dai_trigger, + .shutdown = omap_hdmi_dai_shutdown, }; static struct snd_soc_dai_driver omap_hdmi_dai = { .playback = { .channels_min = 2, - .channels_max = 2, + .channels_max = 8, .rates = OMAP_HDMI_RATES, .formats = OMAP_HDMI_FORMATS, }, @@ -102,31 +264,77 @@ static __devinit int omap_hdmi_probe(struct platform_device *pdev) { int ret; struct resource *hdmi_rsrc; + struct hdmi_priv *hdmi_data; + bool hdmi_dev_found = false; + + hdmi_data = devm_kzalloc(&pdev->dev, sizeof(*hdmi_data), GFP_KERNEL); + if (hdmi_data == NULL) { + dev_err(&pdev->dev, "Cannot allocate memory for HDMI data\n"); + return -ENOMEM; + } hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!hdmi_rsrc) { dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM HDMI\n"); - return -EINVAL; + return -ENODEV; } - omap_hdmi_dai_dma_params.port_addr = hdmi_rsrc->start + hdmi_data->dma_params.port_addr = hdmi_rsrc->start + OMAP_HDMI_AUDIO_DMA_PORT; hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!hdmi_rsrc) { dev_err(&pdev->dev, "Cannot obtain IORESOURCE_DMA HDMI\n"); - return -EINVAL; + return -ENODEV; } - omap_hdmi_dai_dma_params.dma_req = hdmi_rsrc->start; + hdmi_data->dma_params.dma_req = hdmi_rsrc->start; + hdmi_data->dma_params.name = "HDMI playback"; + hdmi_data->dma_params.sync_mode = OMAP_DMA_SYNC_PACKET; + + /* + * TODO: We assume that there is only one DSS HDMI device. Future + * OMAP implementations may support more than one HDMI devices and + * we should provided separate audio support for all of them. + */ + /* Find an HDMI device. */ + for_each_dss_dev(hdmi_data->dssdev) { + omap_dss_get_device(hdmi_data->dssdev); + if (!hdmi_data->dssdev->driver) { + omap_dss_put_device(hdmi_data->dssdev); + continue; + } + + if (hdmi_data->dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { + hdmi_dev_found = true; + break; + } + } + + if (!hdmi_dev_found) { + dev_err(&pdev->dev, "no driver for HDMI display found\n"); + return -ENODEV; + } + + dev_set_drvdata(&pdev->dev, hdmi_data); ret = snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai); + return ret; } static int __devexit omap_hdmi_remove(struct platform_device *pdev) { + struct hdmi_priv *hdmi_data = dev_get_drvdata(&pdev->dev); + snd_soc_unregister_dai(&pdev->dev); + + if (hdmi_data == NULL) { + dev_err(&pdev->dev, "cannot obtain HDMi data\n"); + return -ENODEV; + } + + omap_dss_put_device(hdmi_data->dssdev); return 0; } diff --git a/sound/soc/omap/omap-hdmi.h b/sound/soc/omap/omap-hdmi.h index 34c298d5057..6ad2bf4f269 100644 --- a/sound/soc/omap/omap-hdmi.h +++ b/sound/soc/omap/omap-hdmi.h @@ -28,7 +28,9 @@ #define OMAP_HDMI_AUDIO_DMA_PORT 0x8c #define OMAP_HDMI_RATES (SNDRV_PCM_RATE_32000 | \ - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) #define OMAP_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE) diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 6912ac7cb62..1046083e90a 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -71,18 +71,17 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ - if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) - /* - * Configure McBSP threshold based on either: - * packet_size, when the sDMA is in packet mode, or - * based on the period size. - */ - if (dma_data->packet_size) - words = dma_data->packet_size; - else - words = snd_pcm_lib_period_bytes(substream) / - (mcbsp->wlen / 8); + /* + * Configure McBSP threshold based on either: + * packet_size, when the sDMA is in packet mode, or based on the + * period size in THRESHOLD mode, otherwise use McBSP threshold = 1 + * for mono streams. + */ + if (dma_data->packet_size) + words = dma_data->packet_size; + else if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) + words = snd_pcm_lib_period_bytes(substream) / + (mcbsp->wlen / 8); else words = 1; @@ -139,13 +138,15 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, if (mcbsp->pdata->buffer_size) { /* * Rule for the buffer size. We should not allow - * smaller buffer than the FIFO size to avoid underruns + * smaller buffer than the FIFO size to avoid underruns. + * This applies only for the playback stream. */ - snd_pcm_hw_rule_add(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - omap_mcbsp_hwrule_min_buffersize, - mcbsp, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + omap_mcbsp_hwrule_min_buffersize, + mcbsp, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); /* Make sure, that the period size is always even */ snd_pcm_hw_constraint_step(substream->runtime, 0, @@ -230,6 +231,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, unsigned int format, div, framesize, master; dma_data = &mcbsp->dma_data[substream->stream]; + channels = params_channels(params); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -245,7 +247,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, } if (mcbsp->pdata->buffer_size) { dma_data->set_threshold = omap_mcbsp_set_threshold; - /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) { int period_words, max_thrsh; @@ -283,6 +284,10 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, } else { sync_mode = OMAP_DMA_SYNC_FRAME; } + } else if (channels > 1) { + /* Use packet mode for non mono streams */ + pkt_size = channels; + sync_mode = OMAP_DMA_SYNC_PACKET; } } @@ -301,7 +306,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7)); regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7)); format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK; - wpf = channels = params_channels(params); + wpf = channels; if (channels == 2 && (format == SND_SOC_DAIFMT_I2S || format == SND_SOC_DAIFMT_LEFT_J)) { /* Use dual-phase frames */ diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index 39705561131..59d47ab5b15 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -33,6 +33,7 @@ #include <linux/irq.h> #include <linux/slab.h> #include <linux/pm_runtime.h> +#include <linux/of_device.h> #include <sound/core.h> #include <sound/pcm.h> @@ -507,10 +508,17 @@ static int __devexit asoc_mcpdm_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id omap_mcpdm_of_match[] = { + { .compatible = "ti,omap4-mcpdm", }, + { } +}; +MODULE_DEVICE_TABLE(of, omap_mcpdm_of_match); + static struct platform_driver asoc_mcpdm_driver = { .driver = { .name = "omap-mcpdm", .owner = THIS_MODULE, + .of_match_table = omap_mcpdm_of_match, }, .probe = asoc_mcpdm_probe, diff --git a/sound/soc/omap/omap4-hdmi-card.c b/sound/soc/omap/omap4-hdmi-card.c deleted file mode 100644 index 28d689b2714..00000000000 --- a/sound/soc/omap/omap4-hdmi-card.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * omap4-hdmi-card.c - * - * OMAP ALSA SoC machine driver for TI OMAP4 HDMI - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ - * Author: Ricardo Neri <ricardo.neri@ti.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <linux/module.h> -#include <sound/pcm.h> -#include <sound/soc.h> -#include <asm/mach-types.h> -#include <video/omapdss.h> - -#define DRV_NAME "omap4-hdmi-audio" - -static int omap4_hdmi_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - int i; - struct omap_overlay_manager *mgr = NULL; - struct device *dev = substream->pcm->card->dev; - - /* Find DSS HDMI device */ - for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) { - mgr = omap_dss_get_overlay_manager(i); - if (mgr && mgr->device - && mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) - break; - } - - if (i == omap_dss_get_num_overlay_managers()) { - dev_err(dev, "HDMI display device not found!\n"); - return -ENODEV; - } - - /* Make sure HDMI is power-on to avoid L3 interconnect errors */ - if (mgr->device->state != OMAP_DSS_DISPLAY_ACTIVE) { - dev_err(dev, "HDMI display is not active!\n"); - return -EIO; - } - - return 0; -} - -static struct snd_soc_ops omap4_hdmi_dai_ops = { - .hw_params = omap4_hdmi_dai_hw_params, -}; - -static struct snd_soc_dai_link omap4_hdmi_dai = { - .name = "HDMI", - .stream_name = "HDMI", - .cpu_dai_name = "hdmi-audio-dai", - .platform_name = "omap-pcm-audio", - .codec_name = "omapdss_hdmi", - .codec_dai_name = "hdmi-audio-codec", - .ops = &omap4_hdmi_dai_ops, -}; - -static struct snd_soc_card snd_soc_omap4_hdmi = { - .name = "OMAP4HDMI", - .owner = THIS_MODULE, - .dai_link = &omap4_hdmi_dai, - .num_links = 1, -}; - -static __devinit int omap4_hdmi_probe(struct platform_device *pdev) -{ - struct snd_soc_card *card = &snd_soc_omap4_hdmi; - int ret; - - card->dev = &pdev->dev; - - ret = snd_soc_register_card(card); - if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - card->dev = NULL; - return ret; - } - return 0; -} - -static int __devexit omap4_hdmi_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - card->dev = NULL; - return 0; -} - -static struct platform_driver omap4_hdmi_driver = { - .driver = { - .name = "omap4-hdmi-audio", - .owner = THIS_MODULE, - }, - .probe = omap4_hdmi_probe, - .remove = __devexit_p(omap4_hdmi_remove), -}; - -module_platform_driver(omap4_hdmi_driver); - -MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>"); -MODULE_DESCRIPTION("OMAP4 HDMI machine ASoC driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 7cee22515d9..2ef98536f1d 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1052,6 +1052,13 @@ static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io) return 0; } +static dma_addr_t fsi_dma_get_area(struct fsi_stream *io) +{ + struct snd_pcm_runtime *runtime = io->substream->runtime; + + return io->dma + samples_to_bytes(runtime, io->buff_sample_pos); +} + static void fsi_dma_complete(void *data) { struct fsi_stream *io = (struct fsi_stream *)data; @@ -1061,7 +1068,7 @@ static void fsi_dma_complete(void *data) enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - dma_sync_single_for_cpu(dai->dev, io->dma, + dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io), samples_to_bytes(runtime, io->period_samples), dir); io->buff_sample_pos += io->period_samples; @@ -1078,13 +1085,6 @@ static void fsi_dma_complete(void *data) snd_pcm_period_elapsed(io->substream); } -static dma_addr_t fsi_dma_get_area(struct fsi_stream *io) -{ - struct snd_pcm_runtime *runtime = io->substream->runtime; - - return io->dma + samples_to_bytes(runtime, io->buff_sample_pos); -} - static void fsi_dma_do_tasklet(unsigned long data) { struct fsi_stream *io = (struct fsi_stream *)data; @@ -1110,7 +1110,7 @@ static void fsi_dma_do_tasklet(unsigned long data) len = samples_to_bytes(runtime, io->period_samples); buf = fsi_dma_get_area(io); - dma_sync_single_for_device(dai->dev, io->dma, len, dir); + dma_sync_single_for_device(dai->dev, buf, len, dir); sg_init_table(&sg, 1); sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)), @@ -1172,9 +1172,16 @@ static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, int start) { + struct fsi_master *master = fsi_get_master(fsi); + u32 clk = fsi_is_port_a(fsi) ? CRA : CRB; u32 enable = start ? DMA_ON : 0; fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable); + + dmaengine_terminate_all(io->chan); + + if (fsi_is_clk_master(fsi)) + fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); } static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io) diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 24839d93264..cdf8b760197 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -788,6 +788,9 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, int count = 0, needs_knot = 0; int err; + kfree(subs->rate_list.list); + subs->rate_list.list = NULL; + list_for_each_entry(fp, &subs->fmt_list, list) { if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) return 0; |