sound updates for 4.5-rc1
We've had quite busy weeks in this cycle. Looking at ALSA core, the significant changes are a few fixes wrt timer and sequencer ioctls that have been revealed by fuzzer recently. Other than that, ASoC core got a few updates about DAI link handling, but these are rather straightforward refactoring. In drivers scene, ASoC received quite lots of new drivers in addition to bunch of updates for still ongoing Intel Skylake support and topology API. HD-audio gained a new HDMI/DP hotplug notification via component. FireWire got a pile of code refactoring/updates with SCS.1x driver integration. More highlights are shown below. [NOTE: this contains also many commits for DRM. This is due to the pull of drm stable branch into sound tree, as the base of i915 audio component work for HD-audio. The highlights below don't contain these DRM changes, as these are supposed to be pulled via drm tree in anyway sooner or later.] Core - Handful fixes to harden ALSA timer and sequencer ioctls against races reported by syzkaller fuzzer - Irq description string can be unique to each card; only for HD-audio for now ASoC - Conversion of the array of DAI links to a list for supporting dynamically adding and removing DAI links - Topology API enhancements to make everything more component based and being able to specify PCM links via topology - Some more fixes for the topology code, though it is still not final and ready for enabling in production; we really need to get to the point where that can be done - A pile of changes for Intel SkyLake drivers which hopefully deliver some useful initial functionality for systems with this chipset, though there is more work still to come - Lots of new features and cleanups for the Renesas drivers - ANC support for WM5110 - New drivers: Imagination Technologies IPs, Atmel class D speaker, Cirrus CS47L24 and WM1831, Dialog DA7128, Realtek RT5659 and RT56156, Rockchip RK3036, TI PC3168A, and AMD ACP - Rename PCM1792a driver to be generic pcm179x HD-Audio - Use audio component for i915 HDMI/DP hotplug handling - On-demand binding with i915 driver - bdl_pos_adj parameter adjustment for Baytrail controllers - Enable power_save_node for CX20722; this shouldn't lead to regression, hopefully - Kabylake HDMI/DP codec support - Quirks for Lenovo E50-80, Dell Latitude E-series, and other Dell machines - A few code refactoring FireWire - Lots of code cleanup and refactoring - Integrate the support of SCS.1x devices into snd-oxfw driver; snd-scs1x driver is obsoleted USB-audio - Fix possible NULL dereference at disconnection - A regression fix for Native Instruments devices Misc - A few code cleanups of fm801 driver -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJWmmhNAAoJEGwxgFQ9KSmk/wsP/3eO+giAT9VRPa6qxR6VdT6I dZwTxcp4ZzUrgLxk9k5VYjqey6QL+1xWfl3Abrd+NzXDj1wo4KsDh2XCKG1btO9K UpIZf76Nzt7o91pzHbsU6mrjDeoVNqloZoGbg1utAmmegaXH3owd18p/ZHfE3sz2 BbaHmYW/R8lnaBgBhzqJB97+zRaLJmMWpWHfpHaIPjdfw8/V4j76jtPnpmv2hDZl BHXVHcQXjVGunFRzxdzBLuTC+FmhzUeTAbbAdOT4fEoOCv5MtZqYppNxdhj+b9l5 mrsXe5FBTNmrt9Z5TtfCuzgJPkzoDperFb0aKd7wI1jVMtLzkNCMlanHr9U6B6fr jSrs6l25xrpF1BBfRMfHjNudA5vng/XC5dtW00JofXSrIxtwPNUoDDiqJgw7xVm5 aVWK7KkQIjRbHdCQaeTymv70oHHKei92hbCrXUobXZ7wLeJMXNVPT25ttChWrgAI 7cu5h+K5PjReI/sJFTMPL4aHZ+jAn9quQl7vK8EXiL9E6G8lLiuBiVW6hjGd9At+ Z6UyGV+nCM6O3qZcyParMuLkNtWx9uT7Pcn8oTZAdKPngNhsf8+yl9qmsFkNLDC4 LKPx0+rdCjtMKn2du3krsHhG3EN9pLDrE6g5U3d6Cz83e69Y7fCuSjl31SjD91H0 bZDcM/ejYSbid3yKN4TL =Gvgb -----END PGP SIGNATURE----- Merge tag 'sound-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound updates from Takashi Iwai: "We've had quite busy weeks in this cycle. Looking at ALSA core, the significant changes are a few fixes wrt timer and sequencer ioctls that have been revealed by fuzzer recently. Other than that, ASoC core got a few updates about DAI link handling, but these are rather straightforward refactoring. In drivers scene, ASoC received quite lots of new drivers in addition to bunch of updates for still ongoing Intel Skylake support and topology API. HD-audio gained a new HDMI/DP hotplug notification via component. FireWire got a pile of code refactoring/updates with SCS.1x driver integration. More highlights are shown below. [ NOTE: this contains also many commits for DRM. This is due to the pull of drm stable branch into sound tree, as the base of i915 audio component work for HD-audio. The highlights below don't contain these DRM changes, as these are supposed to be pulled via drm tree in anyway sooner or later. ] Core: - Handful fixes to harden ALSA timer and sequencer ioctls against races reported by syzkaller fuzzer - Irq description string can be unique to each card; only for HD-audio for now ASoC: - Conversion of the array of DAI links to a list for supporting dynamically adding and removing DAI links - Topology API enhancements to make everything more component based and being able to specify PCM links via topology - Some more fixes for the topology code, though it is still not final and ready for enabling in production; we really need to get to the point where that can be done - A pile of changes for Intel SkyLake drivers which hopefully deliver some useful initial functionality for systems with this chipset, though there is more work still to come - Lots of new features and cleanups for the Renesas drivers - ANC support for WM5110 - New drivers: Imagination Technologies IPs, Atmel class D speaker, Cirrus CS47L24 and WM1831, Dialog DA7128, Realtek RT5659 and RT56156, Rockchip RK3036, TI PC3168A, and AMD ACP - Rename PCM1792a driver to be generic pcm179x HD-Audio: - Use audio component for i915 HDMI/DP hotplug handling - On-demand binding with i915 driver - bdl_pos_adj parameter adjustment for Baytrail controllers - Enable power_save_node for CX20722; this shouldn't lead to regression, hopefully - Kabylake HDMI/DP codec support - Quirks for Lenovo E50-80, Dell Latitude E-series, and other Dell machines - A few code refactoring FireWire: - Lots of code cleanup and refactoring - Integrate the support of SCS.1x devices into snd-oxfw driver; snd-scs1x driver is obsoleted USB-audio: - Fix possible NULL dereference at disconnection - A regression fix for Native Instruments devices Misc: - A few code cleanups of fm801 driver" * tag 'sound-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (722 commits) ALSA: timer: Code cleanup ALSA: timer: Harden slave timer list handling ALSA: hda - Add fixup for Dell Latitidue E6540 ALSA: timer: Fix race among timer ioctls ALSA: hda - add codec support for Kabylake display audio codec ALSA: timer: Fix double unlink of active_list ALSA: usb-audio: Fix mixer ctl regression of Native Instrument devices ALSA: hda - fix the headset mic detection problem for a Dell laptop ALSA: hda - Fix white noise on Dell Latitude E5550 ALSA: hda_intel: add card number to irq description ALSA: seq: Fix race at timer setup and close ALSA: seq: Fix missing NULL check at remove_events ioctl ALSA: usb-audio: Avoid calling usb_autopm_put_interface() at disconnect ASoC: hdac_hdmi: remove unused hdac_hdmi_query_pin_connlist ASoC: AMD: Add missing include file ALSA: hda - Fixup inverted internal mic for Lenovo E50-80 ALSA: usb: Add native DSD support for Oppo HA-1 ASoC: Make aux_dev more like a generic component ASoC: bcm2835: cleanup includes by ordering them alphabetically ASoC: AMD: Manage ACP 2.x SRAM banks power ...
This commit is contained in:
commit
a016af2e70
430 changed files with 45260 additions and 10487 deletions
|
@ -38,8 +38,10 @@
|
|||
#include <linux/uio.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/compat.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/compress_params.h>
|
||||
#include <sound/compress_offload.h>
|
||||
#include <sound/compress_driver.h>
|
||||
|
@ -847,6 +849,15 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* support of 32bit userspace on 64bit platforms */
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations snd_compr_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = snd_compr_open,
|
||||
|
@ -854,6 +865,9 @@ static const struct file_operations snd_compr_file_ops = {
|
|||
.write = snd_compr_write,
|
||||
.read = snd_compr_read,
|
||||
.unlocked_ioctl = snd_compr_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = snd_compr_ioctl_compat,
|
||||
#endif
|
||||
.mmap = snd_compr_mmap,
|
||||
.poll = snd_compr_poll,
|
||||
};
|
||||
|
@ -891,11 +905,85 @@ static int snd_compress_dev_disconnect(struct snd_device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_VERBOSE_PROCFS
|
||||
static void snd_compress_proc_info_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_compr *compr = (struct snd_compr *)entry->private_data;
|
||||
|
||||
snd_iprintf(buffer, "card: %d\n", compr->card->number);
|
||||
snd_iprintf(buffer, "device: %d\n", compr->device);
|
||||
snd_iprintf(buffer, "stream: %s\n",
|
||||
compr->direction == SND_COMPRESS_PLAYBACK
|
||||
? "PLAYBACK" : "CAPTURE");
|
||||
snd_iprintf(buffer, "id: %s\n", compr->id);
|
||||
}
|
||||
|
||||
static int snd_compress_proc_init(struct snd_compr *compr)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
char name[16];
|
||||
|
||||
sprintf(name, "compr%i", compr->device);
|
||||
entry = snd_info_create_card_entry(compr->card, name,
|
||||
compr->card->proc_root);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return -ENOMEM;
|
||||
}
|
||||
compr->proc_root = entry;
|
||||
|
||||
entry = snd_info_create_card_entry(compr->card, "info",
|
||||
compr->proc_root);
|
||||
if (entry) {
|
||||
snd_info_set_text_ops(entry, compr,
|
||||
snd_compress_proc_info_read);
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
}
|
||||
compr->proc_info_entry = entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_compress_proc_done(struct snd_compr *compr)
|
||||
{
|
||||
snd_info_free_entry(compr->proc_info_entry);
|
||||
compr->proc_info_entry = NULL;
|
||||
snd_info_free_entry(compr->proc_root);
|
||||
compr->proc_root = NULL;
|
||||
}
|
||||
|
||||
static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
|
||||
{
|
||||
strlcpy(compr->id, id, sizeof(compr->id));
|
||||
}
|
||||
#else
|
||||
static inline int snd_compress_proc_init(struct snd_compr *compr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void snd_compress_proc_done(struct snd_compr *compr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static int snd_compress_dev_free(struct snd_device *device)
|
||||
{
|
||||
struct snd_compr *compr;
|
||||
|
||||
compr = device->device_data;
|
||||
snd_compress_proc_done(compr);
|
||||
put_device(&compr->dev);
|
||||
return 0;
|
||||
}
|
||||
|
@ -908,22 +996,29 @@ static int snd_compress_dev_free(struct snd_device *device)
|
|||
* @compr: compress device pointer
|
||||
*/
|
||||
int snd_compress_new(struct snd_card *card, int device,
|
||||
int dirn, struct snd_compr *compr)
|
||||
int dirn, const char *id, struct snd_compr *compr)
|
||||
{
|
||||
static struct snd_device_ops ops = {
|
||||
.dev_free = snd_compress_dev_free,
|
||||
.dev_register = snd_compress_dev_register,
|
||||
.dev_disconnect = snd_compress_dev_disconnect,
|
||||
};
|
||||
int ret;
|
||||
|
||||
compr->card = card;
|
||||
compr->device = device;
|
||||
compr->direction = dirn;
|
||||
|
||||
snd_compress_set_id(compr, id);
|
||||
|
||||
snd_device_initialize(&compr->dev, card);
|
||||
dev_set_name(&compr->dev, "comprC%iD%i", card->number, device);
|
||||
|
||||
return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
|
||||
ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
|
||||
if (ret == 0)
|
||||
snd_compress_proc_init(compr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_compress_new);
|
||||
|
||||
|
|
|
@ -268,6 +268,9 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
|
|||
if (err < 0)
|
||||
goto __error;
|
||||
|
||||
snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",
|
||||
dev_driver_string(card->dev), dev_name(&card->card_dev));
|
||||
|
||||
/* the control interface cannot be accessed from the user space until */
|
||||
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
|
||||
err = snd_ctl_create(card);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/time.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/compat.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/minors.h>
|
||||
#include <sound/control.h>
|
||||
|
@ -397,7 +398,12 @@ int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned l
|
|||
|
||||
#ifdef CONFIG_COMPAT
|
||||
/* all compatible */
|
||||
#define snd_mixer_oss_ioctl_compat snd_mixer_oss_ioctl
|
||||
static long snd_mixer_oss_ioctl_compat(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return snd_mixer_oss_ioctl1(file->private_data, cmd,
|
||||
(unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#else
|
||||
#define snd_mixer_oss_ioctl_compat NULL
|
||||
#endif
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/compat.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/minors.h>
|
||||
#include <sound/pcm.h>
|
||||
|
@ -850,7 +851,7 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
|
|||
|
||||
if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
||||
return -EINTR;
|
||||
sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL);
|
||||
sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
|
||||
params = kmalloc(sizeof(*params), GFP_KERNEL);
|
||||
sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
|
||||
if (!sw_params || !params || !sparams) {
|
||||
|
@ -988,7 +989,6 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
|
|||
goto failure;
|
||||
}
|
||||
|
||||
memset(sw_params, 0, sizeof(*sw_params));
|
||||
if (runtime->oss.trigger) {
|
||||
sw_params->start_threshold = 1;
|
||||
} else {
|
||||
|
@ -2648,7 +2648,11 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
|
|||
|
||||
#ifdef CONFIG_COMPAT
|
||||
/* all compatible */
|
||||
#define snd_pcm_oss_ioctl_compat snd_pcm_oss_ioctl
|
||||
static long snd_pcm_oss_ioctl_compat(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return snd_pcm_oss_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#else
|
||||
#define snd_pcm_oss_ioctl_compat NULL
|
||||
#endif
|
||||
|
|
|
@ -875,7 +875,7 @@ struct action_ops {
|
|||
* Note: the stream state might be changed also on failure
|
||||
* Note2: call with calling stream lock + link lock
|
||||
*/
|
||||
static int snd_pcm_action_group(struct action_ops *ops,
|
||||
static int snd_pcm_action_group(const struct action_ops *ops,
|
||||
struct snd_pcm_substream *substream,
|
||||
int state, int do_lock)
|
||||
{
|
||||
|
@ -932,7 +932,7 @@ static int snd_pcm_action_group(struct action_ops *ops,
|
|||
/*
|
||||
* Note: call with stream lock
|
||||
*/
|
||||
static int snd_pcm_action_single(struct action_ops *ops,
|
||||
static int snd_pcm_action_single(const struct action_ops *ops,
|
||||
struct snd_pcm_substream *substream,
|
||||
int state)
|
||||
{
|
||||
|
@ -952,7 +952,7 @@ static int snd_pcm_action_single(struct action_ops *ops,
|
|||
/*
|
||||
* Note: call with stream lock
|
||||
*/
|
||||
static int snd_pcm_action(struct action_ops *ops,
|
||||
static int snd_pcm_action(const struct action_ops *ops,
|
||||
struct snd_pcm_substream *substream,
|
||||
int state)
|
||||
{
|
||||
|
@ -984,7 +984,7 @@ static int snd_pcm_action(struct action_ops *ops,
|
|||
/*
|
||||
* Note: don't use any locks before
|
||||
*/
|
||||
static int snd_pcm_action_lock_irq(struct action_ops *ops,
|
||||
static int snd_pcm_action_lock_irq(const struct action_ops *ops,
|
||||
struct snd_pcm_substream *substream,
|
||||
int state)
|
||||
{
|
||||
|
@ -998,7 +998,7 @@ static int snd_pcm_action_lock_irq(struct action_ops *ops,
|
|||
|
||||
/*
|
||||
*/
|
||||
static int snd_pcm_action_nonatomic(struct action_ops *ops,
|
||||
static int snd_pcm_action_nonatomic(const struct action_ops *ops,
|
||||
struct snd_pcm_substream *substream,
|
||||
int state)
|
||||
{
|
||||
|
@ -1056,7 +1056,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
|
|||
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
|
||||
}
|
||||
|
||||
static struct action_ops snd_pcm_action_start = {
|
||||
static const struct action_ops snd_pcm_action_start = {
|
||||
.pre_action = snd_pcm_pre_start,
|
||||
.do_action = snd_pcm_do_start,
|
||||
.undo_action = snd_pcm_undo_start,
|
||||
|
@ -1107,7 +1107,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
|
|||
wake_up(&runtime->tsleep);
|
||||
}
|
||||
|
||||
static struct action_ops snd_pcm_action_stop = {
|
||||
static const struct action_ops snd_pcm_action_stop = {
|
||||
.pre_action = snd_pcm_pre_stop,
|
||||
.do_action = snd_pcm_do_stop,
|
||||
.post_action = snd_pcm_post_stop
|
||||
|
@ -1224,7 +1224,7 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
|
|||
}
|
||||
}
|
||||
|
||||
static struct action_ops snd_pcm_action_pause = {
|
||||
static const struct action_ops snd_pcm_action_pause = {
|
||||
.pre_action = snd_pcm_pre_pause,
|
||||
.do_action = snd_pcm_do_pause,
|
||||
.undo_action = snd_pcm_undo_pause,
|
||||
|
@ -1273,7 +1273,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
|
|||
wake_up(&runtime->tsleep);
|
||||
}
|
||||
|
||||
static struct action_ops snd_pcm_action_suspend = {
|
||||
static const struct action_ops snd_pcm_action_suspend = {
|
||||
.pre_action = snd_pcm_pre_suspend,
|
||||
.do_action = snd_pcm_do_suspend,
|
||||
.post_action = snd_pcm_post_suspend
|
||||
|
@ -1375,7 +1375,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
|
|||
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
|
||||
}
|
||||
|
||||
static struct action_ops snd_pcm_action_resume = {
|
||||
static const struct action_ops snd_pcm_action_resume = {
|
||||
.pre_action = snd_pcm_pre_resume,
|
||||
.do_action = snd_pcm_do_resume,
|
||||
.undo_action = snd_pcm_undo_resume,
|
||||
|
@ -1478,7 +1478,7 @@ static void snd_pcm_post_reset(struct snd_pcm_substream *substream, int state)
|
|||
snd_pcm_playback_silence(substream, ULONG_MAX);
|
||||
}
|
||||
|
||||
static struct action_ops snd_pcm_action_reset = {
|
||||
static const struct action_ops snd_pcm_action_reset = {
|
||||
.pre_action = snd_pcm_pre_reset,
|
||||
.do_action = snd_pcm_do_reset,
|
||||
.post_action = snd_pcm_post_reset
|
||||
|
@ -1522,7 +1522,7 @@ static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state)
|
|||
snd_pcm_set_state(substream, SNDRV_PCM_STATE_PREPARED);
|
||||
}
|
||||
|
||||
static struct action_ops snd_pcm_action_prepare = {
|
||||
static const struct action_ops snd_pcm_action_prepare = {
|
||||
.pre_action = snd_pcm_pre_prepare,
|
||||
.do_action = snd_pcm_do_prepare,
|
||||
.post_action = snd_pcm_post_prepare
|
||||
|
@ -1618,7 +1618,7 @@ static void snd_pcm_post_drain_init(struct snd_pcm_substream *substream, int sta
|
|||
{
|
||||
}
|
||||
|
||||
static struct action_ops snd_pcm_action_drain_init = {
|
||||
static const struct action_ops snd_pcm_action_drain_init = {
|
||||
.pre_action = snd_pcm_pre_drain_init,
|
||||
.do_action = snd_pcm_do_drain_init,
|
||||
.post_action = snd_pcm_post_drain_init
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/compat.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/minors.h>
|
||||
#include <sound/initval.h>
|
||||
|
@ -189,7 +190,11 @@ odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#define odev_ioctl_compat odev_ioctl
|
||||
static long odev_ioctl_compat(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return odev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#else
|
||||
#define odev_ioctl_compat NULL
|
||||
#endif
|
||||
|
|
|
@ -1962,7 +1962,7 @@ static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
|
|||
* No restrictions so for a user client we can clear
|
||||
* the whole fifo
|
||||
*/
|
||||
if (client->type == USER_CLIENT)
|
||||
if (client->type == USER_CLIENT && client->data.user.fifo)
|
||||
snd_seq_fifo_clear(client->data.user.fifo);
|
||||
}
|
||||
|
||||
|
|
|
@ -142,8 +142,10 @@ static struct snd_seq_queue *queue_new(int owner, int locked)
|
|||
static void queue_delete(struct snd_seq_queue *q)
|
||||
{
|
||||
/* stop and release the timer */
|
||||
mutex_lock(&q->timer_mutex);
|
||||
snd_seq_timer_stop(q->timer);
|
||||
snd_seq_timer_close(q);
|
||||
mutex_unlock(&q->timer_mutex);
|
||||
/* wait until access free */
|
||||
snd_use_lock_sync(&q->use_lock);
|
||||
/* release resources... */
|
||||
|
|
|
@ -468,7 +468,7 @@ static int snd_virmidi_dev_unregister(struct snd_rawmidi *rmidi)
|
|||
/*
|
||||
*
|
||||
*/
|
||||
static struct snd_rawmidi_global_ops snd_virmidi_global_ops = {
|
||||
static const struct snd_rawmidi_global_ops snd_virmidi_global_ops = {
|
||||
.dev_register = snd_virmidi_dev_register,
|
||||
.dev_unregister = snd_virmidi_dev_unregister,
|
||||
};
|
||||
|
|
|
@ -73,7 +73,7 @@ struct snd_timer_user {
|
|||
struct timespec tstamp; /* trigger tstamp */
|
||||
wait_queue_head_t qchange_sleep;
|
||||
struct fasync_struct *fasync;
|
||||
struct mutex tread_sem;
|
||||
struct mutex ioctl_lock;
|
||||
};
|
||||
|
||||
/* list of timers */
|
||||
|
@ -215,11 +215,13 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
|
|||
slave->slave_id == master->slave_id) {
|
||||
list_move_tail(&slave->open_list, &master->slave_list_head);
|
||||
spin_lock_irq(&slave_active_lock);
|
||||
spin_lock(&master->timer->lock);
|
||||
slave->master = master;
|
||||
slave->timer = master->timer;
|
||||
if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
|
||||
list_add_tail(&slave->active_list,
|
||||
&master->slave_active_head);
|
||||
spin_unlock(&master->timer->lock);
|
||||
spin_unlock_irq(&slave_active_lock);
|
||||
}
|
||||
}
|
||||
|
@ -299,8 +301,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int _snd_timer_stop(struct snd_timer_instance *timeri,
|
||||
int keep_flag, int event);
|
||||
static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
|
||||
|
||||
/*
|
||||
* close a timer instance
|
||||
|
@ -342,19 +343,22 @@ int snd_timer_close(struct snd_timer_instance *timeri)
|
|||
spin_unlock_irq(&timer->lock);
|
||||
mutex_lock(®ister_mutex);
|
||||
list_del(&timeri->open_list);
|
||||
if (timer && list_empty(&timer->open_list_head) &&
|
||||
if (list_empty(&timer->open_list_head) &&
|
||||
timer->hw.close)
|
||||
timer->hw.close(timer);
|
||||
/* remove slave links */
|
||||
spin_lock_irq(&slave_active_lock);
|
||||
spin_lock(&timer->lock);
|
||||
list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
|
||||
open_list) {
|
||||
spin_lock_irq(&slave_active_lock);
|
||||
_snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
|
||||
list_move_tail(&slave->open_list, &snd_timer_slave_list);
|
||||
slave->master = NULL;
|
||||
slave->timer = NULL;
|
||||
spin_unlock_irq(&slave_active_lock);
|
||||
list_del_init(&slave->ack_list);
|
||||
list_del_init(&slave->active_list);
|
||||
}
|
||||
spin_unlock(&timer->lock);
|
||||
spin_unlock_irq(&slave_active_lock);
|
||||
mutex_unlock(®ister_mutex);
|
||||
}
|
||||
out:
|
||||
|
@ -441,9 +445,12 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
|
|||
|
||||
spin_lock_irqsave(&slave_active_lock, flags);
|
||||
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
|
||||
if (timeri->master)
|
||||
if (timeri->master && timeri->timer) {
|
||||
spin_lock(&timeri->timer->lock);
|
||||
list_add_tail(&timeri->active_list,
|
||||
&timeri->master->slave_active_head);
|
||||
spin_unlock(&timeri->timer->lock);
|
||||
}
|
||||
spin_unlock_irqrestore(&slave_active_lock, flags);
|
||||
return 1; /* delayed start */
|
||||
}
|
||||
|
@ -476,8 +483,7 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
|
|||
return result;
|
||||
}
|
||||
|
||||
static int _snd_timer_stop(struct snd_timer_instance * timeri,
|
||||
int keep_flag, int event)
|
||||
static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
|
||||
{
|
||||
struct snd_timer *timer;
|
||||
unsigned long flags;
|
||||
|
@ -486,11 +492,11 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
|
|||
return -ENXIO;
|
||||
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
|
||||
if (!keep_flag) {
|
||||
spin_lock_irqsave(&slave_active_lock, flags);
|
||||
timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
|
||||
spin_unlock_irqrestore(&slave_active_lock, flags);
|
||||
}
|
||||
spin_lock_irqsave(&slave_active_lock, flags);
|
||||
timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
|
||||
list_del_init(&timeri->ack_list);
|
||||
list_del_init(&timeri->active_list);
|
||||
spin_unlock_irqrestore(&slave_active_lock, flags);
|
||||
goto __end;
|
||||
}
|
||||
timer = timeri->timer;
|
||||
|
@ -511,9 +517,7 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!keep_flag)
|
||||
timeri->flags &=
|
||||
~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
|
||||
timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
|
||||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
__end:
|
||||
if (event != SNDRV_TIMER_EVENT_RESOLUTION)
|
||||
|
@ -532,7 +536,7 @@ int snd_timer_stop(struct snd_timer_instance *timeri)
|
|||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
err = _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_STOP);
|
||||
err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP);
|
||||
if (err < 0)
|
||||
return err;
|
||||
timer = timeri->timer;
|
||||
|
@ -576,7 +580,7 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
|
|||
*/
|
||||
int snd_timer_pause(struct snd_timer_instance * timeri)
|
||||
{
|
||||
return _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_PAUSE);
|
||||
return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -694,7 +698,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
|
|||
} else {
|
||||
ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
|
||||
if (--timer->running)
|
||||
list_del(&ti->active_list);
|
||||
list_del_init(&ti->active_list);
|
||||
}
|
||||
if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
|
||||
(ti->flags & SNDRV_TIMER_IFLG_FAST))
|
||||
|
@ -1253,7 +1257,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file)
|
|||
return -ENOMEM;
|
||||
spin_lock_init(&tu->qlock);
|
||||
init_waitqueue_head(&tu->qchange_sleep);
|
||||
mutex_init(&tu->tread_sem);
|
||||
mutex_init(&tu->ioctl_lock);
|
||||
tu->ticks = 1;
|
||||
tu->queue_size = 128;
|
||||
tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
|
||||
|
@ -1273,8 +1277,10 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
|
|||
if (file->private_data) {
|
||||
tu = file->private_data;
|
||||
file->private_data = NULL;
|
||||
mutex_lock(&tu->ioctl_lock);
|
||||
if (tu->timeri)
|
||||
snd_timer_close(tu->timeri);
|
||||
mutex_unlock(&tu->ioctl_lock);
|
||||
kfree(tu->queue);
|
||||
kfree(tu->tqueue);
|
||||
kfree(tu);
|
||||
|
@ -1512,7 +1518,6 @@ static int snd_timer_user_tselect(struct file *file,
|
|||
int err = 0;
|
||||
|
||||
tu = file->private_data;
|
||||
mutex_lock(&tu->tread_sem);
|
||||
if (tu->timeri) {
|
||||
snd_timer_close(tu->timeri);
|
||||
tu->timeri = NULL;
|
||||
|
@ -1556,7 +1561,6 @@ static int snd_timer_user_tselect(struct file *file,
|
|||
}
|
||||
|
||||
__err:
|
||||
mutex_unlock(&tu->tread_sem);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1769,7 +1773,7 @@ enum {
|
|||
SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
|
||||
};
|
||||
|
||||
static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
|
||||
static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct snd_timer_user *tu;
|
||||
|
@ -1786,17 +1790,11 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
|
|||
{
|
||||
int xarg;
|
||||
|
||||
mutex_lock(&tu->tread_sem);
|
||||
if (tu->timeri) { /* too late */
|
||||
mutex_unlock(&tu->tread_sem);
|
||||
if (tu->timeri) /* too late */
|
||||
return -EBUSY;
|
||||
}
|
||||
if (get_user(xarg, p)) {
|
||||
mutex_unlock(&tu->tread_sem);
|
||||
if (get_user(xarg, p))
|
||||
return -EFAULT;
|
||||
}
|
||||
tu->tread = xarg ? 1 : 0;
|
||||
mutex_unlock(&tu->tread_sem);
|
||||
return 0;
|
||||
}
|
||||
case SNDRV_TIMER_IOCTL_GINFO:
|
||||
|
@ -1829,6 +1827,18 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
|
|||
return -ENOTTY;
|
||||
}
|
||||
|
||||
static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct snd_timer_user *tu = file->private_data;
|
||||
long ret;
|
||||
|
||||
mutex_lock(&tu->ioctl_lock);
|
||||
ret = __snd_timer_user_ioctl(file, cmd, arg);
|
||||
mutex_unlock(&tu->ioctl_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snd_timer_user_fasync(int fd, struct file * file, int on)
|
||||
{
|
||||
struct snd_timer_user *tu;
|
||||
|
|
|
@ -351,7 +351,7 @@ static void dummy_systimer_free(struct snd_pcm_substream *substream)
|
|||
kfree(substream->runtime->private_data);
|
||||
}
|
||||
|
||||
static struct dummy_timer_ops dummy_systimer_ops = {
|
||||
static const struct dummy_timer_ops dummy_systimer_ops = {
|
||||
.create = dummy_systimer_create,
|
||||
.free = dummy_systimer_free,
|
||||
.prepare = dummy_systimer_prepare,
|
||||
|
@ -475,7 +475,7 @@ static void dummy_hrtimer_free(struct snd_pcm_substream *substream)
|
|||
kfree(dpcm);
|
||||
}
|
||||
|
||||
static struct dummy_timer_ops dummy_hrtimer_ops = {
|
||||
static const struct dummy_timer_ops dummy_hrtimer_ops = {
|
||||
.create = dummy_hrtimer_create,
|
||||
.free = dummy_hrtimer_free,
|
||||
.prepare = dummy_hrtimer_prepare,
|
||||
|
|
|
@ -39,6 +39,7 @@ config SND_OXFW
|
|||
* Mackie(Loud) d.2 pro/d.4 pro
|
||||
* Mackie(Loud) U.420/U.420d
|
||||
* TASCAM FireOne
|
||||
* Stanton Controllers & Systems 1 Deck/Mixer
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-oxfw.
|
||||
|
@ -53,17 +54,6 @@ config SND_ISIGHT
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-isight.
|
||||
|
||||
config SND_SCS1X
|
||||
tristate "Stanton Control System 1 MIDI"
|
||||
select SND_FIREWIRE_LIB
|
||||
help
|
||||
Say Y here to include support for the MIDI ports of the Stanton
|
||||
SCS.1d/SCS.1m DJ controllers. (SCS.1m audio is still handled
|
||||
by FFADO.)
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-scs1x.
|
||||
|
||||
config SND_FIREWORKS
|
||||
tristate "Echo Fireworks board module support"
|
||||
select SND_FIREWIRE_LIB
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
|
||||
fcp.o cmp.o amdtp-stream.o amdtp-am824.o
|
||||
snd-isight-objs := isight.o
|
||||
snd-scs1x-objs := scs1x.o
|
||||
|
||||
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
|
||||
obj-$(CONFIG_SND_DICE) += dice/
|
||||
obj-$(CONFIG_SND_OXFW) += oxfw/
|
||||
obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
|
||||
obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
|
||||
obj-$(CONFIG_SND_FIREWORKS) += fireworks/
|
||||
obj-$(CONFIG_SND_BEBOB) += bebob/
|
||||
obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include "dice.h"
|
||||
|
||||
#define NOTIFICATION_TIMEOUT_MS 100
|
||||
#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
|
||||
|
||||
static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
|
||||
u64 offset)
|
||||
|
@ -65,16 +65,15 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
|
|||
static int set_clock_info(struct snd_dice *dice,
|
||||
unsigned int rate, unsigned int source)
|
||||
{
|
||||
unsigned int retries = 3;
|
||||
unsigned int i;
|
||||
__be32 info;
|
||||
u32 mask;
|
||||
u32 clock;
|
||||
int err;
|
||||
retry:
|
||||
|
||||
err = get_clock_info(dice, &info);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
clock = be32_to_cpu(info);
|
||||
if (source != UINT_MAX) {
|
||||
|
@ -87,10 +86,8 @@ retry:
|
|||
if (snd_dice_rates[i] == rate)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(snd_dice_rates)) {
|
||||
err = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
if (i == ARRAY_SIZE(snd_dice_rates))
|
||||
return -EINVAL;
|
||||
|
||||
mask = CLOCK_RATE_MASK;
|
||||
clock &= ~mask;
|
||||
|
@ -104,25 +101,13 @@ retry:
|
|||
err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
|
||||
&info, 4);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
/* Timeout means it's invalid request, probably bus reset occurred. */
|
||||
if (wait_for_completion_timeout(&dice->clock_accepted,
|
||||
msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
|
||||
if (retries-- == 0) {
|
||||
err = -ETIMEDOUT;
|
||||
goto end;
|
||||
}
|
||||
msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
err = snd_dice_transaction_reinit(dice);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
msleep(500); /* arbitrary */
|
||||
goto retry;
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
|
||||
|
@ -331,39 +316,60 @@ int snd_dice_transaction_reinit(struct snd_dice *dice)
|
|||
return register_notification_address(dice, false);
|
||||
}
|
||||
|
||||
int snd_dice_transaction_init(struct snd_dice *dice)
|
||||
static int get_subaddrs(struct snd_dice *dice)
|
||||
{
|
||||
struct fw_address_handler *handler = &dice->notification_handler;
|
||||
static const int min_values[10] = {
|
||||
10, 0x64 / 4,
|
||||
10, 0x18 / 4,
|
||||
10, 0x18 / 4,
|
||||
0, 0,
|
||||
0, 0,
|
||||
};
|
||||
__be32 *pointers;
|
||||
__be32 version;
|
||||
u32 data;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
/* Use the same way which dice_interface_check() does. */
|
||||
pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL);
|
||||
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
|
||||
GFP_KERNEL);
|
||||
if (pointers == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get offsets for sub-addresses */
|
||||
/*
|
||||
* Check that the sub address spaces exist and are located inside the
|
||||
* private address space. The minimum values are chosen so that all
|
||||
* minimally required registers are included.
|
||||
*/
|
||||
err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
|
||||
DICE_PRIVATE_SPACE,
|
||||
pointers, sizeof(__be32) * 10, 0);
|
||||
DICE_PRIVATE_SPACE, pointers,
|
||||
sizeof(__be32) * ARRAY_SIZE(min_values), 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
/* Allocation callback in address space over host controller */
|
||||
handler->length = 4;
|
||||
handler->address_callback = dice_notification;
|
||||
handler->callback_data = dice;
|
||||
err = fw_core_add_address_handler(handler, &fw_high_memory_region);
|
||||
if (err < 0) {
|
||||
handler->callback_data = NULL;
|
||||
goto end;
|
||||
for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
|
||||
data = be32_to_cpu(pointers[i]);
|
||||
if (data < min_values[i] || data >= 0x40000) {
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register the address space */
|
||||
err = register_notification_address(dice, true);
|
||||
if (err < 0) {
|
||||
fw_core_remove_address_handler(handler);
|
||||
handler->callback_data = NULL;
|
||||
/*
|
||||
* Check that the implemented DICE driver specification major version
|
||||
* number matches.
|
||||
*/
|
||||
err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DICE_PRIVATE_SPACE +
|
||||
be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
|
||||
&version, sizeof(version), 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
|
||||
dev_err(&dice->unit->device,
|
||||
"unknown DICE version: 0x%08x\n", be32_to_cpu(version));
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -380,3 +386,32 @@ end:
|
|||
kfree(pointers);
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_dice_transaction_init(struct snd_dice *dice)
|
||||
{
|
||||
struct fw_address_handler *handler = &dice->notification_handler;
|
||||
int err;
|
||||
|
||||
err = get_subaddrs(dice);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Allocation callback in address space over host controller */
|
||||
handler->length = 4;
|
||||
handler->address_callback = dice_notification;
|
||||
handler->callback_data = dice;
|
||||
err = fw_core_add_address_handler(handler, &fw_high_memory_region);
|
||||
if (err < 0) {
|
||||
handler->callback_data = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Register the address space */
|
||||
err = register_notification_address(dice, true);
|
||||
if (err < 0) {
|
||||
fw_core_remove_address_handler(handler);
|
||||
handler->callback_data = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -18,27 +18,14 @@ MODULE_LICENSE("GPL v2");
|
|||
#define WEISS_CATEGORY_ID 0x00
|
||||
#define LOUD_CATEGORY_ID 0x10
|
||||
|
||||
static int dice_interface_check(struct fw_unit *unit)
|
||||
#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
|
||||
|
||||
static int check_dice_category(struct fw_unit *unit)
|
||||
{
|
||||
static const int min_values[10] = {
|
||||
10, 0x64 / 4,
|
||||
10, 0x18 / 4,
|
||||
10, 0x18 / 4,
|
||||
0, 0,
|
||||
0, 0,
|
||||
};
|
||||
struct fw_device *device = fw_parent_device(unit);
|
||||
struct fw_csr_iterator it;
|
||||
int key, val, vendor = -1, model = -1, err;
|
||||
unsigned int category, i;
|
||||
__be32 *pointers;
|
||||
u32 value;
|
||||
__be32 version;
|
||||
|
||||
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
|
||||
GFP_KERNEL);
|
||||
if (pointers == NULL)
|
||||
return -ENOMEM;
|
||||
int key, val, vendor = -1, model = -1;
|
||||
unsigned int category;
|
||||
|
||||
/*
|
||||
* Check that GUID and unit directory are constructed according to DICE
|
||||
|
@ -64,51 +51,10 @@ static int dice_interface_check(struct fw_unit *unit)
|
|||
else
|
||||
category = DICE_CATEGORY_ID;
|
||||
if (device->config_rom[3] != ((vendor << 8) | category) ||
|
||||
device->config_rom[4] >> 22 != model) {
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
device->config_rom[4] >> 22 != model)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Check that the sub address spaces exist and are located inside the
|
||||
* private address space. The minimum values are chosen so that all
|
||||
* minimally required registers are included.
|
||||
*/
|
||||
err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
|
||||
DICE_PRIVATE_SPACE, pointers,
|
||||
sizeof(__be32) * ARRAY_SIZE(min_values), 0);
|
||||
if (err < 0) {
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
|
||||
value = be32_to_cpu(pointers[i]);
|
||||
if (value < min_values[i] || value >= 0x40000) {
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the implemented DICE driver specification major version
|
||||
* number matches.
|
||||
*/
|
||||
err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DICE_PRIVATE_SPACE +
|
||||
be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
|
||||
&version, 4, 0);
|
||||
if (err < 0) {
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
|
||||
dev_err(&unit->device,
|
||||
"unknown DICE version: 0x%08x\n", be32_to_cpu(version));
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int highest_supported_mode_rate(struct snd_dice *dice,
|
||||
|
@ -231,6 +177,16 @@ static void dice_card_strings(struct snd_dice *dice)
|
|||
strcpy(card->mixername, "DICE");
|
||||
}
|
||||
|
||||
static void dice_free(struct snd_dice *dice)
|
||||
{
|
||||
snd_dice_stream_destroy_duplex(dice);
|
||||
snd_dice_transaction_destroy(dice);
|
||||
fw_unit_put(dice->unit);
|
||||
|
||||
mutex_destroy(&dice->mutex);
|
||||
kfree(dice);
|
||||
}
|
||||
|
||||
/*
|
||||
* This module releases the FireWire unit data after all ALSA character devices
|
||||
* are released by applications. This is for releasing stream data or finishing
|
||||
|
@ -239,39 +195,21 @@ static void dice_card_strings(struct snd_dice *dice)
|
|||
*/
|
||||
static void dice_card_free(struct snd_card *card)
|
||||
{
|
||||
struct snd_dice *dice = card->private_data;
|
||||
|
||||
snd_dice_stream_destroy_duplex(dice);
|
||||
snd_dice_transaction_destroy(dice);
|
||||
fw_unit_put(dice->unit);
|
||||
|
||||
mutex_destroy(&dice->mutex);
|
||||
dice_free(card->private_data);
|
||||
}
|
||||
|
||||
static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
|
||||
static void do_registration(struct work_struct *work)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_dice *dice;
|
||||
struct snd_dice *dice = container_of(work, struct snd_dice, dwork.work);
|
||||
int err;
|
||||
|
||||
err = dice_interface_check(unit);
|
||||
if (dice->registered)
|
||||
return;
|
||||
|
||||
err = snd_card_new(&dice->unit->device, -1, NULL, THIS_MODULE, 0,
|
||||
&dice->card);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
|
||||
sizeof(*dice), &card);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
dice = card->private_data;
|
||||
dice->card = card;
|
||||
dice->unit = fw_unit_get(unit);
|
||||
card->private_free = dice_card_free;
|
||||
|
||||
spin_lock_init(&dice->lock);
|
||||
mutex_init(&dice->mutex);
|
||||
init_completion(&dice->clock_accepted);
|
||||
init_waitqueue_head(&dice->hwdep_wait);
|
||||
return;
|
||||
|
||||
err = snd_dice_transaction_init(dice);
|
||||
if (err < 0)
|
||||
|
@ -283,56 +221,131 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
|
|||
|
||||
dice_card_strings(dice);
|
||||
|
||||
snd_dice_create_proc(dice);
|
||||
|
||||
err = snd_dice_create_pcm(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dice_create_midi(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dice_create_hwdep(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
snd_dice_create_proc(dice);
|
||||
|
||||
err = snd_dice_create_midi(dice);
|
||||
err = snd_card_register(dice->card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* After registered, dice instance can be released corresponding to
|
||||
* releasing the sound card instance.
|
||||
*/
|
||||
dice->card->private_free = dice_card_free;
|
||||
dice->card->private_data = dice;
|
||||
dice->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_dice_transaction_destroy(dice);
|
||||
snd_card_free(dice->card);
|
||||
dev_info(&dice->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static void schedule_registration(struct snd_dice *dice)
|
||||
{
|
||||
struct fw_card *fw_card = fw_parent_device(dice->unit)->card;
|
||||
u64 now, delay;
|
||||
|
||||
now = get_jiffies_64();
|
||||
delay = fw_card->reset_jiffies + msecs_to_jiffies(PROBE_DELAY_MS);
|
||||
|
||||
if (time_after64(delay, now))
|
||||
delay -= now;
|
||||
else
|
||||
delay = 0;
|
||||
|
||||
mod_delayed_work(system_wq, &dice->dwork, delay);
|
||||
}
|
||||
|
||||
static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
|
||||
{
|
||||
struct snd_dice *dice;
|
||||
int err;
|
||||
|
||||
err = check_dice_category(unit);
|
||||
if (err < 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* Allocate this independent of sound card instance. */
|
||||
dice = kzalloc(sizeof(struct snd_dice), GFP_KERNEL);
|
||||
if (dice == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dice->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, dice);
|
||||
|
||||
spin_lock_init(&dice->lock);
|
||||
mutex_init(&dice->mutex);
|
||||
init_completion(&dice->clock_accepted);
|
||||
init_waitqueue_head(&dice->hwdep_wait);
|
||||
|
||||
err = snd_dice_stream_init_duplex(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_dice_stream_destroy_duplex(dice);
|
||||
goto error;
|
||||
dice_free(dice);
|
||||
return err;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&unit->device, dice);
|
||||
end:
|
||||
return err;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&dice->dwork, do_registration);
|
||||
schedule_registration(dice);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dice_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_dice *dice = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(dice->card);
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&dice->dwork);
|
||||
|
||||
if (dice->registered) {
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(dice->card);
|
||||
} else {
|
||||
/* Don't forget this case. */
|
||||
dice_free(dice);
|
||||
}
|
||||
}
|
||||
|
||||
static void dice_bus_reset(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_dice *dice = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!dice->registered)
|
||||
schedule_registration(dice);
|
||||
|
||||
/* The handler address register becomes initialized. */
|
||||
snd_dice_transaction_reinit(dice);
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
snd_dice_stream_update_duplex(dice);
|
||||
mutex_unlock(&dice->mutex);
|
||||
/*
|
||||
* After registration, userspace can start packet streaming, then this
|
||||
* code block works fine.
|
||||
*/
|
||||
if (dice->registered) {
|
||||
mutex_lock(&dice->mutex);
|
||||
snd_dice_stream_update_duplex(dice);
|
||||
mutex_unlock(&dice->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
#define DICE_INTERFACE 0x000001
|
||||
|
|
|
@ -45,6 +45,9 @@ struct snd_dice {
|
|||
spinlock_t lock;
|
||||
struct mutex mutex;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
/* Offsets for sub-addresses */
|
||||
unsigned int global_offset;
|
||||
unsigned int rx_offset;
|
||||
|
|
|
@ -86,8 +86,8 @@ struct snd_efw {
|
|||
struct amdtp_stream rx_stream;
|
||||
struct cmp_connection out_conn;
|
||||
struct cmp_connection in_conn;
|
||||
atomic_t capture_substreams;
|
||||
atomic_t playback_substreams;
|
||||
unsigned int capture_substreams;
|
||||
unsigned int playback_substreams;
|
||||
|
||||
/* hardware metering parameters */
|
||||
unsigned int phys_out;
|
||||
|
|
|
@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
|||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
atomic_inc(&efw->capture_substreams);
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->capture_substreams++;
|
||||
err = snd_efw_stream_start_duplex(efw, 0);
|
||||
mutex_unlock(&efw->mutex);
|
||||
if (err < 0)
|
||||
snd_efw_stream_lock_release(efw);
|
||||
|
||||
|
@ -35,8 +37,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
|
|||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
atomic_inc(&efw->playback_substreams);
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->playback_substreams++;
|
||||
err = snd_efw_stream_start_duplex(efw, 0);
|
||||
mutex_unlock(&efw->mutex);
|
||||
if (err < 0)
|
||||
snd_efw_stream_lock_release(efw);
|
||||
end:
|
||||
|
@ -47,8 +51,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
|
|||
{
|
||||
struct snd_efw *efw = substream->rmidi->private_data;
|
||||
|
||||
atomic_dec(&efw->capture_substreams);
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->capture_substreams--;
|
||||
snd_efw_stream_stop_duplex(efw);
|
||||
mutex_unlock(&efw->mutex);
|
||||
|
||||
snd_efw_stream_lock_release(efw);
|
||||
return 0;
|
||||
|
@ -58,8 +64,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
|
|||
{
|
||||
struct snd_efw *efw = substream->rmidi->private_data;
|
||||
|
||||
atomic_dec(&efw->playback_substreams);
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->playback_substreams--;
|
||||
snd_efw_stream_stop_duplex(efw);
|
||||
mutex_unlock(&efw->mutex);
|
||||
|
||||
snd_efw_stream_lock_release(efw);
|
||||
return 0;
|
||||
|
|
|
@ -251,8 +251,11 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||
atomic_inc(&efw->capture_substreams);
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->capture_substreams++;
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
|
||||
amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params));
|
||||
|
||||
|
@ -269,8 +272,11 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||
atomic_inc(&efw->playback_substreams);
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->playback_substreams++;
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
|
||||
amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params));
|
||||
|
||||
|
@ -281,8 +287,11 @@ static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
|
|||
{
|
||||
struct snd_efw *efw = substream->private_data;
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
atomic_dec(&efw->capture_substreams);
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->capture_substreams--;
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
|
||||
snd_efw_stream_stop_duplex(efw);
|
||||
|
||||
|
@ -292,8 +301,11 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
|||
{
|
||||
struct snd_efw *efw = substream->private_data;
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
atomic_dec(&efw->playback_substreams);
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->playback_substreams--;
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
|
||||
snd_efw_stream_stop_duplex(efw);
|
||||
|
||||
|
|
|
@ -209,16 +209,13 @@ end:
|
|||
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
|
||||
{
|
||||
struct amdtp_stream *master, *slave;
|
||||
atomic_t *slave_substreams;
|
||||
unsigned int slave_substreams;
|
||||
enum cip_flags sync_mode;
|
||||
unsigned int curr_rate;
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&efw->mutex);
|
||||
|
||||
/* Need no substreams */
|
||||
if ((atomic_read(&efw->playback_substreams) == 0) &&
|
||||
(atomic_read(&efw->capture_substreams) == 0))
|
||||
if (efw->playback_substreams == 0 && efw->capture_substreams == 0)
|
||||
goto end;
|
||||
|
||||
err = get_sync_mode(efw, &sync_mode);
|
||||
|
@ -227,11 +224,11 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
|
|||
if (sync_mode == CIP_SYNC_TO_DEVICE) {
|
||||
master = &efw->tx_stream;
|
||||
slave = &efw->rx_stream;
|
||||
slave_substreams = &efw->playback_substreams;
|
||||
slave_substreams = efw->playback_substreams;
|
||||
} else {
|
||||
master = &efw->rx_stream;
|
||||
slave = &efw->tx_stream;
|
||||
slave_substreams = &efw->capture_substreams;
|
||||
slave_substreams = efw->capture_substreams;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -277,7 +274,7 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
|
|||
}
|
||||
|
||||
/* start slave if needed */
|
||||
if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
|
||||
if (slave_substreams > 0 && !amdtp_stream_running(slave)) {
|
||||
err = start_stream(efw, slave, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&efw->unit->device,
|
||||
|
@ -286,37 +283,32 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
|
|||
}
|
||||
}
|
||||
end:
|
||||
mutex_unlock(&efw->mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
|
||||
{
|
||||
struct amdtp_stream *master, *slave;
|
||||
atomic_t *master_substreams, *slave_substreams;
|
||||
unsigned int master_substreams, slave_substreams;
|
||||
|
||||
if (efw->master == &efw->rx_stream) {
|
||||
slave = &efw->tx_stream;
|
||||
master = &efw->rx_stream;
|
||||
slave_substreams = &efw->capture_substreams;
|
||||
master_substreams = &efw->playback_substreams;
|
||||
slave_substreams = efw->capture_substreams;
|
||||
master_substreams = efw->playback_substreams;
|
||||
} else {
|
||||
slave = &efw->rx_stream;
|
||||
master = &efw->tx_stream;
|
||||
slave_substreams = &efw->playback_substreams;
|
||||
master_substreams = &efw->capture_substreams;
|
||||
slave_substreams = efw->playback_substreams;
|
||||
master_substreams = efw->capture_substreams;
|
||||
}
|
||||
|
||||
mutex_lock(&efw->mutex);
|
||||
|
||||
if (atomic_read(slave_substreams) == 0) {
|
||||
if (slave_substreams == 0) {
|
||||
stop_stream(efw, slave);
|
||||
|
||||
if (atomic_read(master_substreams) == 0)
|
||||
if (master_substreams == 0)
|
||||
stop_stream(efw, master);
|
||||
}
|
||||
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
|
||||
void snd_efw_stream_update_duplex(struct snd_efw *efw)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
|
||||
oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o
|
||||
snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-pcm.o oxfw-proc.o \
|
||||
oxfw-midi.o oxfw-hwdep.o oxfw-spkr.o oxfw-scs1x.o oxfw.o
|
||||
obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
|
||||
|
|
406
sound/firewire/oxfw/oxfw-scs1x.c
Normal file
406
sound/firewire/oxfw/oxfw-scs1x.c
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* oxfw-scs1x.c - a part of driver for OXFW970/971 based devices
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
* Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "oxfw.h"
|
||||
|
||||
#define HSS1394_ADDRESS 0xc007dedadadaULL
|
||||
#define HSS1394_MAX_PACKET_SIZE 64
|
||||
#define HSS1394_TAG_USER_DATA 0x00
|
||||
#define HSS1394_TAG_CHANGE_ADDRESS 0xf1
|
||||
|
||||
struct fw_scs1x {
|
||||
struct fw_address_handler hss_handler;
|
||||
u8 input_escape_count;
|
||||
struct snd_rawmidi_substream *input;
|
||||
|
||||
/* For MIDI playback. */
|
||||
struct snd_rawmidi_substream *output;
|
||||
bool output_idle;
|
||||
u8 output_status;
|
||||
u8 output_bytes;
|
||||
bool output_escaped;
|
||||
bool output_escape_high_nibble;
|
||||
struct tasklet_struct tasklet;
|
||||
wait_queue_head_t idle_wait;
|
||||
u8 buffer[HSS1394_MAX_PACKET_SIZE];
|
||||
bool transaction_running;
|
||||
struct fw_transaction transaction;
|
||||
struct fw_device *fw_dev;
|
||||
};
|
||||
|
||||
static const u8 sysex_escape_prefix[] = {
|
||||
0xf0, /* SysEx begin */
|
||||
0x00, 0x01, 0x60, /* Stanton DJ */
|
||||
0x48, 0x53, 0x53, /* "HSS" */
|
||||
};
|
||||
|
||||
static void midi_input_escaped_byte(struct snd_rawmidi_substream *stream,
|
||||
u8 byte)
|
||||
{
|
||||
u8 nibbles[2];
|
||||
|
||||
nibbles[0] = byte >> 4;
|
||||
nibbles[1] = byte & 0x0f;
|
||||
snd_rawmidi_receive(stream, nibbles, 2);
|
||||
}
|
||||
|
||||
static void midi_input_byte(struct fw_scs1x *scs,
|
||||
struct snd_rawmidi_substream *stream, u8 byte)
|
||||
{
|
||||
const u8 eox = 0xf7;
|
||||
|
||||
if (scs->input_escape_count > 0) {
|
||||
midi_input_escaped_byte(stream, byte);
|
||||
scs->input_escape_count--;
|
||||
if (scs->input_escape_count == 0)
|
||||
snd_rawmidi_receive(stream, &eox, sizeof(eox));
|
||||
} else if (byte == 0xf9) {
|
||||
snd_rawmidi_receive(stream, sysex_escape_prefix,
|
||||
ARRAY_SIZE(sysex_escape_prefix));
|
||||
midi_input_escaped_byte(stream, 0x00);
|
||||
midi_input_escaped_byte(stream, 0xf9);
|
||||
scs->input_escape_count = 3;
|
||||
} else {
|
||||
snd_rawmidi_receive(stream, &byte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void midi_input_packet(struct fw_scs1x *scs,
|
||||
struct snd_rawmidi_substream *stream,
|
||||
const u8 *data, unsigned int bytes)
|
||||
{
|
||||
unsigned int i;
|
||||
const u8 eox = 0xf7;
|
||||
|
||||
if (data[0] == HSS1394_TAG_USER_DATA) {
|
||||
for (i = 1; i < bytes; ++i)
|
||||
midi_input_byte(scs, stream, data[i]);
|
||||
} else {
|
||||
snd_rawmidi_receive(stream, sysex_escape_prefix,
|
||||
ARRAY_SIZE(sysex_escape_prefix));
|
||||
for (i = 0; i < bytes; ++i)
|
||||
midi_input_escaped_byte(stream, data[i]);
|
||||
snd_rawmidi_receive(stream, &eox, sizeof(eox));
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_hss(struct fw_card *card, struct fw_request *request,
|
||||
int tcode, int destination, int source, int generation,
|
||||
unsigned long long offset, void *data, size_t length,
|
||||
void *callback_data)
|
||||
{
|
||||
struct fw_scs1x *scs = callback_data;
|
||||
struct snd_rawmidi_substream *stream;
|
||||
int rcode;
|
||||
|
||||
if (offset != scs->hss_handler.offset) {
|
||||
rcode = RCODE_ADDRESS_ERROR;
|
||||
goto end;
|
||||
}
|
||||
if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
|
||||
tcode != TCODE_WRITE_BLOCK_REQUEST) {
|
||||
rcode = RCODE_TYPE_ERROR;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (length >= 1) {
|
||||
stream = ACCESS_ONCE(scs->input);
|
||||
if (stream)
|
||||
midi_input_packet(scs, stream, data, length);
|
||||
}
|
||||
|
||||
rcode = RCODE_COMPLETE;
|
||||
end:
|
||||
fw_send_response(card, request, rcode);
|
||||
}
|
||||
|
||||
static void scs_write_callback(struct fw_card *card, int rcode,
|
||||
void *data, size_t length, void *callback_data)
|
||||
{
|
||||
struct fw_scs1x *scs = callback_data;
|
||||
|
||||
if (rcode == RCODE_GENERATION)
|
||||
; /* TODO: retry this packet */
|
||||
|
||||
scs->transaction_running = false;
|
||||
tasklet_schedule(&scs->tasklet);
|
||||
}
|
||||
|
||||
static bool is_valid_running_status(u8 status)
|
||||
{
|
||||
return status >= 0x80 && status <= 0xef;
|
||||
}
|
||||
|
||||
static bool is_one_byte_cmd(u8 status)
|
||||
{
|
||||
return status == 0xf6 ||
|
||||
status >= 0xf8;
|
||||
}
|
||||
|
||||
static bool is_two_bytes_cmd(u8 status)
|
||||
{
|
||||
return (status >= 0xc0 && status <= 0xdf) ||
|
||||
status == 0xf1 ||
|
||||
status == 0xf3;
|
||||
}
|
||||
|
||||
static bool is_three_bytes_cmd(u8 status)
|
||||
{
|
||||
return (status >= 0x80 && status <= 0xbf) ||
|
||||
(status >= 0xe0 && status <= 0xef) ||
|
||||
status == 0xf2;
|
||||
}
|
||||
|
||||
static bool is_invalid_cmd(u8 status)
|
||||
{
|
||||
return status == 0xf4 ||
|
||||
status == 0xf5 ||
|
||||
status == 0xf9 ||
|
||||
status == 0xfd;
|
||||
}
|
||||
|
||||
static void scs_output_tasklet(unsigned long data)
|
||||
{
|
||||
struct fw_scs1x *scs = (struct fw_scs1x *)data;
|
||||
struct snd_rawmidi_substream *stream;
|
||||
unsigned int i;
|
||||
u8 byte;
|
||||
int generation;
|
||||
|
||||
if (scs->transaction_running)
|
||||
return;
|
||||
|
||||
stream = ACCESS_ONCE(scs->output);
|
||||
if (!stream) {
|
||||
scs->output_idle = true;
|
||||
wake_up(&scs->idle_wait);
|
||||
return;
|
||||
}
|
||||
|
||||
i = scs->output_bytes;
|
||||
for (;;) {
|
||||
if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
|
||||
scs->output_bytes = i;
|
||||
scs->output_idle = true;
|
||||
wake_up(&scs->idle_wait);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Convert from real MIDI to what I think the device expects (no
|
||||
* running status, one command per packet, unescaped SysExs).
|
||||
*/
|
||||
if (scs->output_escaped && byte < 0x80) {
|
||||
if (scs->output_escape_high_nibble) {
|
||||
if (i < HSS1394_MAX_PACKET_SIZE) {
|
||||
scs->buffer[i] = byte << 4;
|
||||
scs->output_escape_high_nibble = false;
|
||||
}
|
||||
} else {
|
||||
scs->buffer[i++] |= byte & 0x0f;
|
||||
scs->output_escape_high_nibble = true;
|
||||
}
|
||||
} else if (byte < 0x80) {
|
||||
if (i == 1) {
|
||||
if (!is_valid_running_status(
|
||||
scs->output_status))
|
||||
continue;
|
||||
scs->buffer[0] = HSS1394_TAG_USER_DATA;
|
||||
scs->buffer[i++] = scs->output_status;
|
||||
}
|
||||
scs->buffer[i++] = byte;
|
||||
if ((i == 3 && is_two_bytes_cmd(scs->output_status)) ||
|
||||
(i == 4 && is_three_bytes_cmd(scs->output_status)))
|
||||
break;
|
||||
if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) &&
|
||||
!memcmp(scs->buffer + 1, sysex_escape_prefix,
|
||||
ARRAY_SIZE(sysex_escape_prefix))) {
|
||||
scs->output_escaped = true;
|
||||
scs->output_escape_high_nibble = true;
|
||||
i = 0;
|
||||
}
|
||||
if (i >= HSS1394_MAX_PACKET_SIZE)
|
||||
i = 1;
|
||||
} else if (byte == 0xf7) {
|
||||
if (scs->output_escaped) {
|
||||
if (i >= 1 && scs->output_escape_high_nibble &&
|
||||
scs->buffer[0] !=
|
||||
HSS1394_TAG_CHANGE_ADDRESS)
|
||||
break;
|
||||
} else {
|
||||
if (i > 1 && scs->output_status == 0xf0) {
|
||||
scs->buffer[i++] = 0xf7;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i = 1;
|
||||
scs->output_escaped = false;
|
||||
} else if (!is_invalid_cmd(byte) && byte < 0xf8) {
|
||||
i = 1;
|
||||
scs->buffer[0] = HSS1394_TAG_USER_DATA;
|
||||
scs->buffer[i++] = byte;
|
||||
scs->output_status = byte;
|
||||
scs->output_escaped = false;
|
||||
if (is_one_byte_cmd(byte))
|
||||
break;
|
||||
}
|
||||
}
|
||||
scs->output_bytes = 1;
|
||||
scs->output_escaped = false;
|
||||
|
||||
scs->transaction_running = true;
|
||||
generation = scs->fw_dev->generation;
|
||||
smp_rmb(); /* node_id vs. generation */
|
||||
fw_send_request(scs->fw_dev->card, &scs->transaction,
|
||||
TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
|
||||
generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
|
||||
scs->buffer, i, scs_write_callback, scs);
|
||||
}
|
||||
|
||||
static int midi_capture_open(struct snd_rawmidi_substream *stream)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int midi_capture_close(struct snd_rawmidi_substream *stream)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up)
|
||||
{
|
||||
struct fw_scs1x *scs = stream->rmidi->private_data;
|
||||
|
||||
if (up) {
|
||||
scs->input_escape_count = 0;
|
||||
ACCESS_ONCE(scs->input) = stream;
|
||||
} else {
|
||||
ACCESS_ONCE(scs->input) = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static struct snd_rawmidi_ops midi_capture_ops = {
|
||||
.open = midi_capture_open,
|
||||
.close = midi_capture_close,
|
||||
.trigger = midi_capture_trigger,
|
||||
};
|
||||
|
||||
static int midi_playback_open(struct snd_rawmidi_substream *stream)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int midi_playback_close(struct snd_rawmidi_substream *stream)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
|
||||
{
|
||||
struct fw_scs1x *scs = stream->rmidi->private_data;
|
||||
|
||||
if (up) {
|
||||
scs->output_status = 0;
|
||||
scs->output_bytes = 1;
|
||||
scs->output_escaped = false;
|
||||
scs->output_idle = false;
|
||||
|
||||
ACCESS_ONCE(scs->output) = stream;
|
||||
tasklet_schedule(&scs->tasklet);
|
||||
} else {
|
||||
ACCESS_ONCE(scs->output) = NULL;
|
||||
}
|
||||
}
|
||||
static void midi_playback_drain(struct snd_rawmidi_substream *stream)
|
||||
{
|
||||
struct fw_scs1x *scs = stream->rmidi->private_data;
|
||||
|
||||
wait_event(scs->idle_wait, scs->output_idle);
|
||||
}
|
||||
|
||||
static struct snd_rawmidi_ops midi_playback_ops = {
|
||||
.open = midi_playback_open,
|
||||
.close = midi_playback_close,
|
||||
.trigger = midi_playback_trigger,
|
||||
.drain = midi_playback_drain,
|
||||
};
|
||||
static int register_address(struct snd_oxfw *oxfw)
|
||||
{
|
||||
struct fw_scs1x *scs = oxfw->spec;
|
||||
__be64 data;
|
||||
|
||||
data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
|
||||
scs->hss_handler.offset);
|
||||
return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST,
|
||||
HSS1394_ADDRESS, &data, sizeof(data), 0);
|
||||
}
|
||||
|
||||
static void remove_scs1x(struct snd_rawmidi *rmidi)
|
||||
{
|
||||
struct fw_scs1x *scs = rmidi->private_data;
|
||||
|
||||
fw_core_remove_address_handler(&scs->hss_handler);
|
||||
}
|
||||
|
||||
void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw)
|
||||
{
|
||||
register_address(oxfw);
|
||||
}
|
||||
|
||||
int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
|
||||
{
|
||||
struct snd_rawmidi *rmidi;
|
||||
struct fw_scs1x *scs;
|
||||
int err;
|
||||
|
||||
scs = kzalloc(sizeof(struct fw_scs1x), GFP_KERNEL);
|
||||
if (scs == NULL)
|
||||
return -ENOMEM;
|
||||
scs->fw_dev = fw_parent_device(oxfw->unit);
|
||||
oxfw->spec = scs;
|
||||
|
||||
/* Allocate own handler for imcoming asynchronous transaction. */
|
||||
scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE;
|
||||
scs->hss_handler.address_callback = handle_hss;
|
||||
scs->hss_handler.callback_data = scs;
|
||||
err = fw_core_add_address_handler(&scs->hss_handler,
|
||||
&fw_high_memory_region);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = register_address(oxfw);
|
||||
if (err < 0)
|
||||
goto err_allocated;
|
||||
|
||||
/* Use unique name for backward compatibility to scs1x module. */
|
||||
err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 1, 1, &rmidi);
|
||||
if (err < 0)
|
||||
goto err_allocated;
|
||||
rmidi->private_data = scs;
|
||||
rmidi->private_free = remove_scs1x;
|
||||
|
||||
snprintf(rmidi->name, sizeof(rmidi->name),
|
||||
"%s MIDI", oxfw->card->shortname);
|
||||
|
||||
rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
|
||||
SNDRV_RAWMIDI_INFO_OUTPUT |
|
||||
SNDRV_RAWMIDI_INFO_DUPLEX;
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
|
||||
&midi_capture_ops);
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
|
||||
&midi_playback_ops);
|
||||
|
||||
tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs);
|
||||
init_waitqueue_head(&scs->idle_wait);
|
||||
scs->output_idle = true;
|
||||
|
||||
return 0;
|
||||
err_allocated:
|
||||
fw_core_remove_address_handler(&scs->hss_handler);
|
||||
return err;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* oxfw_stream.c - a part of driver for OXFW970/971 based devices
|
||||
* oxfw-spkr.c - a part of driver for OXFW970/971 based devices
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
|
@ -7,6 +7,17 @@
|
|||
|
||||
#include "oxfw.h"
|
||||
|
||||
struct fw_spkr {
|
||||
bool mute;
|
||||
s16 volume[6];
|
||||
s16 volume_min;
|
||||
s16 volume_max;
|
||||
|
||||
unsigned int mixer_channels;
|
||||
u8 mute_fb_id;
|
||||
u8 volume_fb_id;
|
||||
};
|
||||
|
||||
enum control_action { CTL_READ, CTL_WRITE };
|
||||
enum control_attribute {
|
||||
CTL_MIN = 0x02,
|
||||
|
@ -14,8 +25,8 @@ enum control_attribute {
|
|||
CTL_CURRENT = 0x10,
|
||||
};
|
||||
|
||||
static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value,
|
||||
enum control_action action)
|
||||
static int avc_audio_feature_mute(struct fw_unit *unit, u8 fb_id, bool *value,
|
||||
enum control_action action)
|
||||
{
|
||||
u8 *buf;
|
||||
u8 response_ok;
|
||||
|
@ -35,7 +46,7 @@ static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value,
|
|||
buf[1] = 0x08; /* audio unit 0 */
|
||||
buf[2] = 0xb8; /* FUNCTION BLOCK */
|
||||
buf[3] = 0x81; /* function block type: feature */
|
||||
buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */
|
||||
buf[4] = fb_id; /* function block ID */
|
||||
buf[5] = 0x10; /* control attribute: current */
|
||||
buf[6] = 0x02; /* selector length */
|
||||
buf[7] = 0x00; /* audio channel number */
|
||||
|
@ -46,16 +57,16 @@ static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value,
|
|||
else
|
||||
buf[10] = *value ? 0x70 : 0x60;
|
||||
|
||||
err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe);
|
||||
err = fcp_avc_transaction(unit, buf, 11, buf, 11, 0x3fe);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (err < 11) {
|
||||
dev_err(&oxfw->unit->device, "short FCP response\n");
|
||||
dev_err(&unit->device, "short FCP response\n");
|
||||
err = -EIO;
|
||||
goto error;
|
||||
}
|
||||
if (buf[0] != response_ok) {
|
||||
dev_err(&oxfw->unit->device, "mute command failed\n");
|
||||
dev_err(&unit->device, "mute command failed\n");
|
||||
err = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
@ -70,10 +81,10 @@ error:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value,
|
||||
unsigned int channel,
|
||||
enum control_attribute attribute,
|
||||
enum control_action action)
|
||||
static int avc_audio_feature_volume(struct fw_unit *unit, u8 fb_id, s16 *value,
|
||||
unsigned int channel,
|
||||
enum control_attribute attribute,
|
||||
enum control_action action)
|
||||
{
|
||||
u8 *buf;
|
||||
u8 response_ok;
|
||||
|
@ -93,7 +104,7 @@ static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value,
|
|||
buf[1] = 0x08; /* audio unit 0 */
|
||||
buf[2] = 0xb8; /* FUNCTION BLOCK */
|
||||
buf[3] = 0x81; /* function block type: feature */
|
||||
buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */
|
||||
buf[4] = fb_id; /* function block ID */
|
||||
buf[5] = attribute; /* control attribute */
|
||||
buf[6] = 0x02; /* selector length */
|
||||
buf[7] = channel; /* audio channel number */
|
||||
|
@ -107,16 +118,16 @@ static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value,
|
|||
buf[11] = *value;
|
||||
}
|
||||
|
||||
err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe);
|
||||
err = fcp_avc_transaction(unit, buf, 12, buf, 12, 0x3fe);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (err < 12) {
|
||||
dev_err(&oxfw->unit->device, "short FCP response\n");
|
||||
dev_err(&unit->device, "short FCP response\n");
|
||||
err = -EIO;
|
||||
goto error;
|
||||
}
|
||||
if (buf[0] != response_ok) {
|
||||
dev_err(&oxfw->unit->device, "volume command failed\n");
|
||||
dev_err(&unit->device, "volume command failed\n");
|
||||
err = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
@ -131,75 +142,81 @@ error:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int oxfw_mute_get(struct snd_kcontrol *control,
|
||||
static int spkr_mute_get(struct snd_kcontrol *control,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct snd_oxfw *oxfw = control->private_data;
|
||||
struct fw_spkr *spkr = oxfw->spec;
|
||||
|
||||
value->value.integer.value[0] = !oxfw->mute;
|
||||
value->value.integer.value[0] = !spkr->mute;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxfw_mute_put(struct snd_kcontrol *control,
|
||||
static int spkr_mute_put(struct snd_kcontrol *control,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct snd_oxfw *oxfw = control->private_data;
|
||||
struct fw_spkr *spkr = oxfw->spec;
|
||||
bool mute;
|
||||
int err;
|
||||
|
||||
mute = !value->value.integer.value[0];
|
||||
|
||||
if (mute == oxfw->mute)
|
||||
if (mute == spkr->mute)
|
||||
return 0;
|
||||
|
||||
err = oxfw_mute_command(oxfw, &mute, CTL_WRITE);
|
||||
err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &mute,
|
||||
CTL_WRITE);
|
||||
if (err < 0)
|
||||
return err;
|
||||
oxfw->mute = mute;
|
||||
spkr->mute = mute;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int oxfw_volume_info(struct snd_kcontrol *control,
|
||||
static int spkr_volume_info(struct snd_kcontrol *control,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
struct snd_oxfw *oxfw = control->private_data;
|
||||
struct fw_spkr *spkr = oxfw->spec;
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
info->count = oxfw->device_info->mixer_channels;
|
||||
info->value.integer.min = oxfw->volume_min;
|
||||
info->value.integer.max = oxfw->volume_max;
|
||||
info->count = spkr->mixer_channels;
|
||||
info->value.integer.min = spkr->volume_min;
|
||||
info->value.integer.max = spkr->volume_max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
|
||||
|
||||
static int oxfw_volume_get(struct snd_kcontrol *control,
|
||||
static int spkr_volume_get(struct snd_kcontrol *control,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct snd_oxfw *oxfw = control->private_data;
|
||||
struct fw_spkr *spkr = oxfw->spec;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
|
||||
value->value.integer.value[channel_map[i]] = oxfw->volume[i];
|
||||
for (i = 0; i < spkr->mixer_channels; ++i)
|
||||
value->value.integer.value[channel_map[i]] = spkr->volume[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxfw_volume_put(struct snd_kcontrol *control,
|
||||
static int spkr_volume_put(struct snd_kcontrol *control,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct snd_oxfw *oxfw = control->private_data;
|
||||
struct fw_spkr *spkr = oxfw->spec;
|
||||
unsigned int i, changed_channels;
|
||||
bool equal_values = true;
|
||||
s16 volume;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
|
||||
if (value->value.integer.value[i] < oxfw->volume_min ||
|
||||
value->value.integer.value[i] > oxfw->volume_max)
|
||||
for (i = 0; i < spkr->mixer_channels; ++i) {
|
||||
if (value->value.integer.value[i] < spkr->volume_min ||
|
||||
value->value.integer.value[i] > spkr->volume_max)
|
||||
return -EINVAL;
|
||||
if (value->value.integer.value[i] !=
|
||||
value->value.integer.value[0])
|
||||
|
@ -207,67 +224,86 @@ static int oxfw_volume_put(struct snd_kcontrol *control,
|
|||
}
|
||||
|
||||
changed_channels = 0;
|
||||
for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
|
||||
for (i = 0; i < spkr->mixer_channels; ++i)
|
||||
if (value->value.integer.value[channel_map[i]] !=
|
||||
oxfw->volume[i])
|
||||
spkr->volume[i])
|
||||
changed_channels |= 1 << (i + 1);
|
||||
|
||||
if (equal_values && changed_channels != 0)
|
||||
changed_channels = 1 << 0;
|
||||
|
||||
for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) {
|
||||
for (i = 0; i <= spkr->mixer_channels; ++i) {
|
||||
volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
|
||||
if (changed_channels & (1 << i)) {
|
||||
err = oxfw_volume_command(oxfw, &volume, i,
|
||||
CTL_CURRENT, CTL_WRITE);
|
||||
err = avc_audio_feature_volume(oxfw->unit,
|
||||
spkr->volume_fb_id, &volume,
|
||||
i, CTL_CURRENT, CTL_WRITE);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (i > 0)
|
||||
oxfw->volume[i - 1] = volume;
|
||||
spkr->volume[i - 1] = volume;
|
||||
}
|
||||
|
||||
return changed_channels != 0;
|
||||
}
|
||||
|
||||
int snd_oxfw_create_mixer(struct snd_oxfw *oxfw)
|
||||
int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie)
|
||||
{
|
||||
static const struct snd_kcontrol_new controls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "PCM Playback Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = oxfw_mute_get,
|
||||
.put = oxfw_mute_put,
|
||||
.get = spkr_mute_get,
|
||||
.put = spkr_mute_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "PCM Playback Volume",
|
||||
.info = oxfw_volume_info,
|
||||
.get = oxfw_volume_get,
|
||||
.put = oxfw_volume_put,
|
||||
.info = spkr_volume_info,
|
||||
.get = spkr_volume_get,
|
||||
.put = spkr_volume_put,
|
||||
},
|
||||
};
|
||||
struct fw_spkr *spkr;
|
||||
unsigned int i, first_ch;
|
||||
int err;
|
||||
|
||||
err = oxfw_volume_command(oxfw, &oxfw->volume_min,
|
||||
0, CTL_MIN, CTL_READ);
|
||||
spkr = kzalloc(sizeof(struct fw_spkr), GFP_KERNEL);
|
||||
if (spkr == NULL)
|
||||
return -ENOMEM;
|
||||
oxfw->spec = spkr;
|
||||
|
||||
if (is_lacie) {
|
||||
spkr->mixer_channels = 1;
|
||||
spkr->mute_fb_id = 0x01;
|
||||
spkr->volume_fb_id = 0x01;
|
||||
} else {
|
||||
spkr->mixer_channels = 6;
|
||||
spkr->mute_fb_id = 0x01;
|
||||
spkr->volume_fb_id = 0x02;
|
||||
}
|
||||
|
||||
err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
|
||||
&spkr->volume_min, 0, CTL_MIN, CTL_READ);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = oxfw_volume_command(oxfw, &oxfw->volume_max,
|
||||
0, CTL_MAX, CTL_READ);
|
||||
err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
|
||||
&spkr->volume_max, 0, CTL_MAX, CTL_READ);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ);
|
||||
err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &spkr->mute,
|
||||
CTL_READ);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1;
|
||||
for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
|
||||
err = oxfw_volume_command(oxfw, &oxfw->volume[i],
|
||||
first_ch + i, CTL_CURRENT, CTL_READ);
|
||||
first_ch = spkr->mixer_channels == 1 ? 0 : 1;
|
||||
for (i = 0; i < spkr->mixer_channels; ++i) {
|
||||
err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
|
||||
&spkr->volume[i], first_ch + i,
|
||||
CTL_CURRENT, CTL_READ);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
#define VENDOR_BEHRINGER 0x001564
|
||||
#define VENDOR_LACIE 0x00d04b
|
||||
#define VENDOR_TASCAM 0x00022e
|
||||
#define OUI_STANTON 0x001260
|
||||
|
||||
#define MODEL_SATELLITE 0x00200f
|
||||
|
||||
|
@ -29,6 +30,13 @@ MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver");
|
|||
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("snd-firewire-speakers");
|
||||
MODULE_ALIAS("snd-scs1x");
|
||||
|
||||
struct compat_info {
|
||||
const char *driver_name;
|
||||
const char *vendor_name;
|
||||
const char *model_name;
|
||||
};
|
||||
|
||||
static bool detect_loud_models(struct fw_unit *unit)
|
||||
{
|
||||
|
@ -59,6 +67,7 @@ static bool detect_loud_models(struct fw_unit *unit)
|
|||
static int name_card(struct snd_oxfw *oxfw)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
|
||||
const struct compat_info *info;
|
||||
char vendor[24];
|
||||
char model[32];
|
||||
const char *d, *v, *m;
|
||||
|
@ -84,10 +93,12 @@ static int name_card(struct snd_oxfw *oxfw)
|
|||
be32_to_cpus(&firmware);
|
||||
|
||||
/* to apply card definitions */
|
||||
if (oxfw->device_info) {
|
||||
d = oxfw->device_info->driver_name;
|
||||
v = oxfw->device_info->vendor_name;
|
||||
m = oxfw->device_info->model_name;
|
||||
if (oxfw->entry->vendor_id == VENDOR_GRIFFIN ||
|
||||
oxfw->entry->vendor_id == VENDOR_LACIE) {
|
||||
info = (const struct compat_info *)oxfw->entry->driver_data;
|
||||
d = info->driver_name;
|
||||
v = info->vendor_name;
|
||||
m = info->model_name;
|
||||
} else {
|
||||
d = "OXFW";
|
||||
v = vendor;
|
||||
|
@ -129,16 +140,51 @@ static void oxfw_card_free(struct snd_card *card)
|
|||
kfree(oxfw->rx_stream_formats[i]);
|
||||
}
|
||||
|
||||
kfree(oxfw->spec);
|
||||
mutex_destroy(&oxfw->mutex);
|
||||
}
|
||||
|
||||
static void detect_quirks(struct snd_oxfw *oxfw)
|
||||
static int detect_quirks(struct snd_oxfw *oxfw)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
|
||||
struct fw_csr_iterator it;
|
||||
int key, val;
|
||||
int vendor, model;
|
||||
|
||||
/*
|
||||
* Add ALSA control elements for two models to keep compatibility to
|
||||
* old firewire-speaker module.
|
||||
*/
|
||||
if (oxfw->entry->vendor_id == VENDOR_GRIFFIN)
|
||||
return snd_oxfw_add_spkr(oxfw, false);
|
||||
if (oxfw->entry->vendor_id == VENDOR_LACIE)
|
||||
return snd_oxfw_add_spkr(oxfw, true);
|
||||
|
||||
/*
|
||||
* Stanton models supports asynchronous transactions for unique MIDI
|
||||
* messages.
|
||||
*/
|
||||
if (oxfw->entry->vendor_id == OUI_STANTON) {
|
||||
/* No physical MIDI ports. */
|
||||
oxfw->midi_input_ports = 0;
|
||||
oxfw->midi_output_ports = 0;
|
||||
|
||||
/* Output stream exists but no data channels are useful. */
|
||||
oxfw->has_output = false;
|
||||
|
||||
return snd_oxfw_scs1x_add(oxfw);
|
||||
}
|
||||
|
||||
/*
|
||||
* TASCAM FireOne has physical control and requires a pair of additional
|
||||
* MIDI ports.
|
||||
*/
|
||||
if (oxfw->entry->vendor_id == VENDOR_TASCAM) {
|
||||
oxfw->midi_input_ports++;
|
||||
oxfw->midi_output_ports++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Seek from Root Directory of Config ROM. */
|
||||
vendor = model = 0;
|
||||
fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
|
||||
|
@ -156,24 +202,17 @@ static void detect_quirks(struct snd_oxfw *oxfw)
|
|||
if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
|
||||
oxfw->wrong_dbs = true;
|
||||
|
||||
/*
|
||||
* TASCAM FireOne has physical control and requires a pair of additional
|
||||
* MIDI ports.
|
||||
*/
|
||||
if (vendor == VENDOR_TASCAM) {
|
||||
oxfw->midi_input_ports++;
|
||||
oxfw->midi_output_ports++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxfw_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *id)
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_oxfw *oxfw;
|
||||
int err;
|
||||
|
||||
if ((id->vendor_id == VENDOR_LOUD) && !detect_loud_models(unit))
|
||||
if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
|
||||
return -ENODEV;
|
||||
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
|
||||
|
@ -186,7 +225,7 @@ static int oxfw_probe(struct fw_unit *unit,
|
|||
oxfw->card = card;
|
||||
mutex_init(&oxfw->mutex);
|
||||
oxfw->unit = fw_unit_get(unit);
|
||||
oxfw->device_info = (const struct device_info *)id->driver_data;
|
||||
oxfw->entry = entry;
|
||||
spin_lock_init(&oxfw->lock);
|
||||
init_waitqueue_head(&oxfw->hwdep_wait);
|
||||
|
||||
|
@ -194,22 +233,18 @@ static int oxfw_probe(struct fw_unit *unit,
|
|||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
detect_quirks(oxfw);
|
||||
|
||||
err = name_card(oxfw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = detect_quirks(oxfw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_oxfw_create_pcm(oxfw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (oxfw->device_info) {
|
||||
err = snd_oxfw_create_mixer(oxfw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
snd_oxfw_proc_init(oxfw);
|
||||
|
||||
err = snd_oxfw_create_midi(oxfw);
|
||||
|
@ -257,6 +292,9 @@ static void oxfw_bus_reset(struct fw_unit *unit)
|
|||
snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
|
||||
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
|
||||
if (oxfw->entry->vendor_id == OUI_STANTON)
|
||||
snd_oxfw_scs1x_update(oxfw);
|
||||
}
|
||||
|
||||
static void oxfw_remove(struct fw_unit *unit)
|
||||
|
@ -267,22 +305,16 @@ static void oxfw_remove(struct fw_unit *unit)
|
|||
snd_card_free_when_closed(oxfw->card);
|
||||
}
|
||||
|
||||
static const struct device_info griffin_firewave = {
|
||||
static const struct compat_info griffin_firewave = {
|
||||
.driver_name = "FireWave",
|
||||
.vendor_name = "Griffin",
|
||||
.model_name = "FireWave",
|
||||
.mixer_channels = 6,
|
||||
.mute_fb_id = 0x01,
|
||||
.volume_fb_id = 0x02,
|
||||
};
|
||||
|
||||
static const struct device_info lacie_speakers = {
|
||||
static const struct compat_info lacie_speakers = {
|
||||
.driver_name = "FWSpeakers",
|
||||
.vendor_name = "LaCie",
|
||||
.model_name = "FireWire Speakers",
|
||||
.mixer_channels = 1,
|
||||
.mute_fb_id = 0x01,
|
||||
.volume_fb_id = 0x01,
|
||||
};
|
||||
|
||||
static const struct ieee1394_device_id oxfw_id_table[] = {
|
||||
|
@ -340,6 +372,20 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
|
|||
.vendor_id = VENDOR_TASCAM,
|
||||
.model_id = 0x800007,
|
||||
},
|
||||
/* Stanton, Stanton Controllers & Systems 1 Mixer (SCS.1m) */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_STANTON,
|
||||
.model_id = 0x001000,
|
||||
},
|
||||
/* Stanton, Stanton Controllers & Systems 1 Deck (SCS.1d) */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_STANTON,
|
||||
.model_id = 0x002000,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
|
||||
|
|
|
@ -31,15 +31,6 @@
|
|||
#include "../amdtp-am824.h"
|
||||
#include "../cmp.h"
|
||||
|
||||
struct device_info {
|
||||
const char *driver_name;
|
||||
const char *vendor_name;
|
||||
const char *model_name;
|
||||
unsigned int mixer_channels;
|
||||
u8 mute_fb_id;
|
||||
u8 volume_fb_id;
|
||||
};
|
||||
|
||||
/* This is an arbitrary number for convinience. */
|
||||
#define SND_OXFW_STREAM_FORMAT_ENTRIES 10
|
||||
struct snd_oxfw {
|
||||
|
@ -64,14 +55,12 @@ struct snd_oxfw {
|
|||
unsigned int midi_input_ports;
|
||||
unsigned int midi_output_ports;
|
||||
|
||||
bool mute;
|
||||
s16 volume[6];
|
||||
s16 volume_min;
|
||||
s16 volume_max;
|
||||
|
||||
int dev_lock_count;
|
||||
bool dev_lock_changed;
|
||||
wait_queue_head_t hwdep_wait;
|
||||
|
||||
const struct ieee1394_device_id *entry;
|
||||
void *spec;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -138,10 +127,12 @@ void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw);
|
|||
|
||||
int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
|
||||
|
||||
int snd_oxfw_create_mixer(struct snd_oxfw *oxfw);
|
||||
|
||||
void snd_oxfw_proc_init(struct snd_oxfw *oxfw);
|
||||
|
||||
int snd_oxfw_create_midi(struct snd_oxfw *oxfw);
|
||||
|
||||
int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw);
|
||||
|
||||
int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie);
|
||||
int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw);
|
||||
void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw);
|
||||
|
|
|
@ -1,530 +0,0 @@
|
|||
/*
|
||||
* Stanton Control System 1 MIDI driver
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/firewire.h>
|
||||
#include <linux/firewire-constants.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/wait.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/rawmidi.h>
|
||||
#include "lib.h"
|
||||
|
||||
#define OUI_STANTON 0x001260
|
||||
#define MODEL_SCS_1M 0x001000
|
||||
#define MODEL_SCS_1D 0x002000
|
||||
|
||||
#define HSS1394_ADDRESS 0xc007dedadadaULL
|
||||
#define HSS1394_MAX_PACKET_SIZE 64
|
||||
|
||||
#define HSS1394_TAG_USER_DATA 0x00
|
||||
#define HSS1394_TAG_CHANGE_ADDRESS 0xf1
|
||||
|
||||
struct scs {
|
||||
struct snd_card *card;
|
||||
struct fw_unit *unit;
|
||||
struct fw_address_handler hss_handler;
|
||||
struct fw_transaction transaction;
|
||||
bool transaction_running;
|
||||
bool output_idle;
|
||||
u8 output_status;
|
||||
u8 output_bytes;
|
||||
bool output_escaped;
|
||||
bool output_escape_high_nibble;
|
||||
u8 input_escape_count;
|
||||
struct snd_rawmidi_substream *output;
|
||||
struct snd_rawmidi_substream *input;
|
||||
struct tasklet_struct tasklet;
|
||||
wait_queue_head_t idle_wait;
|
||||
u8 *buffer;
|
||||
};
|
||||
|
||||
static const u8 sysex_escape_prefix[] = {
|
||||
0xf0, /* SysEx begin */
|
||||
0x00, 0x01, 0x60, /* Stanton DJ */
|
||||
0x48, 0x53, 0x53, /* "HSS" */
|
||||
};
|
||||
|
||||
static int scs_output_open(struct snd_rawmidi_substream *stream)
|
||||
{
|
||||
struct scs *scs = stream->rmidi->private_data;
|
||||
|
||||
scs->output_status = 0;
|
||||
scs->output_bytes = 1;
|
||||
scs->output_escaped = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scs_output_close(struct snd_rawmidi_substream *stream)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scs_output_trigger(struct snd_rawmidi_substream *stream, int up)
|
||||
{
|
||||
struct scs *scs = stream->rmidi->private_data;
|
||||
|
||||
ACCESS_ONCE(scs->output) = up ? stream : NULL;
|
||||
if (up) {
|
||||
scs->output_idle = false;
|
||||
tasklet_schedule(&scs->tasklet);
|
||||
}
|
||||
}
|
||||
|
||||
static void scs_write_callback(struct fw_card *card, int rcode,
|
||||
void *data, size_t length, void *callback_data)
|
||||
{
|
||||
struct scs *scs = callback_data;
|
||||
|
||||
if (rcode == RCODE_GENERATION) {
|
||||
/* TODO: retry this packet */
|
||||
}
|
||||
|
||||
scs->transaction_running = false;
|
||||
tasklet_schedule(&scs->tasklet);
|
||||
}
|
||||
|
||||
static bool is_valid_running_status(u8 status)
|
||||
{
|
||||
return status >= 0x80 && status <= 0xef;
|
||||
}
|
||||
|
||||
static bool is_one_byte_cmd(u8 status)
|
||||
{
|
||||
return status == 0xf6 ||
|
||||
status >= 0xf8;
|
||||
}
|
||||
|
||||
static bool is_two_bytes_cmd(u8 status)
|
||||
{
|
||||
return (status >= 0xc0 && status <= 0xdf) ||
|
||||
status == 0xf1 ||
|
||||
status == 0xf3;
|
||||
}
|
||||
|
||||
static bool is_three_bytes_cmd(u8 status)
|
||||
{
|
||||
return (status >= 0x80 && status <= 0xbf) ||
|
||||
(status >= 0xe0 && status <= 0xef) ||
|
||||
status == 0xf2;
|
||||
}
|
||||
|
||||
static bool is_invalid_cmd(u8 status)
|
||||
{
|
||||
return status == 0xf4 ||
|
||||
status == 0xf5 ||
|
||||
status == 0xf9 ||
|
||||
status == 0xfd;
|
||||
}
|
||||
|
||||
static void scs_output_tasklet(unsigned long data)
|
||||
{
|
||||
struct scs *scs = (void *)data;
|
||||
struct snd_rawmidi_substream *stream;
|
||||
unsigned int i;
|
||||
u8 byte;
|
||||
struct fw_device *dev;
|
||||
int generation;
|
||||
|
||||
if (scs->transaction_running)
|
||||
return;
|
||||
|
||||
stream = ACCESS_ONCE(scs->output);
|
||||
if (!stream) {
|
||||
scs->output_idle = true;
|
||||
wake_up(&scs->idle_wait);
|
||||
return;
|
||||
}
|
||||
|
||||
i = scs->output_bytes;
|
||||
for (;;) {
|
||||
if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
|
||||
scs->output_bytes = i;
|
||||
scs->output_idle = true;
|
||||
wake_up(&scs->idle_wait);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Convert from real MIDI to what I think the device expects (no
|
||||
* running status, one command per packet, unescaped SysExs).
|
||||
*/
|
||||
if (scs->output_escaped && byte < 0x80) {
|
||||
if (scs->output_escape_high_nibble) {
|
||||
if (i < HSS1394_MAX_PACKET_SIZE) {
|
||||
scs->buffer[i] = byte << 4;
|
||||
scs->output_escape_high_nibble = false;
|
||||
}
|
||||
} else {
|
||||
scs->buffer[i++] |= byte & 0x0f;
|
||||
scs->output_escape_high_nibble = true;
|
||||
}
|
||||
} else if (byte < 0x80) {
|
||||
if (i == 1) {
|
||||
if (!is_valid_running_status(scs->output_status))
|
||||
continue;
|
||||
scs->buffer[0] = HSS1394_TAG_USER_DATA;
|
||||
scs->buffer[i++] = scs->output_status;
|
||||
}
|
||||
scs->buffer[i++] = byte;
|
||||
if ((i == 3 && is_two_bytes_cmd(scs->output_status)) ||
|
||||
(i == 4 && is_three_bytes_cmd(scs->output_status)))
|
||||
break;
|
||||
if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) &&
|
||||
!memcmp(scs->buffer + 1, sysex_escape_prefix,
|
||||
ARRAY_SIZE(sysex_escape_prefix))) {
|
||||
scs->output_escaped = true;
|
||||
scs->output_escape_high_nibble = true;
|
||||
i = 0;
|
||||
}
|
||||
if (i >= HSS1394_MAX_PACKET_SIZE)
|
||||
i = 1;
|
||||
} else if (byte == 0xf7) {
|
||||
if (scs->output_escaped) {
|
||||
if (i >= 1 && scs->output_escape_high_nibble &&
|
||||
scs->buffer[0] != HSS1394_TAG_CHANGE_ADDRESS)
|
||||
break;
|
||||
} else {
|
||||
if (i > 1 && scs->output_status == 0xf0) {
|
||||
scs->buffer[i++] = 0xf7;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i = 1;
|
||||
scs->output_escaped = false;
|
||||
} else if (!is_invalid_cmd(byte) &&
|
||||
byte < 0xf8) {
|
||||
i = 1;
|
||||
scs->buffer[0] = HSS1394_TAG_USER_DATA;
|
||||
scs->buffer[i++] = byte;
|
||||
scs->output_status = byte;
|
||||
scs->output_escaped = false;
|
||||
if (is_one_byte_cmd(byte))
|
||||
break;
|
||||
}
|
||||
}
|
||||
scs->output_bytes = 1;
|
||||
scs->output_escaped = false;
|
||||
|
||||
scs->transaction_running = true;
|
||||
dev = fw_parent_device(scs->unit);
|
||||
generation = dev->generation;
|
||||
smp_rmb(); /* node_id vs. generation */
|
||||
fw_send_request(dev->card, &scs->transaction, TCODE_WRITE_BLOCK_REQUEST,
|
||||
dev->node_id, generation, dev->max_speed,
|
||||
HSS1394_ADDRESS, scs->buffer, i,
|
||||
scs_write_callback, scs);
|
||||
}
|
||||
|
||||
static void scs_output_drain(struct snd_rawmidi_substream *stream)
|
||||
{
|
||||
struct scs *scs = stream->rmidi->private_data;
|
||||
|
||||
wait_event(scs->idle_wait, scs->output_idle);
|
||||
}
|
||||
|
||||
static struct snd_rawmidi_ops output_ops = {
|
||||
.open = scs_output_open,
|
||||
.close = scs_output_close,
|
||||
.trigger = scs_output_trigger,
|
||||
.drain = scs_output_drain,
|
||||
};
|
||||
|
||||
static int scs_input_open(struct snd_rawmidi_substream *stream)
|
||||
{
|
||||
struct scs *scs = stream->rmidi->private_data;
|
||||
|
||||
scs->input_escape_count = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scs_input_close(struct snd_rawmidi_substream *stream)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scs_input_trigger(struct snd_rawmidi_substream *stream, int up)
|
||||
{
|
||||
struct scs *scs = stream->rmidi->private_data;
|
||||
|
||||
ACCESS_ONCE(scs->input) = up ? stream : NULL;
|
||||
}
|
||||
|
||||
static void scs_input_escaped_byte(struct snd_rawmidi_substream *stream,
|
||||
u8 byte)
|
||||
{
|
||||
u8 nibbles[2];
|
||||
|
||||
nibbles[0] = byte >> 4;
|
||||
nibbles[1] = byte & 0x0f;
|
||||
snd_rawmidi_receive(stream, nibbles, 2);
|
||||
}
|
||||
|
||||
static void scs_input_midi_byte(struct scs *scs,
|
||||
struct snd_rawmidi_substream *stream,
|
||||
u8 byte)
|
||||
{
|
||||
if (scs->input_escape_count > 0) {
|
||||
scs_input_escaped_byte(stream, byte);
|
||||
scs->input_escape_count--;
|
||||
if (scs->input_escape_count == 0)
|
||||
snd_rawmidi_receive(stream, (const u8[]) { 0xf7 }, 1);
|
||||
} else if (byte == 0xf9) {
|
||||
snd_rawmidi_receive(stream, sysex_escape_prefix,
|
||||
ARRAY_SIZE(sysex_escape_prefix));
|
||||
scs_input_escaped_byte(stream, 0x00);
|
||||
scs_input_escaped_byte(stream, 0xf9);
|
||||
scs->input_escape_count = 3;
|
||||
} else {
|
||||
snd_rawmidi_receive(stream, &byte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void scs_input_packet(struct scs *scs,
|
||||
struct snd_rawmidi_substream *stream,
|
||||
const u8 *data, unsigned int bytes)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (data[0] == HSS1394_TAG_USER_DATA) {
|
||||
for (i = 1; i < bytes; ++i)
|
||||
scs_input_midi_byte(scs, stream, data[i]);
|
||||
} else {
|
||||
snd_rawmidi_receive(stream, sysex_escape_prefix,
|
||||
ARRAY_SIZE(sysex_escape_prefix));
|
||||
for (i = 0; i < bytes; ++i)
|
||||
scs_input_escaped_byte(stream, data[i]);
|
||||
snd_rawmidi_receive(stream, (const u8[]) { 0xf7 }, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static struct snd_rawmidi_ops input_ops = {
|
||||
.open = scs_input_open,
|
||||
.close = scs_input_close,
|
||||
.trigger = scs_input_trigger,
|
||||
};
|
||||
|
||||
static int scs_create_midi(struct scs *scs)
|
||||
{
|
||||
struct snd_rawmidi *rmidi;
|
||||
int err;
|
||||
|
||||
err = snd_rawmidi_new(scs->card, "SCS.1x", 0, 1, 1, &rmidi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snprintf(rmidi->name, sizeof(rmidi->name),
|
||||
"%s MIDI", scs->card->shortname);
|
||||
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
|
||||
SNDRV_RAWMIDI_INFO_INPUT |
|
||||
SNDRV_RAWMIDI_INFO_DUPLEX;
|
||||
rmidi->private_data = scs;
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &output_ops);
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &input_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_hss(struct fw_card *card, struct fw_request *request,
|
||||
int tcode, int destination, int source, int generation,
|
||||
unsigned long long offset, void *data, size_t length,
|
||||
void *callback_data)
|
||||
{
|
||||
struct scs *scs = callback_data;
|
||||
struct snd_rawmidi_substream *stream;
|
||||
|
||||
if (offset != scs->hss_handler.offset) {
|
||||
fw_send_response(card, request, RCODE_ADDRESS_ERROR);
|
||||
return;
|
||||
}
|
||||
if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
|
||||
tcode != TCODE_WRITE_BLOCK_REQUEST) {
|
||||
fw_send_response(card, request, RCODE_TYPE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length >= 1) {
|
||||
stream = ACCESS_ONCE(scs->input);
|
||||
if (stream)
|
||||
scs_input_packet(scs, stream, data, length);
|
||||
}
|
||||
|
||||
fw_send_response(card, request, RCODE_COMPLETE);
|
||||
}
|
||||
|
||||
static int scs_init_hss_address(struct scs *scs)
|
||||
{
|
||||
__be64 data;
|
||||
int err;
|
||||
|
||||
data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
|
||||
scs->hss_handler.offset);
|
||||
err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
|
||||
HSS1394_ADDRESS, &data, 8, 0);
|
||||
if (err < 0)
|
||||
dev_err(&scs->unit->device, "HSS1394 communication failed\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void scs_card_free(struct snd_card *card)
|
||||
{
|
||||
struct scs *scs = card->private_data;
|
||||
|
||||
fw_core_remove_address_handler(&scs->hss_handler);
|
||||
kfree(scs->buffer);
|
||||
}
|
||||
|
||||
static int scs_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(unit);
|
||||
struct snd_card *card;
|
||||
struct scs *scs;
|
||||
int err;
|
||||
|
||||
err = snd_card_new(&unit->device, -16, NULL, THIS_MODULE,
|
||||
sizeof(*scs), &card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
scs = card->private_data;
|
||||
scs->card = card;
|
||||
scs->unit = unit;
|
||||
tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs);
|
||||
init_waitqueue_head(&scs->idle_wait);
|
||||
scs->output_idle = true;
|
||||
|
||||
scs->buffer = kmalloc(HSS1394_MAX_PACKET_SIZE, GFP_KERNEL);
|
||||
if (!scs->buffer) {
|
||||
err = -ENOMEM;
|
||||
goto err_card;
|
||||
}
|
||||
|
||||
scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE;
|
||||
scs->hss_handler.address_callback = handle_hss;
|
||||
scs->hss_handler.callback_data = scs;
|
||||
err = fw_core_add_address_handler(&scs->hss_handler,
|
||||
&fw_high_memory_region);
|
||||
if (err < 0)
|
||||
goto err_buffer;
|
||||
|
||||
card->private_free = scs_card_free;
|
||||
|
||||
strcpy(card->driver, "SCS.1x");
|
||||
strcpy(card->shortname, "SCS.1x");
|
||||
fw_csr_string(unit->directory, CSR_MODEL,
|
||||
card->shortname, sizeof(card->shortname));
|
||||
snprintf(card->longname, sizeof(card->longname),
|
||||
"Stanton DJ %s (GUID %08x%08x) at %s, S%d",
|
||||
card->shortname, fw_dev->config_rom[3], fw_dev->config_rom[4],
|
||||
dev_name(&unit->device), 100 << fw_dev->max_speed);
|
||||
strcpy(card->mixername, card->shortname);
|
||||
|
||||
err = scs_init_hss_address(scs);
|
||||
if (err < 0)
|
||||
goto err_card;
|
||||
|
||||
err = scs_create_midi(scs);
|
||||
if (err < 0)
|
||||
goto err_card;
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto err_card;
|
||||
|
||||
dev_set_drvdata(&unit->device, scs);
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer:
|
||||
kfree(scs->buffer);
|
||||
err_card:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void scs_update(struct fw_unit *unit)
|
||||
{
|
||||
struct scs *scs = dev_get_drvdata(&unit->device);
|
||||
int generation;
|
||||
__be64 data;
|
||||
|
||||
data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
|
||||
scs->hss_handler.offset);
|
||||
generation = fw_parent_device(unit)->generation;
|
||||
smp_rmb(); /* node_id vs. generation */
|
||||
snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
|
||||
HSS1394_ADDRESS, &data, 8,
|
||||
FW_FIXED_GENERATION | generation);
|
||||
}
|
||||
|
||||
static void scs_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct scs *scs = dev_get_drvdata(&unit->device);
|
||||
|
||||
snd_card_disconnect(scs->card);
|
||||
|
||||
ACCESS_ONCE(scs->output) = NULL;
|
||||
ACCESS_ONCE(scs->input) = NULL;
|
||||
|
||||
wait_event(scs->idle_wait, scs->output_idle);
|
||||
|
||||
tasklet_kill(&scs->tasklet);
|
||||
|
||||
snd_card_free_when_closed(scs->card);
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id scs_id_table[] = {
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_STANTON,
|
||||
.model_id = MODEL_SCS_1M,
|
||||
},
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_STANTON,
|
||||
.model_id = MODEL_SCS_1D,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, scs_id_table);
|
||||
|
||||
MODULE_DESCRIPTION("SCS.1x MIDI driver");
|
||||
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static struct fw_driver scs_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = KBUILD_MODNAME,
|
||||
.bus = &fw_bus_type,
|
||||
},
|
||||
.probe = scs_probe,
|
||||
.update = scs_update,
|
||||
.remove = scs_remove,
|
||||
.id_table = scs_id_table,
|
||||
};
|
||||
|
||||
static int __init alsa_scs1x_init(void)
|
||||
{
|
||||
return driver_register(&scs_driver.driver);
|
||||
}
|
||||
|
||||
static void __exit alsa_scs1x_exit(void)
|
||||
{
|
||||
driver_unregister(&scs_driver.driver);
|
||||
}
|
||||
|
||||
module_init(alsa_scs1x_init);
|
||||
module_exit(alsa_scs1x_exit);
|
|
@ -77,6 +77,12 @@ int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *ebus)
|
|||
ebus->spbcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
case AZX_DRSM_CAP_ID:
|
||||
/* DMA resume capability found, handler function */
|
||||
dev_dbg(bus->dev, "Found DRSM capability\n");
|
||||
ebus->drsmcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap);
|
||||
break;
|
||||
|
@ -240,7 +246,7 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
|
|||
int mask = (1 << AZX_MLCTL_CPA);
|
||||
|
||||
udelay(3);
|
||||
timeout = 50;
|
||||
timeout = 150;
|
||||
|
||||
do {
|
||||
val = readl(link->ml_addr + AZX_REG_ML_LCTL);
|
||||
|
@ -281,6 +287,27 @@ int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_bus_link_power_up_all -power up all hda link
|
||||
* @ebus: HD-audio extended bus
|
||||
*/
|
||||
int snd_hdac_ext_bus_link_power_up_all(struct hdac_ext_bus *ebus)
|
||||
{
|
||||
struct hdac_ext_link *hlink = NULL;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(hlink, &ebus->hlink_list, list) {
|
||||
snd_hdac_updatel(hlink->ml_addr,
|
||||
AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA);
|
||||
ret = check_hdac_link_power_active(hlink, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_bus_link_power_down_all -power down all hda link
|
||||
* @ebus: HD-audio extended bus
|
||||
|
|
|
@ -59,6 +59,10 @@ void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
|
|||
AZX_SPB_MAXFIFO;
|
||||
}
|
||||
|
||||
if (ebus->drsmcap)
|
||||
stream->dpibr_addr = ebus->drsmcap + AZX_DRSM_BASE +
|
||||
AZX_DRSM_INTERVAL * idx;
|
||||
|
||||
stream->decoupled = false;
|
||||
snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag);
|
||||
}
|
||||
|
@ -107,6 +111,7 @@ void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus)
|
|||
while (!list_empty(&bus->stream_list)) {
|
||||
s = list_first_entry(&bus->stream_list, struct hdac_stream, list);
|
||||
stream = stream_to_hdac_ext_stream(s);
|
||||
snd_hdac_ext_stream_decouple(ebus, stream, false);
|
||||
list_del(&s->list);
|
||||
kfree(stream);
|
||||
}
|
||||
|
@ -497,3 +502,70 @@ void snd_hdac_ext_stop_streams(struct hdac_ext_bus *ebus)
|
|||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream
|
||||
* @ebus: HD-audio ext core bus
|
||||
* @enable: flag to enable/disable DRSM
|
||||
* @index: stream index for which DRSM need to be enabled
|
||||
*/
|
||||
void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus,
|
||||
bool enable, int index)
|
||||
{
|
||||
u32 mask = 0;
|
||||
u32 register_mask = 0;
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->drsmcap) {
|
||||
dev_err(bus->dev, "Address of DRSM capability is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
mask |= (1 << index);
|
||||
|
||||
register_mask = readl(ebus->drsmcap + AZX_REG_SPB_SPBFCCTL);
|
||||
|
||||
mask |= register_mask;
|
||||
|
||||
if (enable)
|
||||
snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, 0, mask);
|
||||
else
|
||||
snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream
|
||||
* @ebus: HD-audio ext core bus
|
||||
* @stream: hdac_ext_stream
|
||||
* @value: dpib value to set
|
||||
*/
|
||||
int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus,
|
||||
struct hdac_ext_stream *stream, u32 value)
|
||||
{
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->drsmcap) {
|
||||
dev_err(bus->dev, "Address of DRSM capability is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(value, stream->dpibr_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream
|
||||
* @ebus: HD-audio ext core bus
|
||||
* @stream: hdac_ext_stream
|
||||
* @value: lpib value to set
|
||||
*/
|
||||
int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value)
|
||||
{
|
||||
snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib);
|
||||
|
|
|
@ -118,6 +118,72 @@ int snd_hdac_get_display_clk(struct hdac_bus *bus)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk);
|
||||
|
||||
/* There is a fixed mapping between audio pin node and display port
|
||||
* on current Intel platforms:
|
||||
* Pin Widget 5 - PORT B (port = 1 in i915 driver)
|
||||
* Pin Widget 6 - PORT C (port = 2 in i915 driver)
|
||||
* Pin Widget 7 - PORT D (port = 3 in i915 driver)
|
||||
*/
|
||||
static int pin2port(hda_nid_t pin_nid)
|
||||
{
|
||||
return pin_nid - 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
|
||||
* @bus: HDA core bus
|
||||
* @nid: the pin widget NID
|
||||
* @rate: the sample rate to set
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function sets N/CTS value based on the given sample rate.
|
||||
* Returns zero for success, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate)
|
||||
{
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
|
||||
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
|
||||
return -ENODEV;
|
||||
return acomp->ops->sync_audio_rate(acomp->dev, pin2port(nid), rate);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
|
||||
|
||||
/**
|
||||
* snd_hdac_acomp_get_eld - Get the audio state and ELD via component
|
||||
* @bus: HDA core bus
|
||||
* @nid: the pin widget NID
|
||||
* @audio_enabled: the pointer to store the current audio state
|
||||
* @buffer: the buffer pointer to store ELD bytes
|
||||
* @max_bytes: the max bytes to be stored on @buffer
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function queries the current state of the audio on the given
|
||||
* digital port and fetches the ELD bytes onto the given buffer.
|
||||
* It returns the number of bytes for the total ELD data, zero for
|
||||
* invalid ELD, or a negative error code.
|
||||
*
|
||||
* The return size is the total bytes required for the whole ELD bytes,
|
||||
* thus it may be over @max_bytes. If it's over @max_bytes, it implies
|
||||
* that only a part of ELD bytes have been fetched.
|
||||
*/
|
||||
int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
|
||||
bool *audio_enabled, char *buffer, int max_bytes)
|
||||
{
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
|
||||
if (!acomp || !acomp->ops || !acomp->ops->get_eld)
|
||||
return -ENODEV;
|
||||
|
||||
return acomp->ops->get_eld(acomp->dev, pin2port(nid), audio_enabled,
|
||||
buffer, max_bytes);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
|
||||
|
||||
static int hdac_component_master_bind(struct device *dev)
|
||||
{
|
||||
struct i915_audio_component *acomp = hdac_acomp;
|
||||
|
|
|
@ -39,7 +39,7 @@ static int snd_i2c_bit_readbytes(struct snd_i2c_device *device,
|
|||
static int snd_i2c_bit_probeaddr(struct snd_i2c_bus *bus,
|
||||
unsigned short addr);
|
||||
|
||||
static struct snd_i2c_ops snd_i2c_bit_ops = {
|
||||
static const struct snd_i2c_ops snd_i2c_bit_ops = {
|
||||
.sendbytes = snd_i2c_bit_sendbytes,
|
||||
.readbytes = snd_i2c_bit_readbytes,
|
||||
.probeaddr = snd_i2c_bit_probeaddr,
|
||||
|
|
|
@ -240,7 +240,7 @@ config MSND_FIFOSIZE
|
|||
|
||||
menuconfig SOUND_OSS
|
||||
tristate "OSS sound modules"
|
||||
depends on ISA_DMA_API && VIRT_TO_BUS
|
||||
depends on ISA_DMA_API && (VIRT_TO_BUS || ARCH_RPC || ARCH_NETWINDER)
|
||||
depends on !GENERIC_ISA_DMA_SUPPORT_BROKEN
|
||||
help
|
||||
OSS is the Open Sound System suite of sound card drivers. They make
|
||||
|
|
|
@ -1219,7 +1219,7 @@ static struct ac97_pcm atiixp_pcm_defs[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
|
||||
static const struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
|
||||
.type = ATI_DMA_PLAYBACK,
|
||||
.llp_offset = ATI_REG_OUT_DMA_LINKPTR,
|
||||
.dt_cur = ATI_REG_OUT_DMA_DT_CUR,
|
||||
|
@ -1228,7 +1228,7 @@ static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
|
|||
.flush_dma = atiixp_out_flush_dma,
|
||||
};
|
||||
|
||||
static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
|
||||
static const struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
|
||||
.type = ATI_DMA_CAPTURE,
|
||||
.llp_offset = ATI_REG_IN_DMA_LINKPTR,
|
||||
.dt_cur = ATI_REG_IN_DMA_DT_CUR,
|
||||
|
@ -1237,7 +1237,7 @@ static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
|
|||
.flush_dma = atiixp_in_flush_dma,
|
||||
};
|
||||
|
||||
static struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = {
|
||||
static const struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = {
|
||||
.type = ATI_DMA_SPDIF,
|
||||
.llp_offset = ATI_REG_SPDF_DMA_LINKPTR,
|
||||
.dt_cur = ATI_REG_SPDF_DMA_DT_CUR,
|
||||
|
|
|
@ -970,7 +970,7 @@ static struct snd_pcm_ops snd_atiixp_capture_ops = {
|
|||
.pointer = snd_atiixp_pcm_pointer,
|
||||
};
|
||||
|
||||
static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
|
||||
static const struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
|
||||
.type = ATI_DMA_PLAYBACK,
|
||||
.llp_offset = ATI_REG_MODEM_OUT_DMA1_LINKPTR,
|
||||
.dt_cur = ATI_REG_MODEM_OUT_DMA1_DT_CUR,
|
||||
|
@ -979,7 +979,7 @@ static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
|
|||
.flush_dma = atiixp_out_flush_dma,
|
||||
};
|
||||
|
||||
static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
|
||||
static const struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
|
||||
.type = ATI_DMA_CAPTURE,
|
||||
.llp_offset = ATI_REG_MODEM_IN_DMA_LINKPTR,
|
||||
.dt_cur = ATI_REG_MODEM_IN_DMA_DT_CUR,
|
||||
|
|
|
@ -2294,8 +2294,6 @@ snd_azf3328_free(struct snd_azf3328 *chip)
|
|||
snd_azf3328_timer_stop(chip->timer);
|
||||
snd_azf3328_gameport_free(chip);
|
||||
|
||||
if (chip->irq >= 0)
|
||||
synchronize_irq(chip->irq);
|
||||
__end_hw:
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
|
|
|
@ -402,7 +402,7 @@ static struct snd_pcm_ops snd_cs5535audio_capture_ops = {
|
|||
.pointer = snd_cs5535audio_pcm_pointer,
|
||||
};
|
||||
|
||||
static struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
|
||||
static const struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
|
||||
.type = CS5535AUDIO_DMA_PLAYBACK,
|
||||
.enable_dma = cs5535audio_playback_enable_dma,
|
||||
.disable_dma = cs5535audio_playback_disable_dma,
|
||||
|
@ -412,7 +412,7 @@ static struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
|
|||
.read_dma_pntr = cs5535audio_playback_read_dma_pntr,
|
||||
};
|
||||
|
||||
static struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
|
||||
static const struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
|
||||
.type = CS5535AUDIO_DMA_CAPTURE,
|
||||
.enable_dma = cs5535audio_capture_enable_dma,
|
||||
.disable_dma = cs5535audio_capture_disable_dma,
|
||||
|
|
|
@ -163,6 +163,7 @@ MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|||
* @cap_ctrl: capture control
|
||||
*/
|
||||
struct fm801 {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
|
||||
unsigned long port;
|
||||
|
@ -190,7 +191,6 @@ struct fm801 {
|
|||
struct snd_ac97 *ac97;
|
||||
struct snd_ac97 *ac97_sec;
|
||||
|
||||
struct pci_dev *pci;
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_rawmidi *rmidi;
|
||||
|
@ -212,6 +212,20 @@ struct fm801 {
|
|||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* IO accessors
|
||||
*/
|
||||
|
||||
static inline void fm801_iowrite16(struct fm801 *chip, unsigned short offset, u16 value)
|
||||
{
|
||||
outw(value, chip->port + offset);
|
||||
}
|
||||
|
||||
static inline u16 fm801_ioread16(struct fm801 *chip, unsigned short offset)
|
||||
{
|
||||
return inw(chip->port + offset);
|
||||
}
|
||||
|
||||
static const struct pci_device_id snd_fm801_ids[] = {
|
||||
{ 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* FM801 */
|
||||
{ 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* Gallant Odyssey Sound 4 */
|
||||
|
@ -256,11 +270,11 @@ static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg,
|
|||
unsigned short old, new;
|
||||
|
||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||
old = inw(chip->port + reg);
|
||||
old = fm801_ioread16(chip, reg);
|
||||
new = (old & ~mask) | value;
|
||||
change = old != new;
|
||||
if (change)
|
||||
outw(new, chip->port + reg);
|
||||
fm801_iowrite16(chip, reg, new);
|
||||
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||
return change;
|
||||
}
|
||||
|
@ -578,8 +592,9 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
|
|||
}
|
||||
if (chip->rmidi && (status & FM801_IRQ_MPU))
|
||||
snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
|
||||
if (status & FM801_IRQ_VOLUME)
|
||||
;/* TODO */
|
||||
if (status & FM801_IRQ_VOLUME) {
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -700,6 +715,7 @@ static struct snd_pcm_ops snd_fm801_capture_ops = {
|
|||
|
||||
static int snd_fm801_pcm(struct fm801 *chip, int device)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(chip->dev);
|
||||
struct snd_pcm *pcm;
|
||||
int err;
|
||||
|
||||
|
@ -715,7 +731,7 @@ static int snd_fm801_pcm(struct fm801 *chip, int device)
|
|||
chip->pcm = pcm;
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data(chip->pci),
|
||||
snd_dma_pci_data(pdev),
|
||||
chip->multichannel ? 128*1024 : 64*1024, 128*1024);
|
||||
|
||||
return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
|
@ -851,10 +867,11 @@ static int snd_fm801_get_single(struct snd_kcontrol *kcontrol,
|
|||
int shift = (kcontrol->private_value >> 8) & 0xff;
|
||||
int mask = (kcontrol->private_value >> 16) & 0xff;
|
||||
int invert = (kcontrol->private_value >> 24) & 0xff;
|
||||
long *value = ucontrol->value.integer.value;
|
||||
|
||||
ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask;
|
||||
value[0] = (fm801_ioread16(chip, reg) >> shift) & mask;
|
||||
if (invert)
|
||||
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
|
||||
value[0] = mask - value[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -907,14 +924,15 @@ static int snd_fm801_get_double(struct snd_kcontrol *kcontrol,
|
|||
int shift_right = (kcontrol->private_value >> 12) & 0x0f;
|
||||
int mask = (kcontrol->private_value >> 16) & 0xff;
|
||||
int invert = (kcontrol->private_value >> 24) & 0xff;
|
||||
long *value = ucontrol->value.integer.value;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask;
|
||||
ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask;
|
||||
value[0] = (fm801_ioread16(chip, reg) >> shift_left) & mask;
|
||||
value[1] = (fm801_ioread16(chip, reg) >> shift_right) & mask;
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
if (invert) {
|
||||
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
|
||||
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
|
||||
value[0] = mask - value[0];
|
||||
value[1] = mask - value[1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1080,26 +1098,20 @@ static int wait_for_codec(struct fm801 *chip, unsigned int codec_id,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
static int snd_fm801_chip_init(struct fm801 *chip, int resume)
|
||||
static int reset_codec(struct fm801 *chip)
|
||||
{
|
||||
unsigned short cmdw;
|
||||
|
||||
if (chip->tea575x_tuner & TUNER_ONLY)
|
||||
goto __ac97_ok;
|
||||
|
||||
/* codec cold reset + AC'97 warm reset */
|
||||
fm801_writew(chip, CODEC_CTRL, (1 << 5) | (1 << 6));
|
||||
fm801_readw(chip, CODEC_CTRL); /* flush posting data */
|
||||
udelay(100);
|
||||
fm801_writew(chip, CODEC_CTRL, 0);
|
||||
|
||||
if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
|
||||
if (!resume) {
|
||||
dev_info(chip->card->dev,
|
||||
"Primary AC'97 codec not found, assume SF64-PCR (tuner-only)\n");
|
||||
chip->tea575x_tuner = 3 | TUNER_ONLY;
|
||||
goto __ac97_ok;
|
||||
}
|
||||
return wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750));
|
||||
}
|
||||
|
||||
static void snd_fm801_chip_multichannel_init(struct fm801 *chip)
|
||||
{
|
||||
unsigned short cmdw;
|
||||
|
||||
if (chip->multichannel) {
|
||||
if (chip->secondary_addr) {
|
||||
|
@ -1126,8 +1138,11 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
|
|||
/* cause timeout problems */
|
||||
wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750));
|
||||
}
|
||||
}
|
||||
|
||||
__ac97_ok:
|
||||
static void snd_fm801_chip_init(struct fm801 *chip)
|
||||
{
|
||||
unsigned short cmdw;
|
||||
|
||||
/* init volume */
|
||||
fm801_writew(chip, PCM_VOL, 0x0808);
|
||||
|
@ -1148,11 +1163,8 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
|
|||
/* interrupt clear */
|
||||
fm801_writew(chip, IRQ_STATUS,
|
||||
FM801_IRQ_PLAYBACK | FM801_IRQ_CAPTURE | FM801_IRQ_MPU);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_fm801_free(struct fm801 *chip)
|
||||
{
|
||||
unsigned short cmdw;
|
||||
|
@ -1165,6 +1177,8 @@ static int snd_fm801_free(struct fm801 *chip)
|
|||
cmdw |= 0x00c3;
|
||||
fm801_writew(chip, IRQ_MASK, cmdw);
|
||||
|
||||
devm_free_irq(chip->dev, chip->irq, chip);
|
||||
|
||||
__end_hw:
|
||||
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||
if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
|
||||
|
@ -1201,13 +1215,29 @@ static int snd_fm801_create(struct snd_card *card,
|
|||
return -ENOMEM;
|
||||
spin_lock_init(&chip->reg_lock);
|
||||
chip->card = card;
|
||||
chip->pci = pci;
|
||||
chip->dev = &pci->dev;
|
||||
chip->irq = -1;
|
||||
chip->tea575x_tuner = tea575x_tuner;
|
||||
if ((err = pci_request_regions(pci, "FM801")) < 0)
|
||||
return err;
|
||||
chip->port = pci_resource_start(pci, 0);
|
||||
if ((tea575x_tuner & TUNER_ONLY) == 0) {
|
||||
|
||||
if (pci->revision >= 0xb1) /* FM801-AU */
|
||||
chip->multichannel = 1;
|
||||
|
||||
if (!(chip->tea575x_tuner & TUNER_ONLY)) {
|
||||
if (reset_codec(chip) < 0) {
|
||||
dev_info(chip->card->dev,
|
||||
"Primary AC'97 codec not found, assume SF64-PCR (tuner-only)\n");
|
||||
chip->tea575x_tuner = 3 | TUNER_ONLY;
|
||||
} else {
|
||||
snd_fm801_chip_multichannel_init(chip);
|
||||
}
|
||||
}
|
||||
|
||||
snd_fm801_chip_init(chip);
|
||||
|
||||
if ((chip->tea575x_tuner & TUNER_ONLY) == 0) {
|
||||
if (devm_request_irq(&pci->dev, pci->irq, snd_fm801_interrupt,
|
||||
IRQF_SHARED, KBUILD_MODNAME, chip)) {
|
||||
dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
|
||||
|
@ -1218,13 +1248,6 @@ static int snd_fm801_create(struct snd_card *card,
|
|||
pci_set_master(pci);
|
||||
}
|
||||
|
||||
if (pci->revision >= 0xb1) /* FM801-AU */
|
||||
chip->multichannel = 1;
|
||||
|
||||
snd_fm801_chip_init(chip, 0);
|
||||
/* init might set tuner access method */
|
||||
tea575x_tuner = chip->tea575x_tuner;
|
||||
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
|
||||
snd_fm801_free(chip);
|
||||
return err;
|
||||
|
@ -1241,14 +1264,16 @@ static int snd_fm801_create(struct snd_card *card,
|
|||
chip->tea.private_data = chip;
|
||||
chip->tea.ops = &snd_fm801_tea_ops;
|
||||
sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
|
||||
if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
|
||||
(tea575x_tuner & TUNER_TYPE_MASK) < 4) {
|
||||
if ((chip->tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
|
||||
(chip->tea575x_tuner & TUNER_TYPE_MASK) < 4) {
|
||||
if (snd_tea575x_init(&chip->tea, THIS_MODULE)) {
|
||||
dev_err(card->dev, "TEA575x radio not found\n");
|
||||
snd_fm801_free(chip);
|
||||
return -ENODEV;
|
||||
}
|
||||
} else if ((tea575x_tuner & TUNER_TYPE_MASK) == 0) {
|
||||
} else if ((chip->tea575x_tuner & TUNER_TYPE_MASK) == 0) {
|
||||
unsigned int tuner_only = chip->tea575x_tuner & TUNER_ONLY;
|
||||
|
||||
/* autodetect tuner connection */
|
||||
for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) {
|
||||
chip->tea575x_tuner = tea575x_tuner;
|
||||
|
@ -1263,6 +1288,8 @@ static int snd_fm801_create(struct snd_card *card,
|
|||
dev_err(card->dev, "TEA575x radio not found\n");
|
||||
chip->tea575x_tuner = TUNER_DISABLED;
|
||||
}
|
||||
|
||||
chip->tea575x_tuner |= tuner_only;
|
||||
}
|
||||
if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
|
||||
strlcpy(chip->tea.card, get_tea575x_gpio(chip)->name,
|
||||
|
@ -1366,12 +1393,18 @@ static int snd_fm801_suspend(struct device *dev)
|
|||
int i;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
snd_pcm_suspend_all(chip->pcm);
|
||||
snd_ac97_suspend(chip->ac97);
|
||||
snd_ac97_suspend(chip->ac97_sec);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
|
||||
chip->saved_regs[i] = inw(chip->port + saved_regs[i]);
|
||||
/* FIXME: tea575x suspend */
|
||||
chip->saved_regs[i] = fm801_ioread16(chip, saved_regs[i]);
|
||||
|
||||
if (chip->tea575x_tuner & TUNER_ONLY) {
|
||||
/* FIXME: tea575x suspend */
|
||||
} else {
|
||||
snd_pcm_suspend_all(chip->pcm);
|
||||
snd_ac97_suspend(chip->ac97);
|
||||
snd_ac97_suspend(chip->ac97_sec);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1381,11 +1414,23 @@ static int snd_fm801_resume(struct device *dev)
|
|||
struct fm801 *chip = card->private_data;
|
||||
int i;
|
||||
|
||||
snd_fm801_chip_init(chip, 1);
|
||||
snd_ac97_resume(chip->ac97);
|
||||
snd_ac97_resume(chip->ac97_sec);
|
||||
if (chip->tea575x_tuner & TUNER_ONLY) {
|
||||
snd_fm801_chip_init(chip);
|
||||
} else {
|
||||
reset_codec(chip);
|
||||
snd_fm801_chip_multichannel_init(chip);
|
||||
snd_fm801_chip_init(chip);
|
||||
snd_ac97_resume(chip->ac97);
|
||||
snd_ac97_resume(chip->ac97_sec);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
|
||||
outw(chip->saved_regs[i], chip->port + saved_regs[i]);
|
||||
fm801_iowrite16(chip, saved_regs[i], chip->saved_regs[i]);
|
||||
|
||||
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||
if (!(chip->tea575x_tuner & TUNER_DISABLED))
|
||||
snd_tea575x_set_freq(&chip->tea);
|
||||
#endif
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
|
|
|
@ -956,7 +956,7 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
|
|||
status = azx_readb(chip, RIRBSTS);
|
||||
if (status & RIRB_INT_MASK) {
|
||||
if (status & RIRB_INT_RESPONSE) {
|
||||
if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY)
|
||||
if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
|
||||
udelay(80);
|
||||
snd_hdac_bus_update_rirb(bus);
|
||||
}
|
||||
|
@ -1050,16 +1050,10 @@ int azx_bus_init(struct azx *chip, const char *model,
|
|||
if (chip->get_position[0] != azx_get_pos_lpib ||
|
||||
chip->get_position[1] != azx_get_pos_lpib)
|
||||
bus->core.use_posbuf = true;
|
||||
if (chip->bdl_pos_adj)
|
||||
bus->core.bdl_pos_adj = chip->bdl_pos_adj[chip->dev_index];
|
||||
bus->core.bdl_pos_adj = chip->bdl_pos_adj;
|
||||
if (chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)
|
||||
bus->core.corbrp_self_clear = true;
|
||||
|
||||
if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) {
|
||||
dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
|
||||
bus->needs_damn_long_delay = 1;
|
||||
}
|
||||
|
||||
if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY)
|
||||
bus->core.align_bdle_4k = true;
|
||||
|
||||
|
|
|
@ -32,21 +32,25 @@
|
|||
#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */
|
||||
#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */
|
||||
#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */
|
||||
#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */
|
||||
#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */
|
||||
/* 13 unused */
|
||||
/* 14 unused */
|
||||
#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
|
||||
#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
|
||||
#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */
|
||||
/* 17 unused */
|
||||
#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */
|
||||
#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */
|
||||
#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */
|
||||
#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */
|
||||
/* 22 unused */
|
||||
#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
|
||||
#define AZX_DCAPS_REVERSE_ASSIGN (1 << 24) /* Assign devices in reverse order */
|
||||
/* 24 unused */
|
||||
#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */
|
||||
#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
|
||||
#ifdef CONFIG_SND_HDA_I915
|
||||
#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */
|
||||
#else
|
||||
#define AZX_DCAPS_I915_POWERWELL 0 /* NOP */
|
||||
#endif
|
||||
#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */
|
||||
#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */
|
||||
#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */
|
||||
|
@ -143,7 +147,7 @@ struct azx {
|
|||
#endif
|
||||
|
||||
/* flags */
|
||||
const int *bdl_pos_adj;
|
||||
int bdl_pos_adj;
|
||||
int poll_count;
|
||||
unsigned int running:1;
|
||||
unsigned int single_cmd:1;
|
||||
|
|
|
@ -253,6 +253,7 @@ int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e,
|
|||
int mnl;
|
||||
int i;
|
||||
|
||||
memset(e, 0, sizeof(*e));
|
||||
e->eld_ver = GRAB_BITS(buf, 0, 3, 5);
|
||||
if (e->eld_ver != ELD_VER_CEA_861D &&
|
||||
e->eld_ver != ELD_VER_PARTIAL) {
|
||||
|
|
|
@ -278,22 +278,6 @@ static struct nid_path *get_nid_path(struct hda_codec *codec,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_get_nid_path - get the path between the given NIDs
|
||||
* @codec: the HDA codec
|
||||
* @from_nid: the NID where the path start from
|
||||
* @to_nid: the NID where the path ends at
|
||||
*
|
||||
* Return the found nid_path object or NULL for error.
|
||||
* Passing 0 to either @from_nid or @to_nid behaves as a wildcard.
|
||||
*/
|
||||
struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
|
||||
hda_nid_t from_nid, hda_nid_t to_nid)
|
||||
{
|
||||
return get_nid_path(codec, from_nid, to_nid, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_get_nid_path);
|
||||
|
||||
/**
|
||||
* snd_hda_get_path_idx - get the index number corresponding to the path
|
||||
* instance
|
||||
|
@ -451,7 +435,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* snd_hda_parse_nid_path - parse the widget path from the given nid to
|
||||
* the target nid
|
||||
* @codec: the HDA codec
|
||||
|
@ -470,7 +454,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
|
|||
* with the negative of given value are excluded, only other paths are chosen.
|
||||
* when @anchor_nid is zero, no special handling about path selection.
|
||||
*/
|
||||
bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
|
||||
static bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
|
||||
hda_nid_t to_nid, int anchor_nid,
|
||||
struct nid_path *path)
|
||||
{
|
||||
|
@ -481,7 +465,6 @@ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_parse_nid_path);
|
||||
|
||||
/**
|
||||
* snd_hda_add_new_path - parse the path between the given NIDs and
|
||||
|
@ -771,9 +754,6 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
|
|||
unsigned int caps;
|
||||
unsigned int mask, val;
|
||||
|
||||
if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
|
||||
return;
|
||||
|
||||
caps = query_amp_caps(codec, nid, dir);
|
||||
val = get_amp_val_to_activate(codec, nid, dir, caps, enable);
|
||||
mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps);
|
||||
|
@ -784,12 +764,22 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
|
|||
update_amp(codec, nid, dir, idx, mask, val);
|
||||
}
|
||||
|
||||
static void check_and_activate_amp(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dir, int idx, int idx_to_check,
|
||||
bool enable)
|
||||
{
|
||||
/* check whether the given amp is still used by others */
|
||||
if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
|
||||
return;
|
||||
activate_amp(codec, nid, dir, idx, idx_to_check, enable);
|
||||
}
|
||||
|
||||
static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
|
||||
int i, bool enable)
|
||||
{
|
||||
hda_nid_t nid = path->path[i];
|
||||
init_amp(codec, nid, HDA_OUTPUT, 0);
|
||||
activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
|
||||
check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
|
||||
}
|
||||
|
||||
static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
|
||||
|
@ -817,9 +807,16 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
|
|||
* when aa-mixer is available, we need to enable the path as well
|
||||
*/
|
||||
for (n = 0; n < nums; n++) {
|
||||
if (n != idx && (!add_aamix || conn[n] != spec->mixer_merge_nid))
|
||||
continue;
|
||||
activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
|
||||
if (n != idx) {
|
||||
if (conn[n] != spec->mixer_merge_nid)
|
||||
continue;
|
||||
/* when aamix is disabled, force to off */
|
||||
if (!add_aamix) {
|
||||
activate_amp(codec, nid, HDA_INPUT, n, n, false);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
check_and_activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1580,6 +1577,12 @@ static bool map_singles(struct hda_codec *codec, int outs,
|
|||
return found;
|
||||
}
|
||||
|
||||
static inline bool has_aamix_out_paths(struct hda_gen_spec *spec)
|
||||
{
|
||||
return spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
|
||||
spec->aamix_out_paths[2];
|
||||
}
|
||||
|
||||
/* create a new path including aamix if available, and return its index */
|
||||
static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
|
||||
{
|
||||
|
@ -2422,25 +2425,51 @@ static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
|
|||
}
|
||||
}
|
||||
|
||||
/* re-initialize the output paths; only called from loopback_mixing_put() */
|
||||
static void update_output_paths(struct hda_codec *codec, int num_outs,
|
||||
const int *paths)
|
||||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
struct nid_path *path;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_outs; i++) {
|
||||
path = snd_hda_get_path_from_idx(codec, paths[i]);
|
||||
if (path)
|
||||
snd_hda_activate_path(codec, path, path->active,
|
||||
spec->aamix_mode);
|
||||
}
|
||||
}
|
||||
|
||||
static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
const struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
unsigned int val = ucontrol->value.enumerated.item[0];
|
||||
|
||||
if (val == spec->aamix_mode)
|
||||
return 0;
|
||||
spec->aamix_mode = val;
|
||||
update_aamix_paths(codec, val, spec->out_paths[0],
|
||||
spec->aamix_out_paths[0],
|
||||
spec->autocfg.line_out_type);
|
||||
update_aamix_paths(codec, val, spec->hp_paths[0],
|
||||
spec->aamix_out_paths[1],
|
||||
AUTO_PIN_HP_OUT);
|
||||
update_aamix_paths(codec, val, spec->speaker_paths[0],
|
||||
spec->aamix_out_paths[2],
|
||||
AUTO_PIN_SPEAKER_OUT);
|
||||
if (has_aamix_out_paths(spec)) {
|
||||
update_aamix_paths(codec, val, spec->out_paths[0],
|
||||
spec->aamix_out_paths[0],
|
||||
cfg->line_out_type);
|
||||
update_aamix_paths(codec, val, spec->hp_paths[0],
|
||||
spec->aamix_out_paths[1],
|
||||
AUTO_PIN_HP_OUT);
|
||||
update_aamix_paths(codec, val, spec->speaker_paths[0],
|
||||
spec->aamix_out_paths[2],
|
||||
AUTO_PIN_SPEAKER_OUT);
|
||||
} else {
|
||||
update_output_paths(codec, cfg->line_outs, spec->out_paths);
|
||||
if (cfg->line_out_type != AUTO_PIN_HP_OUT)
|
||||
update_output_paths(codec, cfg->hp_outs, spec->hp_paths);
|
||||
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
|
||||
update_output_paths(codec, cfg->speaker_outs,
|
||||
spec->speaker_paths);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -2458,12 +2487,13 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec)
|
|||
|
||||
if (!spec->mixer_nid)
|
||||
return 0;
|
||||
if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
|
||||
spec->aamix_out_paths[2]))
|
||||
return 0;
|
||||
if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
|
||||
return -ENOMEM;
|
||||
spec->have_aamix_ctl = 1;
|
||||
/* if no explicit aamix path is present (e.g. for Realtek codecs),
|
||||
* enable aamix as default -- just for compatibility
|
||||
*/
|
||||
spec->aamix_mode = !has_aamix_out_paths(spec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5664,6 +5694,8 @@ static void init_aamix_paths(struct hda_codec *codec)
|
|||
|
||||
if (!spec->have_aamix_ctl)
|
||||
return;
|
||||
if (!has_aamix_out_paths(spec))
|
||||
return;
|
||||
update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0],
|
||||
spec->aamix_out_paths[0],
|
||||
spec->autocfg.line_out_type);
|
||||
|
|
|
@ -306,13 +306,8 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
|
|||
int snd_hda_gen_init(struct hda_codec *codec);
|
||||
void snd_hda_gen_free(struct hda_codec *codec);
|
||||
|
||||
struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
|
||||
hda_nid_t from_nid, hda_nid_t to_nid);
|
||||
int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path);
|
||||
struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx);
|
||||
bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
|
||||
hda_nid_t to_nid, int anchor_nid,
|
||||
struct nid_path *path);
|
||||
struct nid_path *
|
||||
snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
|
||||
hda_nid_t to_nid, int anchor_nid);
|
||||
|
|
|
@ -284,13 +284,19 @@ enum {
|
|||
(AZX_DCAPS_OLD_SSYNC | AZX_DCAPS_NO_ALIGN_BUFSIZE)
|
||||
|
||||
/* quirks for Intel PCH */
|
||||
#define AZX_DCAPS_INTEL_PCH_NOPM \
|
||||
#define AZX_DCAPS_INTEL_PCH_BASE \
|
||||
(AZX_DCAPS_NO_ALIGN_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\
|
||||
AZX_DCAPS_REVERSE_ASSIGN | AZX_DCAPS_SNOOP_TYPE(SCH))
|
||||
AZX_DCAPS_SNOOP_TYPE(SCH))
|
||||
|
||||
/* PCH up to IVB; no runtime PM */
|
||||
#define AZX_DCAPS_INTEL_PCH_NOPM \
|
||||
(AZX_DCAPS_INTEL_PCH_BASE)
|
||||
|
||||
/* PCH for HSW/BDW; with runtime PM */
|
||||
#define AZX_DCAPS_INTEL_PCH \
|
||||
(AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME)
|
||||
(AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME)
|
||||
|
||||
/* HSW HDMI */
|
||||
#define AZX_DCAPS_INTEL_HASWELL \
|
||||
(/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\
|
||||
AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\
|
||||
|
@ -332,7 +338,7 @@ enum {
|
|||
|
||||
/* quirks for Nvidia */
|
||||
#define AZX_DCAPS_PRESET_NVIDIA \
|
||||
(AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI | /*AZX_DCAPS_ALIGN_BUFSIZE |*/ \
|
||||
(AZX_DCAPS_NO_MSI | /*AZX_DCAPS_ALIGN_BUFSIZE |*/ \
|
||||
AZX_DCAPS_NO_64BIT | AZX_DCAPS_CORBRP_SELF_CLEAR |\
|
||||
AZX_DCAPS_SNOOP_TYPE(NVIDIA))
|
||||
|
||||
|
@ -649,7 +655,7 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
|
|||
if (wallclk < (azx_dev->core.period_wallclk * 5) / 4 &&
|
||||
pos % azx_dev->core.period_bytes > azx_dev->core.period_bytes / 2)
|
||||
/* NG - it's below the first next period boundary */
|
||||
return chip->bdl_pos_adj[chip->dev_index] ? 0 : -1;
|
||||
return chip->bdl_pos_adj ? 0 : -1;
|
||||
azx_dev->core.start_wallclk += wallclk;
|
||||
return 1; /* OK, it's fine */
|
||||
}
|
||||
|
@ -719,7 +725,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect)
|
|||
|
||||
if (request_irq(chip->pci->irq, azx_interrupt,
|
||||
chip->msi ? 0 : IRQF_SHARED,
|
||||
KBUILD_MODNAME, chip)) {
|
||||
chip->card->irq_descr, chip)) {
|
||||
dev_err(chip->card->dev,
|
||||
"unable to grab IRQ %d, disabling device\n",
|
||||
chip->pci->irq);
|
||||
|
@ -1376,7 +1382,7 @@ static int check_position_fix(struct azx *chip, int fix)
|
|||
}
|
||||
|
||||
/* Check VIA/ATI HD Audio Controller exist */
|
||||
if (chip->driver_caps & AZX_DCAPS_POSFIX_VIA) {
|
||||
if (chip->driver_type == AZX_DRIVER_VIA) {
|
||||
dev_dbg(chip->card->dev, "Using VIACOMBO position fix\n");
|
||||
return POS_FIX_VIACOMBO;
|
||||
}
|
||||
|
@ -1539,6 +1545,26 @@ static void azx_probe_work(struct work_struct *work)
|
|||
azx_probe_continue(&hda->chip);
|
||||
}
|
||||
|
||||
static int default_bdl_pos_adj(struct azx *chip)
|
||||
{
|
||||
/* some exceptions: Atoms seem problematic with value 1 */
|
||||
if (chip->pci->vendor == PCI_VENDOR_ID_INTEL) {
|
||||
switch (chip->pci->device) {
|
||||
case 0x0f04: /* Baytrail */
|
||||
case 0x2284: /* Braswell */
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
|
||||
switch (chip->driver_type) {
|
||||
case AZX_DRIVER_ICH:
|
||||
case AZX_DRIVER_PCH:
|
||||
return 1;
|
||||
default:
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* constructor
|
||||
*/
|
||||
|
@ -1592,18 +1618,10 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
chip->single_cmd = single_cmd;
|
||||
azx_check_snoop_available(chip);
|
||||
|
||||
if (bdl_pos_adj[dev] < 0) {
|
||||
switch (chip->driver_type) {
|
||||
case AZX_DRIVER_ICH:
|
||||
case AZX_DRIVER_PCH:
|
||||
bdl_pos_adj[dev] = 1;
|
||||
break;
|
||||
default:
|
||||
bdl_pos_adj[dev] = 32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chip->bdl_pos_adj = bdl_pos_adj;
|
||||
if (bdl_pos_adj[dev] < 0)
|
||||
chip->bdl_pos_adj = default_bdl_pos_adj(chip);
|
||||
else
|
||||
chip->bdl_pos_adj = bdl_pos_adj[dev];
|
||||
|
||||
err = azx_bus_init(chip, model[dev], &pci_hda_io_ops);
|
||||
if (err < 0) {
|
||||
|
@ -1612,6 +1630,11 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
return err;
|
||||
}
|
||||
|
||||
if (chip->driver_type == AZX_DRIVER_NVIDIA) {
|
||||
dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
|
||||
chip->bus.needs_damn_long_delay = 1;
|
||||
}
|
||||
|
||||
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "Error creating device [card]!\n");
|
||||
|
@ -2005,8 +2028,8 @@ static int azx_probe(struct pci_dev *pci,
|
|||
#endif /* CONFIG_SND_HDA_PATCH_LOADER */
|
||||
|
||||
#ifndef CONFIG_SND_HDA_I915
|
||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
|
||||
dev_err(card->dev, "Haswell must build in CONFIG_SND_HDA_I915\n");
|
||||
if (CONTROLLER_IN_GPU(pci))
|
||||
dev_err(card->dev, "Haswell/Broadwell HDMI/DP must build in CONFIG_SND_HDA_I915\n");
|
||||
#endif
|
||||
|
||||
if (schedule_probe)
|
||||
|
@ -2203,10 +2226,10 @@ static const struct pci_device_id azx_ids[] = {
|
|||
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
|
||||
/* Poulsbo */
|
||||
{ PCI_DEVICE(0x8086, 0x811b),
|
||||
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
|
||||
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE },
|
||||
/* Oaktrail */
|
||||
{ PCI_DEVICE(0x8086, 0x080a),
|
||||
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
|
||||
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE },
|
||||
/* BayTrail */
|
||||
{ PCI_DEVICE(0x8086, 0x0f04),
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BAYTRAIL },
|
||||
|
@ -2318,8 +2341,7 @@ static const struct pci_device_id azx_ids[] = {
|
|||
{ PCI_DEVICE(0x1002, 0xaae8),
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
|
||||
/* VIA VT8251/VT8237A */
|
||||
{ PCI_DEVICE(0x1106, 0x3288),
|
||||
.driver_data = AZX_DRIVER_VIA | AZX_DCAPS_POSFIX_VIA },
|
||||
{ PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA },
|
||||
/* VIA GFX VT7122/VX900 */
|
||||
{ PCI_DEVICE(0x1106, 0x9170), .driver_data = AZX_DRIVER_GENERIC },
|
||||
/* VIA GFX VT6122/VX11 */
|
||||
|
@ -2353,14 +2375,12 @@ static const struct pci_device_id azx_ids[] = {
|
|||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
.class_mask = 0xffffff,
|
||||
.driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
|
||||
AZX_DCAPS_NO_64BIT |
|
||||
AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
|
||||
AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
|
||||
#else
|
||||
/* this entry seems still valid -- i.e. without emu20kx chip */
|
||||
{ PCI_DEVICE(0x1102, 0x0009),
|
||||
.driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
|
||||
AZX_DCAPS_NO_64BIT |
|
||||
AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
|
||||
AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
|
||||
#endif
|
||||
/* CM8888 */
|
||||
{ PCI_DEVICE(0x13f6, 0x5011),
|
||||
|
|
|
@ -464,6 +464,8 @@ static int hda_tegra_create(struct snd_card *card,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
chip->bus.needs_damn_long_delay = 1;
|
||||
|
||||
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "Error creating device\n");
|
||||
|
@ -481,8 +483,7 @@ MODULE_DEVICE_TABLE(of, hda_tegra_match);
|
|||
|
||||
static int hda_tegra_probe(struct platform_device *pdev)
|
||||
{
|
||||
const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY |
|
||||
AZX_DCAPS_CORBRP_SELF_CLEAR;
|
||||
const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR;
|
||||
struct snd_card *card;
|
||||
struct azx *chip;
|
||||
struct hda_tegra *hda;
|
||||
|
|
|
@ -901,6 +901,9 @@ static int patch_conexant_auto(struct hda_codec *codec)
|
|||
snd_hda_pick_fixup(codec, cxt5051_fixup_models,
|
||||
cxt5051_fixups, cxt_fixups);
|
||||
break;
|
||||
case 0x14f150f2:
|
||||
codec->power_save_node = 1;
|
||||
/* Fall through */
|
||||
default:
|
||||
codec->pin_amp_workaround = 1;
|
||||
snd_hda_pick_fixup(codec, cxt5066_fixup_models,
|
||||
|
|
|
@ -51,8 +51,10 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
|
|||
#define is_broadwell(codec) ((codec)->core.vendor_id == 0x80862808)
|
||||
#define is_skylake(codec) ((codec)->core.vendor_id == 0x80862809)
|
||||
#define is_broxton(codec) ((codec)->core.vendor_id == 0x8086280a)
|
||||
#define is_kabylake(codec) ((codec)->core.vendor_id == 0x8086280b)
|
||||
#define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \
|
||||
|| is_skylake(codec) || is_broxton(codec))
|
||||
|| is_skylake(codec) || is_broxton(codec) \
|
||||
|| is_kabylake(codec))
|
||||
|
||||
#define is_valleyview(codec) ((codec)->core.vendor_id == 0x80862882)
|
||||
#define is_cherryview(codec) ((codec)->core.vendor_id == 0x80862883)
|
||||
|
@ -83,6 +85,7 @@ struct hdmi_spec_per_pin {
|
|||
struct mutex lock;
|
||||
struct delayed_work work;
|
||||
struct snd_kcontrol *eld_ctl;
|
||||
struct snd_jack *acomp_jack; /* jack via audio component */
|
||||
int repoll_count;
|
||||
bool setup; /* the stream has been set up by prepare callback */
|
||||
int channels; /* current number of channels */
|
||||
|
@ -150,8 +153,15 @@ struct hdmi_spec {
|
|||
|
||||
/* i915/powerwell (Haswell+/Valleyview+) specific */
|
||||
struct i915_audio_component_audio_ops i915_audio_ops;
|
||||
bool i915_bound; /* was i915 bound in this driver? */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_HDA_I915
|
||||
#define codec_has_acomp(codec) \
|
||||
((codec)->bus->core.audio_component != NULL)
|
||||
#else
|
||||
#define codec_has_acomp(codec) false
|
||||
#endif
|
||||
|
||||
struct hdmi_audio_infoframe {
|
||||
u8 type; /* 0x84 */
|
||||
|
@ -1530,7 +1540,59 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
||||
/* update per_pin ELD from the given new ELD;
|
||||
* setup info frame and notification accordingly
|
||||
*/
|
||||
static void update_eld(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
struct hdmi_eld *pin_eld = &per_pin->sink_eld;
|
||||
bool old_eld_valid = pin_eld->eld_valid;
|
||||
bool eld_changed;
|
||||
|
||||
if (eld->eld_valid)
|
||||
snd_hdmi_show_eld(codec, &eld->info);
|
||||
|
||||
eld_changed = (pin_eld->eld_valid != eld->eld_valid);
|
||||
if (eld->eld_valid && pin_eld->eld_valid)
|
||||
if (pin_eld->eld_size != eld->eld_size ||
|
||||
memcmp(pin_eld->eld_buffer, eld->eld_buffer,
|
||||
eld->eld_size) != 0)
|
||||
eld_changed = true;
|
||||
|
||||
pin_eld->eld_valid = eld->eld_valid;
|
||||
pin_eld->eld_size = eld->eld_size;
|
||||
if (eld->eld_valid)
|
||||
memcpy(pin_eld->eld_buffer, eld->eld_buffer, eld->eld_size);
|
||||
pin_eld->info = eld->info;
|
||||
|
||||
/*
|
||||
* Re-setup pin and infoframe. This is needed e.g. when
|
||||
* - sink is first plugged-in
|
||||
* - transcoder can change during stream playback on Haswell
|
||||
* and this can make HW reset converter selection on a pin.
|
||||
*/
|
||||
if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
|
||||
if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
|
||||
intel_verify_pin_cvt_connect(codec, per_pin);
|
||||
intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
|
||||
per_pin->mux_idx);
|
||||
}
|
||||
|
||||
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
|
||||
}
|
||||
|
||||
if (eld_changed)
|
||||
snd_ctl_notify(codec->card,
|
||||
SNDRV_CTL_EVENT_MASK_VALUE |
|
||||
SNDRV_CTL_EVENT_MASK_INFO,
|
||||
&per_pin->eld_ctl->id);
|
||||
}
|
||||
|
||||
/* update ELD and jack state via HD-audio verbs */
|
||||
static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
|
||||
int repoll)
|
||||
{
|
||||
struct hda_jack_tbl *jack;
|
||||
struct hda_codec *codec = per_pin->codec;
|
||||
|
@ -1547,9 +1609,8 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
|||
* the unsolicited response to avoid custom WARs.
|
||||
*/
|
||||
int present;
|
||||
bool update_eld = false;
|
||||
bool eld_changed = false;
|
||||
bool ret;
|
||||
bool do_repoll = false;
|
||||
|
||||
snd_hda_power_up_pm(codec);
|
||||
present = snd_hda_pin_sense(codec, pin_nid);
|
||||
|
@ -1570,66 +1631,19 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
|||
&eld->eld_size) < 0)
|
||||
eld->eld_valid = false;
|
||||
else {
|
||||
memset(&eld->info, 0, sizeof(struct parsed_hdmi_eld));
|
||||
if (snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer,
|
||||
eld->eld_size) < 0)
|
||||
eld->eld_valid = false;
|
||||
}
|
||||
|
||||
if (eld->eld_valid) {
|
||||
snd_hdmi_show_eld(codec, &eld->info);
|
||||
update_eld = true;
|
||||
}
|
||||
else if (repoll) {
|
||||
schedule_delayed_work(&per_pin->work,
|
||||
msecs_to_jiffies(300));
|
||||
goto unlock;
|
||||
}
|
||||
if (!eld->eld_valid && repoll)
|
||||
do_repoll = true;
|
||||
}
|
||||
|
||||
if (pin_eld->eld_valid != eld->eld_valid)
|
||||
eld_changed = true;
|
||||
if (do_repoll)
|
||||
schedule_delayed_work(&per_pin->work, msecs_to_jiffies(300));
|
||||
else
|
||||
update_eld(codec, per_pin, eld);
|
||||
|
||||
if (pin_eld->eld_valid && !eld->eld_valid)
|
||||
update_eld = true;
|
||||
|
||||
if (update_eld) {
|
||||
bool old_eld_valid = pin_eld->eld_valid;
|
||||
pin_eld->eld_valid = eld->eld_valid;
|
||||
if (pin_eld->eld_size != eld->eld_size ||
|
||||
memcmp(pin_eld->eld_buffer, eld->eld_buffer,
|
||||
eld->eld_size) != 0) {
|
||||
memcpy(pin_eld->eld_buffer, eld->eld_buffer,
|
||||
eld->eld_size);
|
||||
eld_changed = true;
|
||||
}
|
||||
pin_eld->eld_size = eld->eld_size;
|
||||
pin_eld->info = eld->info;
|
||||
|
||||
/*
|
||||
* Re-setup pin and infoframe. This is needed e.g. when
|
||||
* - sink is first plugged-in (infoframe is not set up if !monitor_present)
|
||||
* - transcoder can change during stream playback on Haswell
|
||||
* and this can make HW reset converter selection on a pin.
|
||||
*/
|
||||
if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
|
||||
if (is_haswell_plus(codec) ||
|
||||
is_valleyview_plus(codec)) {
|
||||
intel_verify_pin_cvt_connect(codec, per_pin);
|
||||
intel_not_share_assigned_cvt(codec, pin_nid,
|
||||
per_pin->mux_idx);
|
||||
}
|
||||
|
||||
hdmi_setup_audio_infoframe(codec, per_pin,
|
||||
per_pin->non_pcm);
|
||||
}
|
||||
}
|
||||
|
||||
if (eld_changed)
|
||||
snd_ctl_notify(codec->card,
|
||||
SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
|
||||
&per_pin->eld_ctl->id);
|
||||
unlock:
|
||||
ret = !repoll || !pin_eld->monitor_present || pin_eld->eld_valid;
|
||||
|
||||
jack = snd_hda_jack_tbl_get(codec, pin_nid);
|
||||
|
@ -1641,6 +1655,54 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* update ELD and jack state via audio component */
|
||||
static void sync_eld_via_acomp(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld = &spec->temp_eld;
|
||||
int size;
|
||||
|
||||
mutex_lock(&per_pin->lock);
|
||||
size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid,
|
||||
&eld->monitor_present, eld->eld_buffer,
|
||||
ELD_MAX_SIZE);
|
||||
if (size < 0)
|
||||
goto unlock;
|
||||
if (size > 0) {
|
||||
size = min(size, ELD_MAX_SIZE);
|
||||
if (snd_hdmi_parse_eld(codec, &eld->info,
|
||||
eld->eld_buffer, size) < 0)
|
||||
size = -EINVAL;
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
eld->eld_valid = true;
|
||||
eld->eld_size = size;
|
||||
} else {
|
||||
eld->eld_valid = false;
|
||||
eld->eld_size = 0;
|
||||
}
|
||||
|
||||
update_eld(codec, per_pin, eld);
|
||||
snd_jack_report(per_pin->acomp_jack,
|
||||
eld->monitor_present ? SND_JACK_AVOUT : 0);
|
||||
unlock:
|
||||
mutex_unlock(&per_pin->lock);
|
||||
}
|
||||
|
||||
static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
||||
{
|
||||
struct hda_codec *codec = per_pin->codec;
|
||||
|
||||
if (codec_has_acomp(codec)) {
|
||||
sync_eld_via_acomp(codec, per_pin);
|
||||
return false; /* don't call snd_hda_jack_report_sync() */
|
||||
} else {
|
||||
return hdmi_present_sense_via_verbs(per_pin, repoll);
|
||||
}
|
||||
}
|
||||
|
||||
static void hdmi_repoll_eld(struct work_struct *work)
|
||||
{
|
||||
struct hdmi_spec_per_pin *per_pin =
|
||||
|
@ -1776,17 +1838,6 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
|
|||
return non_pcm;
|
||||
}
|
||||
|
||||
/* There is a fixed mapping between audio pin node and display port
|
||||
* on current Intel platforms:
|
||||
* Pin Widget 5 - PORT B (port = 1 in i915 driver)
|
||||
* Pin Widget 6 - PORT C (port = 2 in i915 driver)
|
||||
* Pin Widget 7 - PORT D (port = 3 in i915 driver)
|
||||
*/
|
||||
static int intel_pin2port(hda_nid_t pin_nid)
|
||||
{
|
||||
return pin_nid - 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* HDMI callbacks
|
||||
*/
|
||||
|
@ -1803,7 +1854,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
hda_nid_t pin_nid = per_pin->pin_nid;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct i915_audio_component *acomp = codec->bus->core.audio_component;
|
||||
bool non_pcm;
|
||||
int pinctl;
|
||||
|
||||
|
@ -1822,10 +1872,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|||
|
||||
/* Call sync_audio_rate to set the N/CTS/M manually if necessary */
|
||||
/* Todo: add DP1.2 MST audio support later */
|
||||
if (acomp && acomp->ops && acomp->ops->sync_audio_rate)
|
||||
acomp->ops->sync_audio_rate(acomp->dev,
|
||||
intel_pin2port(pin_nid),
|
||||
runtime->rate);
|
||||
snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate);
|
||||
|
||||
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
|
||||
mutex_lock(&per_pin->lock);
|
||||
|
@ -2091,6 +2138,30 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void free_acomp_jack_priv(struct snd_jack *jack)
|
||||
{
|
||||
struct hdmi_spec_per_pin *per_pin = jack->private_data;
|
||||
|
||||
per_pin->acomp_jack = NULL;
|
||||
}
|
||||
|
||||
static int add_acomp_jack_kctl(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin,
|
||||
const char *name)
|
||||
{
|
||||
struct snd_jack *jack;
|
||||
int err;
|
||||
|
||||
err = snd_jack_new(codec->card, name, SND_JACK_AVOUT, &jack,
|
||||
true, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
per_pin->acomp_jack = jack;
|
||||
jack->private_data = per_pin;
|
||||
jack->private_free = free_acomp_jack_priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
|
||||
{
|
||||
char hdmi_str[32] = "HDMI/DP";
|
||||
|
@ -2101,6 +2172,8 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
|
|||
|
||||
if (pcmdev > 0)
|
||||
sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
|
||||
if (codec_has_acomp(codec))
|
||||
return add_acomp_jack_kctl(codec, per_pin, hdmi_str);
|
||||
phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid);
|
||||
if (phantom_jack)
|
||||
strncat(hdmi_str, " Phantom",
|
||||
|
@ -2196,8 +2269,10 @@ static int generic_hdmi_init(struct hda_codec *codec)
|
|||
hda_nid_t pin_nid = per_pin->pin_nid;
|
||||
|
||||
hdmi_init_pin(codec, pin_nid);
|
||||
snd_hda_jack_detect_enable_callback(codec, pin_nid,
|
||||
codec->jackpoll_interval > 0 ? jack_callback : NULL);
|
||||
if (!codec_has_acomp(codec))
|
||||
snd_hda_jack_detect_enable_callback(codec, pin_nid,
|
||||
codec->jackpoll_interval > 0 ?
|
||||
jack_callback : NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -2219,7 +2294,7 @@ static void generic_hdmi_free(struct hda_codec *codec)
|
|||
struct hdmi_spec *spec = codec->spec;
|
||||
int pin_idx;
|
||||
|
||||
if (is_haswell_plus(codec) || is_valleyview_plus(codec))
|
||||
if (codec_has_acomp(codec))
|
||||
snd_hdac_i915_register_notifier(NULL);
|
||||
|
||||
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
|
@ -2227,8 +2302,12 @@ static void generic_hdmi_free(struct hda_codec *codec)
|
|||
|
||||
cancel_delayed_work_sync(&per_pin->work);
|
||||
eld_proc_free(per_pin);
|
||||
if (per_pin->acomp_jack)
|
||||
snd_device_free(codec->card, per_pin->acomp_jack);
|
||||
}
|
||||
|
||||
if (spec->i915_bound)
|
||||
snd_hdac_i915_exit(&codec->bus->core);
|
||||
hdmi_array_free(spec);
|
||||
kfree(spec);
|
||||
}
|
||||
|
@ -2357,6 +2436,9 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
|
|||
*/
|
||||
if (snd_power_get_state(codec->card) != SNDRV_CTL_POWER_D0)
|
||||
return;
|
||||
/* ditto during suspend/resume process itself */
|
||||
if (atomic_read(&(codec)->core.in_pm))
|
||||
return;
|
||||
|
||||
check_presence_and_report(codec, pin_nid);
|
||||
}
|
||||
|
@ -2373,6 +2455,12 @@ static int patch_generic_hdmi(struct hda_codec *codec)
|
|||
codec->spec = spec;
|
||||
hdmi_array_init(spec, 4);
|
||||
|
||||
/* Try to bind with i915 for any Intel codecs (if not done yet) */
|
||||
if (!codec_has_acomp(codec) &&
|
||||
(codec->core.vendor_id >> 16) == 0x8086)
|
||||
if (!snd_hdac_i915_init(&codec->bus->core))
|
||||
spec->i915_bound = true;
|
||||
|
||||
if (is_haswell_plus(codec)) {
|
||||
intel_haswell_enable_all_pins(codec, true);
|
||||
intel_haswell_fixup_enable_dp12(codec);
|
||||
|
@ -2388,7 +2476,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
|
|||
is_broxton(codec))
|
||||
codec->core.link_power_control = 1;
|
||||
|
||||
if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
|
||||
if (codec_has_acomp(codec)) {
|
||||
codec->depop_delay = 0;
|
||||
spec->i915_audio_ops.audio_ptr = codec;
|
||||
spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
|
||||
|
@ -2396,6 +2484,8 @@ static int patch_generic_hdmi(struct hda_codec *codec)
|
|||
}
|
||||
|
||||
if (hdmi_parse_codec(codec) < 0) {
|
||||
if (spec->i915_bound)
|
||||
snd_hdac_i915_exit(&codec->bus->core);
|
||||
codec->spec = NULL;
|
||||
kfree(spec);
|
||||
return -EINVAL;
|
||||
|
@ -3579,6 +3669,7 @@ HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi),
|
|||
HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi),
|
||||
|
|
|
@ -4666,6 +4666,7 @@ enum {
|
|||
ALC290_FIXUP_SUBWOOFER,
|
||||
ALC290_FIXUP_SUBWOOFER_HSJACK,
|
||||
ALC269_FIXUP_THINKPAD_ACPI,
|
||||
ALC269_FIXUP_DMIC_THINKPAD_ACPI,
|
||||
ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
|
||||
ALC255_FIXUP_HEADSET_MODE,
|
||||
|
@ -5103,6 +5104,12 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = hda_fixup_thinkpad_acpi,
|
||||
},
|
||||
[ALC269_FIXUP_DMIC_THINKPAD_ACPI] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc_fixup_inv_dmic,
|
||||
.chained = true,
|
||||
.chain_id = ALC269_FIXUP_THINKPAD_ACPI,
|
||||
},
|
||||
[ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
|
@ -5324,6 +5331,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
|
||||
SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS),
|
||||
SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X),
|
||||
SND_PCI_QUIRK(0x1028, 0x05be, "Dell Latitude E6540", ALC292_FIXUP_DELL_E7X),
|
||||
SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X),
|
||||
SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X),
|
||||
SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER),
|
||||
|
@ -5332,6 +5340,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
|
||||
SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
|
||||
SND_PCI_QUIRK(0x1028, 0x062c, "Dell Latitude E5550", ALC292_FIXUP_DELL_E7X),
|
||||
SND_PCI_QUIRK(0x1028, 0x062e, "Dell Latitude E7450", ALC292_FIXUP_DELL_E7X),
|
||||
SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
|
||||
SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
|
@ -5457,6 +5466,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
|
||||
SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
|
||||
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
|
||||
SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
|
||||
|
@ -5615,6 +5625,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|||
{0x12, 0x90a60170},
|
||||
{0x14, 0x90170130},
|
||||
{0x21, 0x02211040}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
{0x12, 0x90a60170},
|
||||
{0x14, 0x90171130},
|
||||
{0x21, 0x02211040}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
{0x12, 0x90a60170},
|
||||
{0x14, 0x90170140},
|
||||
|
|
|
@ -174,7 +174,7 @@ static int ap_cs8427_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
static struct snd_i2c_ops ap_cs8427_i2c_ops = {
|
||||
static const struct snd_i2c_ops ap_cs8427_i2c_ops = {
|
||||
.sendbytes = ap_cs8427_sendbytes,
|
||||
.readbytes = ap_cs8427_readbytes,
|
||||
.probeaddr = ap_cs8427_probeaddr,
|
||||
|
|
|
@ -38,6 +38,7 @@ config SND_SOC_TOPOLOGY
|
|||
|
||||
# All the supported SoCs
|
||||
source "sound/soc/adi/Kconfig"
|
||||
source "sound/soc/amd/Kconfig"
|
||||
source "sound/soc/atmel/Kconfig"
|
||||
source "sound/soc/au1x/Kconfig"
|
||||
source "sound/soc/bcm/Kconfig"
|
||||
|
@ -50,6 +51,7 @@ source "sound/soc/jz4740/Kconfig"
|
|||
source "sound/soc/nuc900/Kconfig"
|
||||
source "sound/soc/omap/Kconfig"
|
||||
source "sound/soc/kirkwood/Kconfig"
|
||||
source "sound/soc/img/Kconfig"
|
||||
source "sound/soc/intel/Kconfig"
|
||||
source "sound/soc/mediatek/Kconfig"
|
||||
source "sound/soc/mxs/Kconfig"
|
||||
|
|
|
@ -18,6 +18,7 @@ obj-$(CONFIG_SND_SOC) += snd-soc-core.o
|
|||
obj-$(CONFIG_SND_SOC) += codecs/
|
||||
obj-$(CONFIG_SND_SOC) += generic/
|
||||
obj-$(CONFIG_SND_SOC) += adi/
|
||||
obj-$(CONFIG_SND_SOC) += amd/
|
||||
obj-$(CONFIG_SND_SOC) += atmel/
|
||||
obj-$(CONFIG_SND_SOC) += au1x/
|
||||
obj-$(CONFIG_SND_SOC) += bcm/
|
||||
|
@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC) += davinci/
|
|||
obj-$(CONFIG_SND_SOC) += dwc/
|
||||
obj-$(CONFIG_SND_SOC) += fsl/
|
||||
obj-$(CONFIG_SND_SOC) += jz4740/
|
||||
obj-$(CONFIG_SND_SOC) += img/
|
||||
obj-$(CONFIG_SND_SOC) += intel/
|
||||
obj-$(CONFIG_SND_SOC) += mediatek/
|
||||
obj-$(CONFIG_SND_SOC) += mxs/
|
||||
|
|
4
sound/soc/amd/Kconfig
Normal file
4
sound/soc/amd/Kconfig
Normal file
|
@ -0,0 +1,4 @@
|
|||
config SND_SOC_AMD_ACP
|
||||
tristate "AMD Audio Coprocessor support"
|
||||
help
|
||||
This option enables ACP DMA support on AMD platform.
|
3
sound/soc/amd/Makefile
Normal file
3
sound/soc/amd/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
snd-soc-acp-pcm-objs := acp-pcm-dma.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP) += snd-soc-acp-pcm.o
|
1043
sound/soc/amd/acp-pcm-dma.c
Normal file
1043
sound/soc/amd/acp-pcm-dma.c
Normal file
File diff suppressed because it is too large
Load diff
118
sound/soc/amd/acp.h
Normal file
118
sound/soc/amd/acp.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
#ifndef __ACP_HW_H
|
||||
#define __ACP_HW_H
|
||||
|
||||
#include "include/acp_2_2_d.h"
|
||||
#include "include/acp_2_2_sh_mask.h"
|
||||
|
||||
#define ACP_PAGE_SIZE_4K_ENABLE 0x02
|
||||
|
||||
#define ACP_PLAYBACK_PTE_OFFSET 10
|
||||
#define ACP_CAPTURE_PTE_OFFSET 0
|
||||
|
||||
#define ACP_GARLIC_CNTL_DEFAULT 0x00000FB4
|
||||
#define ACP_ONION_CNTL_DEFAULT 0x00000FB4
|
||||
|
||||
#define ACP_PHYSICAL_BASE 0x14000
|
||||
|
||||
/* Playback SRAM address (as a destination in dma descriptor) */
|
||||
#define ACP_SHARED_RAM_BANK_1_ADDRESS 0x4002000
|
||||
|
||||
/* Capture SRAM address (as a source in dma descriptor) */
|
||||
#define ACP_SHARED_RAM_BANK_5_ADDRESS 0x400A000
|
||||
|
||||
#define ACP_DMA_RESET_TIME 10000
|
||||
#define ACP_CLOCK_EN_TIME_OUT_VALUE 0x000000FF
|
||||
#define ACP_SOFT_RESET_DONE_TIME_OUT_VALUE 0x000000FF
|
||||
#define ACP_DMA_COMPLETE_TIME_OUT_VALUE 0x000000FF
|
||||
|
||||
#define ACP_SRAM_BASE_ADDRESS 0x4000000
|
||||
#define ACP_DAGB_GRP_SRAM_BASE_ADDRESS 0x4001000
|
||||
#define ACP_DAGB_GRP_SRBM_SRAM_BASE_OFFSET 0x1000
|
||||
#define ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS 0x00000000
|
||||
#define ACP_INTERNAL_APERTURE_WINDOW_4_ADDRESS 0x01800000
|
||||
|
||||
#define TO_ACP_I2S_1 0x2
|
||||
#define TO_ACP_I2S_2 0x4
|
||||
#define FROM_ACP_I2S_1 0xa
|
||||
#define FROM_ACP_I2S_2 0xb
|
||||
|
||||
#define ACP_TILE_ON_MASK 0x03
|
||||
#define ACP_TILE_OFF_MASK 0x02
|
||||
#define ACP_TILE_ON_RETAIN_REG_MASK 0x1f
|
||||
#define ACP_TILE_OFF_RETAIN_REG_MASK 0x20
|
||||
|
||||
#define ACP_TILE_P1_MASK 0x3e
|
||||
#define ACP_TILE_P2_MASK 0x3d
|
||||
#define ACP_TILE_DSP0_MASK 0x3b
|
||||
#define ACP_TILE_DSP1_MASK 0x37
|
||||
|
||||
#define ACP_TILE_DSP2_MASK 0x2f
|
||||
/* Playback DMA channels */
|
||||
#define SYSRAM_TO_ACP_CH_NUM 12
|
||||
#define ACP_TO_I2S_DMA_CH_NUM 13
|
||||
|
||||
/* Capture DMA channels */
|
||||
#define ACP_TO_SYSRAM_CH_NUM 14
|
||||
#define I2S_TO_ACP_DMA_CH_NUM 15
|
||||
|
||||
#define NUM_DSCRS_PER_CHANNEL 2
|
||||
|
||||
#define PLAYBACK_START_DMA_DESCR_CH12 0
|
||||
#define PLAYBACK_END_DMA_DESCR_CH12 1
|
||||
#define PLAYBACK_START_DMA_DESCR_CH13 2
|
||||
#define PLAYBACK_END_DMA_DESCR_CH13 3
|
||||
|
||||
#define CAPTURE_START_DMA_DESCR_CH14 4
|
||||
#define CAPTURE_END_DMA_DESCR_CH14 5
|
||||
#define CAPTURE_START_DMA_DESCR_CH15 6
|
||||
#define CAPTURE_END_DMA_DESCR_CH15 7
|
||||
|
||||
enum acp_dma_priority_level {
|
||||
/* 0x0 Specifies the DMA channel is given normal priority */
|
||||
ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0,
|
||||
/* 0x1 Specifies the DMA channel is given high priority */
|
||||
ACP_DMA_PRIORITY_LEVEL_HIGH = 0x1,
|
||||
ACP_DMA_PRIORITY_LEVEL_FORCESIZE = 0xFF
|
||||
};
|
||||
|
||||
struct audio_substream_data {
|
||||
struct page *pg;
|
||||
unsigned int order;
|
||||
u16 num_of_pages;
|
||||
u16 direction;
|
||||
uint64_t size;
|
||||
void __iomem *acp_mmio;
|
||||
};
|
||||
|
||||
enum {
|
||||
ACP_TILE_P1 = 0,
|
||||
ACP_TILE_P2,
|
||||
ACP_TILE_DSP0,
|
||||
ACP_TILE_DSP1,
|
||||
ACP_TILE_DSP2,
|
||||
};
|
||||
|
||||
enum {
|
||||
ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION = 0x0,
|
||||
ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC = 0x1,
|
||||
ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM = 0x8,
|
||||
ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM = 0x9,
|
||||
ACP_DMA_ATTRIBUTES_FORCE_SIZE = 0xF
|
||||
};
|
||||
|
||||
typedef struct acp_dma_dscr_transfer {
|
||||
/* Specifies the source memory location for the DMA data transfer. */
|
||||
u32 src;
|
||||
/* Specifies the destination memory location to where the data will
|
||||
* be transferred.
|
||||
*/
|
||||
u32 dest;
|
||||
/* Specifies the number of bytes need to be transferred
|
||||
* from source to destination memory.Transfer direction & IOC enable
|
||||
*/
|
||||
u32 xfer_val;
|
||||
/* Reserved for future use */
|
||||
u32 reserved;
|
||||
} acp_dma_dscr_transfer_t;
|
||||
|
||||
#endif /*__ACP_HW_H */
|
609
sound/soc/amd/include/acp_2_2_d.h
Normal file
609
sound/soc/amd/include/acp_2_2_d.h
Normal file
|
@ -0,0 +1,609 @@
|
|||
/*
|
||||
* ACP_2_2 Register documentation
|
||||
*
|
||||
* Copyright (C) 2014 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ACP_2_2_D_H
|
||||
#define ACP_2_2_D_H
|
||||
|
||||
#define mmACP_DMA_CNTL_0 0x5000
|
||||
#define mmACP_DMA_CNTL_1 0x5001
|
||||
#define mmACP_DMA_CNTL_2 0x5002
|
||||
#define mmACP_DMA_CNTL_3 0x5003
|
||||
#define mmACP_DMA_CNTL_4 0x5004
|
||||
#define mmACP_DMA_CNTL_5 0x5005
|
||||
#define mmACP_DMA_CNTL_6 0x5006
|
||||
#define mmACP_DMA_CNTL_7 0x5007
|
||||
#define mmACP_DMA_CNTL_8 0x5008
|
||||
#define mmACP_DMA_CNTL_9 0x5009
|
||||
#define mmACP_DMA_CNTL_10 0x500a
|
||||
#define mmACP_DMA_CNTL_11 0x500b
|
||||
#define mmACP_DMA_CNTL_12 0x500c
|
||||
#define mmACP_DMA_CNTL_13 0x500d
|
||||
#define mmACP_DMA_CNTL_14 0x500e
|
||||
#define mmACP_DMA_CNTL_15 0x500f
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_0 0x5010
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_1 0x5011
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_2 0x5012
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_3 0x5013
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_4 0x5014
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_5 0x5015
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_6 0x5016
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_7 0x5017
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_8 0x5018
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_9 0x5019
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_10 0x501a
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_11 0x501b
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_12 0x501c
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_13 0x501d
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_14 0x501e
|
||||
#define mmACP_DMA_DSCR_STRT_IDX_15 0x501f
|
||||
#define mmACP_DMA_DSCR_CNT_0 0x5020
|
||||
#define mmACP_DMA_DSCR_CNT_1 0x5021
|
||||
#define mmACP_DMA_DSCR_CNT_2 0x5022
|
||||
#define mmACP_DMA_DSCR_CNT_3 0x5023
|
||||
#define mmACP_DMA_DSCR_CNT_4 0x5024
|
||||
#define mmACP_DMA_DSCR_CNT_5 0x5025
|
||||
#define mmACP_DMA_DSCR_CNT_6 0x5026
|
||||
#define mmACP_DMA_DSCR_CNT_7 0x5027
|
||||
#define mmACP_DMA_DSCR_CNT_8 0x5028
|
||||
#define mmACP_DMA_DSCR_CNT_9 0x5029
|
||||
#define mmACP_DMA_DSCR_CNT_10 0x502a
|
||||
#define mmACP_DMA_DSCR_CNT_11 0x502b
|
||||
#define mmACP_DMA_DSCR_CNT_12 0x502c
|
||||
#define mmACP_DMA_DSCR_CNT_13 0x502d
|
||||
#define mmACP_DMA_DSCR_CNT_14 0x502e
|
||||
#define mmACP_DMA_DSCR_CNT_15 0x502f
|
||||
#define mmACP_DMA_PRIO_0 0x5030
|
||||
#define mmACP_DMA_PRIO_1 0x5031
|
||||
#define mmACP_DMA_PRIO_2 0x5032
|
||||
#define mmACP_DMA_PRIO_3 0x5033
|
||||
#define mmACP_DMA_PRIO_4 0x5034
|
||||
#define mmACP_DMA_PRIO_5 0x5035
|
||||
#define mmACP_DMA_PRIO_6 0x5036
|
||||
#define mmACP_DMA_PRIO_7 0x5037
|
||||
#define mmACP_DMA_PRIO_8 0x5038
|
||||
#define mmACP_DMA_PRIO_9 0x5039
|
||||
#define mmACP_DMA_PRIO_10 0x503a
|
||||
#define mmACP_DMA_PRIO_11 0x503b
|
||||
#define mmACP_DMA_PRIO_12 0x503c
|
||||
#define mmACP_DMA_PRIO_13 0x503d
|
||||
#define mmACP_DMA_PRIO_14 0x503e
|
||||
#define mmACP_DMA_PRIO_15 0x503f
|
||||
#define mmACP_DMA_CUR_DSCR_0 0x5040
|
||||
#define mmACP_DMA_CUR_DSCR_1 0x5041
|
||||
#define mmACP_DMA_CUR_DSCR_2 0x5042
|
||||
#define mmACP_DMA_CUR_DSCR_3 0x5043
|
||||
#define mmACP_DMA_CUR_DSCR_4 0x5044
|
||||
#define mmACP_DMA_CUR_DSCR_5 0x5045
|
||||
#define mmACP_DMA_CUR_DSCR_6 0x5046
|
||||
#define mmACP_DMA_CUR_DSCR_7 0x5047
|
||||
#define mmACP_DMA_CUR_DSCR_8 0x5048
|
||||
#define mmACP_DMA_CUR_DSCR_9 0x5049
|
||||
#define mmACP_DMA_CUR_DSCR_10 0x504a
|
||||
#define mmACP_DMA_CUR_DSCR_11 0x504b
|
||||
#define mmACP_DMA_CUR_DSCR_12 0x504c
|
||||
#define mmACP_DMA_CUR_DSCR_13 0x504d
|
||||
#define mmACP_DMA_CUR_DSCR_14 0x504e
|
||||
#define mmACP_DMA_CUR_DSCR_15 0x504f
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_0 0x5050
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_1 0x5051
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_2 0x5052
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_3 0x5053
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_4 0x5054
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_5 0x5055
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_6 0x5056
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_7 0x5057
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_8 0x5058
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_9 0x5059
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_10 0x505a
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_11 0x505b
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_12 0x505c
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_13 0x505d
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_14 0x505e
|
||||
#define mmACP_DMA_CUR_TRANS_CNT_15 0x505f
|
||||
#define mmACP_DMA_ERR_STS_0 0x5060
|
||||
#define mmACP_DMA_ERR_STS_1 0x5061
|
||||
#define mmACP_DMA_ERR_STS_2 0x5062
|
||||
#define mmACP_DMA_ERR_STS_3 0x5063
|
||||
#define mmACP_DMA_ERR_STS_4 0x5064
|
||||
#define mmACP_DMA_ERR_STS_5 0x5065
|
||||
#define mmACP_DMA_ERR_STS_6 0x5066
|
||||
#define mmACP_DMA_ERR_STS_7 0x5067
|
||||
#define mmACP_DMA_ERR_STS_8 0x5068
|
||||
#define mmACP_DMA_ERR_STS_9 0x5069
|
||||
#define mmACP_DMA_ERR_STS_10 0x506a
|
||||
#define mmACP_DMA_ERR_STS_11 0x506b
|
||||
#define mmACP_DMA_ERR_STS_12 0x506c
|
||||
#define mmACP_DMA_ERR_STS_13 0x506d
|
||||
#define mmACP_DMA_ERR_STS_14 0x506e
|
||||
#define mmACP_DMA_ERR_STS_15 0x506f
|
||||
#define mmACP_DMA_DESC_BASE_ADDR 0x5070
|
||||
#define mmACP_DMA_DESC_MAX_NUM_DSCR 0x5071
|
||||
#define mmACP_DMA_CH_STS 0x5072
|
||||
#define mmACP_DMA_CH_GROUP 0x5073
|
||||
#define mmACP_DSP0_CACHE_OFFSET0 0x5078
|
||||
#define mmACP_DSP0_CACHE_SIZE0 0x5079
|
||||
#define mmACP_DSP0_CACHE_OFFSET1 0x507a
|
||||
#define mmACP_DSP0_CACHE_SIZE1 0x507b
|
||||
#define mmACP_DSP0_CACHE_OFFSET2 0x507c
|
||||
#define mmACP_DSP0_CACHE_SIZE2 0x507d
|
||||
#define mmACP_DSP0_CACHE_OFFSET3 0x507e
|
||||
#define mmACP_DSP0_CACHE_SIZE3 0x507f
|
||||
#define mmACP_DSP0_CACHE_OFFSET4 0x5080
|
||||
#define mmACP_DSP0_CACHE_SIZE4 0x5081
|
||||
#define mmACP_DSP0_CACHE_OFFSET5 0x5082
|
||||
#define mmACP_DSP0_CACHE_SIZE5 0x5083
|
||||
#define mmACP_DSP0_CACHE_OFFSET6 0x5084
|
||||
#define mmACP_DSP0_CACHE_SIZE6 0x5085
|
||||
#define mmACP_DSP0_CACHE_OFFSET7 0x5086
|
||||
#define mmACP_DSP0_CACHE_SIZE7 0x5087
|
||||
#define mmACP_DSP0_CACHE_OFFSET8 0x5088
|
||||
#define mmACP_DSP0_CACHE_SIZE8 0x5089
|
||||
#define mmACP_DSP0_NONCACHE_OFFSET0 0x508a
|
||||
#define mmACP_DSP0_NONCACHE_SIZE0 0x508b
|
||||
#define mmACP_DSP0_NONCACHE_OFFSET1 0x508c
|
||||
#define mmACP_DSP0_NONCACHE_SIZE1 0x508d
|
||||
#define mmACP_DSP0_DEBUG_PC 0x508e
|
||||
#define mmACP_DSP0_NMI_SEL 0x508f
|
||||
#define mmACP_DSP0_CLKRST_CNTL 0x5090
|
||||
#define mmACP_DSP0_RUNSTALL 0x5091
|
||||
#define mmACP_DSP0_OCD_HALT_ON_RST 0x5092
|
||||
#define mmACP_DSP0_WAIT_MODE 0x5093
|
||||
#define mmACP_DSP0_VECT_SEL 0x5094
|
||||
#define mmACP_DSP0_DEBUG_REG1 0x5095
|
||||
#define mmACP_DSP0_DEBUG_REG2 0x5096
|
||||
#define mmACP_DSP0_DEBUG_REG3 0x5097
|
||||
#define mmACP_DSP1_CACHE_OFFSET0 0x509d
|
||||
#define mmACP_DSP1_CACHE_SIZE0 0x509e
|
||||
#define mmACP_DSP1_CACHE_OFFSET1 0x509f
|
||||
#define mmACP_DSP1_CACHE_SIZE1 0x50a0
|
||||
#define mmACP_DSP1_CACHE_OFFSET2 0x50a1
|
||||
#define mmACP_DSP1_CACHE_SIZE2 0x50a2
|
||||
#define mmACP_DSP1_CACHE_OFFSET3 0x50a3
|
||||
#define mmACP_DSP1_CACHE_SIZE3 0x50a4
|
||||
#define mmACP_DSP1_CACHE_OFFSET4 0x50a5
|
||||
#define mmACP_DSP1_CACHE_SIZE4 0x50a6
|
||||
#define mmACP_DSP1_CACHE_OFFSET5 0x50a7
|
||||
#define mmACP_DSP1_CACHE_SIZE5 0x50a8
|
||||
#define mmACP_DSP1_CACHE_OFFSET6 0x50a9
|
||||
#define mmACP_DSP1_CACHE_SIZE6 0x50aa
|
||||
#define mmACP_DSP1_CACHE_OFFSET7 0x50ab
|
||||
#define mmACP_DSP1_CACHE_SIZE7 0x50ac
|
||||
#define mmACP_DSP1_CACHE_OFFSET8 0x50ad
|
||||
#define mmACP_DSP1_CACHE_SIZE8 0x50ae
|
||||
#define mmACP_DSP1_NONCACHE_OFFSET0 0x50af
|
||||
#define mmACP_DSP1_NONCACHE_SIZE0 0x50b0
|
||||
#define mmACP_DSP1_NONCACHE_OFFSET1 0x50b1
|
||||
#define mmACP_DSP1_NONCACHE_SIZE1 0x50b2
|
||||
#define mmACP_DSP1_DEBUG_PC 0x50b3
|
||||
#define mmACP_DSP1_NMI_SEL 0x50b4
|
||||
#define mmACP_DSP1_CLKRST_CNTL 0x50b5
|
||||
#define mmACP_DSP1_RUNSTALL 0x50b6
|
||||
#define mmACP_DSP1_OCD_HALT_ON_RST 0x50b7
|
||||
#define mmACP_DSP1_WAIT_MODE 0x50b8
|
||||
#define mmACP_DSP1_VECT_SEL 0x50b9
|
||||
#define mmACP_DSP1_DEBUG_REG1 0x50ba
|
||||
#define mmACP_DSP1_DEBUG_REG2 0x50bb
|
||||
#define mmACP_DSP1_DEBUG_REG3 0x50bc
|
||||
#define mmACP_DSP2_CACHE_OFFSET0 0x50c2
|
||||
#define mmACP_DSP2_CACHE_SIZE0 0x50c3
|
||||
#define mmACP_DSP2_CACHE_OFFSET1 0x50c4
|
||||
#define mmACP_DSP2_CACHE_SIZE1 0x50c5
|
||||
#define mmACP_DSP2_CACHE_OFFSET2 0x50c6
|
||||
#define mmACP_DSP2_CACHE_SIZE2 0x50c7
|
||||
#define mmACP_DSP2_CACHE_OFFSET3 0x50c8
|
||||
#define mmACP_DSP2_CACHE_SIZE3 0x50c9
|
||||
#define mmACP_DSP2_CACHE_OFFSET4 0x50ca
|
||||
#define mmACP_DSP2_CACHE_SIZE4 0x50cb
|
||||
#define mmACP_DSP2_CACHE_OFFSET5 0x50cc
|
||||
#define mmACP_DSP2_CACHE_SIZE5 0x50cd
|
||||
#define mmACP_DSP2_CACHE_OFFSET6 0x50ce
|
||||
#define mmACP_DSP2_CACHE_SIZE6 0x50cf
|
||||
#define mmACP_DSP2_CACHE_OFFSET7 0x50d0
|
||||
#define mmACP_DSP2_CACHE_SIZE7 0x50d1
|
||||
#define mmACP_DSP2_CACHE_OFFSET8 0x50d2
|
||||
#define mmACP_DSP2_CACHE_SIZE8 0x50d3
|
||||
#define mmACP_DSP2_NONCACHE_OFFSET0 0x50d4
|
||||
#define mmACP_DSP2_NONCACHE_SIZE0 0x50d5
|
||||
#define mmACP_DSP2_NONCACHE_OFFSET1 0x50d6
|
||||
#define mmACP_DSP2_NONCACHE_SIZE1 0x50d7
|
||||
#define mmACP_DSP2_DEBUG_PC 0x50d8
|
||||
#define mmACP_DSP2_NMI_SEL 0x50d9
|
||||
#define mmACP_DSP2_CLKRST_CNTL 0x50da
|
||||
#define mmACP_DSP2_RUNSTALL 0x50db
|
||||
#define mmACP_DSP2_OCD_HALT_ON_RST 0x50dc
|
||||
#define mmACP_DSP2_WAIT_MODE 0x50dd
|
||||
#define mmACP_DSP2_VECT_SEL 0x50de
|
||||
#define mmACP_DSP2_DEBUG_REG1 0x50df
|
||||
#define mmACP_DSP2_DEBUG_REG2 0x50e0
|
||||
#define mmACP_DSP2_DEBUG_REG3 0x50e1
|
||||
#define mmACP_AXI2DAGB_ONION_CNTL 0x50e7
|
||||
#define mmACP_AXI2DAGB_ONION_ERR_STATUS_WR 0x50e8
|
||||
#define mmACP_AXI2DAGB_ONION_ERR_STATUS_RD 0x50e9
|
||||
#define mmACP_DAGB_Onion_TransPerf_Counter_Control 0x50ea
|
||||
#define mmACP_DAGB_Onion_Wr_TransPerf_Counter_Current 0x50eb
|
||||
#define mmACP_DAGB_Onion_Wr_TransPerf_Counter_Peak 0x50ec
|
||||
#define mmACP_DAGB_Onion_Rd_TransPerf_Counter_Current 0x50ed
|
||||
#define mmACP_DAGB_Onion_Rd_TransPerf_Counter_Peak 0x50ee
|
||||
#define mmACP_AXI2DAGB_GARLIC_CNTL 0x50f3
|
||||
#define mmACP_AXI2DAGB_GARLIC_ERR_STATUS_WR 0x50f4
|
||||
#define mmACP_AXI2DAGB_GARLIC_ERR_STATUS_RD 0x50f5
|
||||
#define mmACP_DAGB_Garlic_TransPerf_Counter_Control 0x50f6
|
||||
#define mmACP_DAGB_Garlic_Wr_TransPerf_Counter_Current 0x50f7
|
||||
#define mmACP_DAGB_Garlic_Wr_TransPerf_Counter_Peak 0x50f8
|
||||
#define mmACP_DAGB_Garlic_Rd_TransPerf_Counter_Current 0x50f9
|
||||
#define mmACP_DAGB_Garlic_Rd_TransPerf_Counter_Peak 0x50fa
|
||||
#define mmACP_DAGB_PAGE_SIZE_GRP_1 0x50ff
|
||||
#define mmACP_DAGB_BASE_ADDR_GRP_1 0x5100
|
||||
#define mmACP_DAGB_PAGE_SIZE_GRP_2 0x5101
|
||||
#define mmACP_DAGB_BASE_ADDR_GRP_2 0x5102
|
||||
#define mmACP_DAGB_PAGE_SIZE_GRP_3 0x5103
|
||||
#define mmACP_DAGB_BASE_ADDR_GRP_3 0x5104
|
||||
#define mmACP_DAGB_PAGE_SIZE_GRP_4 0x5105
|
||||
#define mmACP_DAGB_BASE_ADDR_GRP_4 0x5106
|
||||
#define mmACP_DAGB_PAGE_SIZE_GRP_5 0x5107
|
||||
#define mmACP_DAGB_BASE_ADDR_GRP_5 0x5108
|
||||
#define mmACP_DAGB_PAGE_SIZE_GRP_6 0x5109
|
||||
#define mmACP_DAGB_BASE_ADDR_GRP_6 0x510a
|
||||
#define mmACP_DAGB_PAGE_SIZE_GRP_7 0x510b
|
||||
#define mmACP_DAGB_BASE_ADDR_GRP_7 0x510c
|
||||
#define mmACP_DAGB_PAGE_SIZE_GRP_8 0x510d
|
||||
#define mmACP_DAGB_BASE_ADDR_GRP_8 0x510e
|
||||
#define mmACP_DAGB_ATU_CTRL 0x510f
|
||||
#define mmACP_CONTROL 0x5131
|
||||
#define mmACP_STATUS 0x5133
|
||||
#define mmACP_SOFT_RESET 0x5134
|
||||
#define mmACP_PwrMgmt_CNTL 0x5135
|
||||
#define mmACP_CAC_INDICATOR_CONTROL 0x5136
|
||||
#define mmACP_SMU_MAILBOX 0x5137
|
||||
#define mmACP_FUTURE_REG_SCLK_0 0x5138
|
||||
#define mmACP_FUTURE_REG_SCLK_1 0x5139
|
||||
#define mmACP_FUTURE_REG_SCLK_2 0x513a
|
||||
#define mmACP_FUTURE_REG_SCLK_3 0x513b
|
||||
#define mmACP_FUTURE_REG_SCLK_4 0x513c
|
||||
#define mmACP_DAGB_DEBUG_CNT_ENABLE 0x513d
|
||||
#define mmACP_DAGBG_WR_ASK_CNT 0x513e
|
||||
#define mmACP_DAGBG_WR_GO_CNT 0x513f
|
||||
#define mmACP_DAGBG_WR_EXP_RESP_CNT 0x5140
|
||||
#define mmACP_DAGBG_WR_ACTUAL_RESP_CNT 0x5141
|
||||
#define mmACP_DAGBG_RD_ASK_CNT 0x5142
|
||||
#define mmACP_DAGBG_RD_GO_CNT 0x5143
|
||||
#define mmACP_DAGBG_RD_EXP_RESP_CNT 0x5144
|
||||
#define mmACP_DAGBG_RD_ACTUAL_RESP_CNT 0x5145
|
||||
#define mmACP_DAGBO_WR_ASK_CNT 0x5146
|
||||
#define mmACP_DAGBO_WR_GO_CNT 0x5147
|
||||
#define mmACP_DAGBO_WR_EXP_RESP_CNT 0x5148
|
||||
#define mmACP_DAGBO_WR_ACTUAL_RESP_CNT 0x5149
|
||||
#define mmACP_DAGBO_RD_ASK_CNT 0x514a
|
||||
#define mmACP_DAGBO_RD_GO_CNT 0x514b
|
||||
#define mmACP_DAGBO_RD_EXP_RESP_CNT 0x514c
|
||||
#define mmACP_DAGBO_RD_ACTUAL_RESP_CNT 0x514d
|
||||
#define mmACP_BRB_CONTROL 0x5156
|
||||
#define mmACP_EXTERNAL_INTR_ENB 0x5157
|
||||
#define mmACP_EXTERNAL_INTR_CNTL 0x5158
|
||||
#define mmACP_ERROR_SOURCE_STS 0x5159
|
||||
#define mmACP_DSP_SW_INTR_TRIG 0x515a
|
||||
#define mmACP_DSP_SW_INTR_CNTL 0x515b
|
||||
#define mmACP_DAGBG_TIMEOUT_CNTL 0x515c
|
||||
#define mmACP_DAGBO_TIMEOUT_CNTL 0x515d
|
||||
#define mmACP_EXTERNAL_INTR_STAT 0x515e
|
||||
#define mmACP_DSP_SW_INTR_STAT 0x515f
|
||||
#define mmACP_DSP0_INTR_CNTL 0x5160
|
||||
#define mmACP_DSP0_INTR_STAT 0x5161
|
||||
#define mmACP_DSP0_TIMEOUT_CNTL 0x5162
|
||||
#define mmACP_DSP1_INTR_CNTL 0x5163
|
||||
#define mmACP_DSP1_INTR_STAT 0x5164
|
||||
#define mmACP_DSP1_TIMEOUT_CNTL 0x5165
|
||||
#define mmACP_DSP2_INTR_CNTL 0x5166
|
||||
#define mmACP_DSP2_INTR_STAT 0x5167
|
||||
#define mmACP_DSP2_TIMEOUT_CNTL 0x5168
|
||||
#define mmACP_DSP0_EXT_TIMER_CNTL 0x5169
|
||||
#define mmACP_DSP1_EXT_TIMER_CNTL 0x516a
|
||||
#define mmACP_DSP2_EXT_TIMER_CNTL 0x516b
|
||||
#define mmACP_AXI2DAGB_SEM_0 0x516c
|
||||
#define mmACP_AXI2DAGB_SEM_1 0x516d
|
||||
#define mmACP_AXI2DAGB_SEM_2 0x516e
|
||||
#define mmACP_AXI2DAGB_SEM_3 0x516f
|
||||
#define mmACP_AXI2DAGB_SEM_4 0x5170
|
||||
#define mmACP_AXI2DAGB_SEM_5 0x5171
|
||||
#define mmACP_AXI2DAGB_SEM_6 0x5172
|
||||
#define mmACP_AXI2DAGB_SEM_7 0x5173
|
||||
#define mmACP_AXI2DAGB_SEM_8 0x5174
|
||||
#define mmACP_AXI2DAGB_SEM_9 0x5175
|
||||
#define mmACP_AXI2DAGB_SEM_10 0x5176
|
||||
#define mmACP_AXI2DAGB_SEM_11 0x5177
|
||||
#define mmACP_AXI2DAGB_SEM_12 0x5178
|
||||
#define mmACP_AXI2DAGB_SEM_13 0x5179
|
||||
#define mmACP_AXI2DAGB_SEM_14 0x517a
|
||||
#define mmACP_AXI2DAGB_SEM_15 0x517b
|
||||
#define mmACP_AXI2DAGB_SEM_16 0x517c
|
||||
#define mmACP_AXI2DAGB_SEM_17 0x517d
|
||||
#define mmACP_AXI2DAGB_SEM_18 0x517e
|
||||
#define mmACP_AXI2DAGB_SEM_19 0x517f
|
||||
#define mmACP_AXI2DAGB_SEM_20 0x5180
|
||||
#define mmACP_AXI2DAGB_SEM_21 0x5181
|
||||
#define mmACP_AXI2DAGB_SEM_22 0x5182
|
||||
#define mmACP_AXI2DAGB_SEM_23 0x5183
|
||||
#define mmACP_AXI2DAGB_SEM_24 0x5184
|
||||
#define mmACP_AXI2DAGB_SEM_25 0x5185
|
||||
#define mmACP_AXI2DAGB_SEM_26 0x5186
|
||||
#define mmACP_AXI2DAGB_SEM_27 0x5187
|
||||
#define mmACP_AXI2DAGB_SEM_28 0x5188
|
||||
#define mmACP_AXI2DAGB_SEM_29 0x5189
|
||||
#define mmACP_AXI2DAGB_SEM_30 0x518a
|
||||
#define mmACP_AXI2DAGB_SEM_31 0x518b
|
||||
#define mmACP_AXI2DAGB_SEM_32 0x518c
|
||||
#define mmACP_AXI2DAGB_SEM_33 0x518d
|
||||
#define mmACP_AXI2DAGB_SEM_34 0x518e
|
||||
#define mmACP_AXI2DAGB_SEM_35 0x518f
|
||||
#define mmACP_AXI2DAGB_SEM_36 0x5190
|
||||
#define mmACP_AXI2DAGB_SEM_37 0x5191
|
||||
#define mmACP_AXI2DAGB_SEM_38 0x5192
|
||||
#define mmACP_AXI2DAGB_SEM_39 0x5193
|
||||
#define mmACP_AXI2DAGB_SEM_40 0x5194
|
||||
#define mmACP_AXI2DAGB_SEM_41 0x5195
|
||||
#define mmACP_AXI2DAGB_SEM_42 0x5196
|
||||
#define mmACP_AXI2DAGB_SEM_43 0x5197
|
||||
#define mmACP_AXI2DAGB_SEM_44 0x5198
|
||||
#define mmACP_AXI2DAGB_SEM_45 0x5199
|
||||
#define mmACP_AXI2DAGB_SEM_46 0x519a
|
||||
#define mmACP_AXI2DAGB_SEM_47 0x519b
|
||||
#define mmACP_SRBM_Client_Base_Addr 0x519c
|
||||
#define mmACP_SRBM_Client_RDDATA 0x519d
|
||||
#define mmACP_SRBM_Cycle_Sts 0x519e
|
||||
#define mmACP_SRBM_Targ_Idx_Addr 0x519f
|
||||
#define mmACP_SRBM_Targ_Idx_Data 0x51a0
|
||||
#define mmACP_SEMA_ADDR_LOW 0x51a1
|
||||
#define mmACP_SEMA_ADDR_HIGH 0x51a2
|
||||
#define mmACP_SEMA_CMD 0x51a3
|
||||
#define mmACP_SEMA_STS 0x51a4
|
||||
#define mmACP_SEMA_REQ 0x51a5
|
||||
#define mmACP_FW_STATUS 0x51a6
|
||||
#define mmACP_FUTURE_REG_ACLK_0 0x51a7
|
||||
#define mmACP_FUTURE_REG_ACLK_1 0x51a8
|
||||
#define mmACP_FUTURE_REG_ACLK_2 0x51a9
|
||||
#define mmACP_FUTURE_REG_ACLK_3 0x51aa
|
||||
#define mmACP_FUTURE_REG_ACLK_4 0x51ab
|
||||
#define mmACP_TIMER 0x51ac
|
||||
#define mmACP_TIMER_CNTL 0x51ad
|
||||
#define mmACP_DSP0_TIMER 0x51ae
|
||||
#define mmACP_DSP1_TIMER 0x51af
|
||||
#define mmACP_DSP2_TIMER 0x51b0
|
||||
#define mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH 0x51b1
|
||||
#define mmACP_I2S_TRANSMIT_BYTE_CNT_LOW 0x51b2
|
||||
#define mmACP_I2S_BT_TRANSMIT_BYTE_CNT_HIGH 0x51b3
|
||||
#define mmACP_I2S_BT_TRANSMIT_BYTE_CNT_LOW 0x51b4
|
||||
#define mmACP_I2S_BT_RECEIVE_BYTE_CNT_HIGH 0x51b5
|
||||
#define mmACP_I2S_BT_RECEIVE_BYTE_CNT_LOW 0x51b6
|
||||
#define mmACP_DSP0_CS_STATE 0x51b7
|
||||
#define mmACP_DSP1_CS_STATE 0x51b8
|
||||
#define mmACP_DSP2_CS_STATE 0x51b9
|
||||
#define mmACP_SCRATCH_REG_BASE_ADDR 0x51ba
|
||||
#define mmCC_ACP_EFUSE 0x51c8
|
||||
#define mmACP_PGFSM_RETAIN_REG 0x51c9
|
||||
#define mmACP_PGFSM_CONFIG_REG 0x51ca
|
||||
#define mmACP_PGFSM_WRITE_REG 0x51cb
|
||||
#define mmACP_PGFSM_READ_REG_0 0x51cc
|
||||
#define mmACP_PGFSM_READ_REG_1 0x51cd
|
||||
#define mmACP_PGFSM_READ_REG_2 0x51ce
|
||||
#define mmACP_PGFSM_READ_REG_3 0x51cf
|
||||
#define mmACP_PGFSM_READ_REG_4 0x51d0
|
||||
#define mmACP_PGFSM_READ_REG_5 0x51d1
|
||||
#define mmACP_IP_PGFSM_ENABLE 0x51d2
|
||||
#define mmACP_I2S_PIN_CONFIG 0x51d3
|
||||
#define mmACP_AZALIA_I2S_SELECT 0x51d4
|
||||
#define mmACP_CHIP_PKG_FOR_PAD_ISOLATION 0x51d5
|
||||
#define mmACP_AUDIO_PAD_PULLUP_PULLDOWN_CTRL 0x51d6
|
||||
#define mmACP_BT_UART_PAD_SEL 0x51d7
|
||||
#define mmACP_SCRATCH_REG_0 0x52c0
|
||||
#define mmACP_SCRATCH_REG_1 0x52c1
|
||||
#define mmACP_SCRATCH_REG_2 0x52c2
|
||||
#define mmACP_SCRATCH_REG_3 0x52c3
|
||||
#define mmACP_SCRATCH_REG_4 0x52c4
|
||||
#define mmACP_SCRATCH_REG_5 0x52c5
|
||||
#define mmACP_SCRATCH_REG_6 0x52c6
|
||||
#define mmACP_SCRATCH_REG_7 0x52c7
|
||||
#define mmACP_SCRATCH_REG_8 0x52c8
|
||||
#define mmACP_SCRATCH_REG_9 0x52c9
|
||||
#define mmACP_SCRATCH_REG_10 0x52ca
|
||||
#define mmACP_SCRATCH_REG_11 0x52cb
|
||||
#define mmACP_SCRATCH_REG_12 0x52cc
|
||||
#define mmACP_SCRATCH_REG_13 0x52cd
|
||||
#define mmACP_SCRATCH_REG_14 0x52ce
|
||||
#define mmACP_SCRATCH_REG_15 0x52cf
|
||||
#define mmACP_SCRATCH_REG_16 0x52d0
|
||||
#define mmACP_SCRATCH_REG_17 0x52d1
|
||||
#define mmACP_SCRATCH_REG_18 0x52d2
|
||||
#define mmACP_SCRATCH_REG_19 0x52d3
|
||||
#define mmACP_SCRATCH_REG_20 0x52d4
|
||||
#define mmACP_SCRATCH_REG_21 0x52d5
|
||||
#define mmACP_SCRATCH_REG_22 0x52d6
|
||||
#define mmACP_SCRATCH_REG_23 0x52d7
|
||||
#define mmACP_SCRATCH_REG_24 0x52d8
|
||||
#define mmACP_SCRATCH_REG_25 0x52d9
|
||||
#define mmACP_SCRATCH_REG_26 0x52da
|
||||
#define mmACP_SCRATCH_REG_27 0x52db
|
||||
#define mmACP_SCRATCH_REG_28 0x52dc
|
||||
#define mmACP_SCRATCH_REG_29 0x52dd
|
||||
#define mmACP_SCRATCH_REG_30 0x52de
|
||||
#define mmACP_SCRATCH_REG_31 0x52df
|
||||
#define mmACP_SCRATCH_REG_32 0x52e0
|
||||
#define mmACP_SCRATCH_REG_33 0x52e1
|
||||
#define mmACP_SCRATCH_REG_34 0x52e2
|
||||
#define mmACP_SCRATCH_REG_35 0x52e3
|
||||
#define mmACP_SCRATCH_REG_36 0x52e4
|
||||
#define mmACP_SCRATCH_REG_37 0x52e5
|
||||
#define mmACP_SCRATCH_REG_38 0x52e6
|
||||
#define mmACP_SCRATCH_REG_39 0x52e7
|
||||
#define mmACP_SCRATCH_REG_40 0x52e8
|
||||
#define mmACP_SCRATCH_REG_41 0x52e9
|
||||
#define mmACP_SCRATCH_REG_42 0x52ea
|
||||
#define mmACP_SCRATCH_REG_43 0x52eb
|
||||
#define mmACP_SCRATCH_REG_44 0x52ec
|
||||
#define mmACP_SCRATCH_REG_45 0x52ed
|
||||
#define mmACP_SCRATCH_REG_46 0x52ee
|
||||
#define mmACP_SCRATCH_REG_47 0x52ef
|
||||
#define mmACP_VOICE_WAKEUP_ENABLE 0x51e8
|
||||
#define mmACP_VOICE_WAKEUP_STATUS 0x51e9
|
||||
#define mmI2S_VOICE_WAKEUP_LOWER_THRESHOLD 0x51ea
|
||||
#define mmI2S_VOICE_WAKEUP_HIGHER_THRESHOLD 0x51eb
|
||||
#define mmI2S_VOICE_WAKEUP_NO_OF_SAMPLES 0x51ec
|
||||
#define mmI2S_VOICE_WAKEUP_NO_OF_PEAKS 0x51ed
|
||||
#define mmI2S_VOICE_WAKEUP_DURATION_OF_N_PEAKS 0x51ee
|
||||
#define mmI2S_VOICE_WAKEUP_BITCLK_TOGGLE_DETECTION 0x51ef
|
||||
#define mmI2S_VOICE_WAKEUP_DATA_PATH_SWITCH 0x51f0
|
||||
#define mmI2S_VOICE_WAKEUP_DATA_POINTER 0x51f1
|
||||
#define mmI2S_VOICE_WAKEUP_AUTH_MATCH 0x51f2
|
||||
#define mmI2S_VOICE_WAKEUP_8KB_WRAP 0x51f3
|
||||
#define mmACP_I2S_RECEIVED_BYTE_CNT_HIGH 0x51f4
|
||||
#define mmACP_I2S_RECEIVED_BYTE_CNT_LOW 0x51f5
|
||||
#define mmACP_I2S_MICSP_TRANSMIT_BYTE_CNT_HIGH 0x51f6
|
||||
#define mmACP_I2S_MICSP_TRANSMIT_BYTE_CNT_LOW 0x51f7
|
||||
#define mmACP_MEM_SHUT_DOWN_REQ_LO 0x51f8
|
||||
#define mmACP_MEM_SHUT_DOWN_REQ_HI 0x51f9
|
||||
#define mmACP_MEM_SHUT_DOWN_STS_LO 0x51fa
|
||||
#define mmACP_MEM_SHUT_DOWN_STS_HI 0x51fb
|
||||
#define mmACP_MEM_DEEP_SLEEP_REQ_LO 0x51fc
|
||||
#define mmACP_MEM_DEEP_SLEEP_REQ_HI 0x51fd
|
||||
#define mmACP_MEM_DEEP_SLEEP_STS_LO 0x51fe
|
||||
#define mmACP_MEM_DEEP_SLEEP_STS_HI 0x51ff
|
||||
#define mmACP_MEM_WAKEUP_FROM_SHUT_DOWN_LO 0x5200
|
||||
#define mmACP_MEM_WAKEUP_FROM_SHUT_DOWN_HI 0x5201
|
||||
#define mmACP_MEM_WAKEUP_FROM_SLEEP_LO 0x5202
|
||||
#define mmACP_MEM_WAKEUP_FROM_SLEEP_HI 0x5203
|
||||
#define mmACP_I2SSP_IER 0x5210
|
||||
#define mmACP_I2SSP_IRER 0x5211
|
||||
#define mmACP_I2SSP_ITER 0x5212
|
||||
#define mmACP_I2SSP_CER 0x5213
|
||||
#define mmACP_I2SSP_CCR 0x5214
|
||||
#define mmACP_I2SSP_RXFFR 0x5215
|
||||
#define mmACP_I2SSP_TXFFR 0x5216
|
||||
#define mmACP_I2SSP_LRBR0 0x5218
|
||||
#define mmACP_I2SSP_RRBR0 0x5219
|
||||
#define mmACP_I2SSP_RER0 0x521a
|
||||
#define mmACP_I2SSP_TER0 0x521b
|
||||
#define mmACP_I2SSP_RCR0 0x521c
|
||||
#define mmACP_I2SSP_TCR0 0x521d
|
||||
#define mmACP_I2SSP_ISR0 0x521e
|
||||
#define mmACP_I2SSP_IMR0 0x521f
|
||||
#define mmACP_I2SSP_ROR0 0x5220
|
||||
#define mmACP_I2SSP_TOR0 0x5221
|
||||
#define mmACP_I2SSP_RFCR0 0x5222
|
||||
#define mmACP_I2SSP_TFCR0 0x5223
|
||||
#define mmACP_I2SSP_RFF0 0x5224
|
||||
#define mmACP_I2SSP_TFF0 0x5225
|
||||
#define mmACP_I2SSP_RXDMA 0x5226
|
||||
#define mmACP_I2SSP_RRXDMA 0x5227
|
||||
#define mmACP_I2SSP_TXDMA 0x5228
|
||||
#define mmACP_I2SSP_RTXDMA 0x5229
|
||||
#define mmACP_I2SSP_COMP_PARAM_2 0x522a
|
||||
#define mmACP_I2SSP_COMP_PARAM_1 0x522b
|
||||
#define mmACP_I2SSP_COMP_VERSION 0x522c
|
||||
#define mmACP_I2SSP_COMP_TYPE 0x522d
|
||||
#define mmACP_I2SMICSP_IER 0x522e
|
||||
#define mmACP_I2SMICSP_IRER 0x522f
|
||||
#define mmACP_I2SMICSP_ITER 0x5230
|
||||
#define mmACP_I2SMICSP_CER 0x5231
|
||||
#define mmACP_I2SMICSP_CCR 0x5232
|
||||
#define mmACP_I2SMICSP_RXFFR 0x5233
|
||||
#define mmACP_I2SMICSP_TXFFR 0x5234
|
||||
#define mmACP_I2SMICSP_LRBR0 0x5236
|
||||
#define mmACP_I2SMICSP_RRBR0 0x5237
|
||||
#define mmACP_I2SMICSP_RER0 0x5238
|
||||
#define mmACP_I2SMICSP_TER0 0x5239
|
||||
#define mmACP_I2SMICSP_RCR0 0x523a
|
||||
#define mmACP_I2SMICSP_TCR0 0x523b
|
||||
#define mmACP_I2SMICSP_ISR0 0x523c
|
||||
#define mmACP_I2SMICSP_IMR0 0x523d
|
||||
#define mmACP_I2SMICSP_ROR0 0x523e
|
||||
#define mmACP_I2SMICSP_TOR0 0x523f
|
||||
#define mmACP_I2SMICSP_RFCR0 0x5240
|
||||
#define mmACP_I2SMICSP_TFCR0 0x5241
|
||||
#define mmACP_I2SMICSP_RFF0 0x5242
|
||||
#define mmACP_I2SMICSP_TFF0 0x5243
|
||||
#define mmACP_I2SMICSP_LRBR1 0x5246
|
||||
#define mmACP_I2SMICSP_RRBR1 0x5247
|
||||
#define mmACP_I2SMICSP_RER1 0x5248
|
||||
#define mmACP_I2SMICSP_TER1 0x5249
|
||||
#define mmACP_I2SMICSP_RCR1 0x524a
|
||||
#define mmACP_I2SMICSP_TCR1 0x524b
|
||||
#define mmACP_I2SMICSP_ISR1 0x524c
|
||||
#define mmACP_I2SMICSP_IMR1 0x524d
|
||||
#define mmACP_I2SMICSP_ROR1 0x524e
|
||||
#define mmACP_I2SMICSP_TOR1 0x524f
|
||||
#define mmACP_I2SMICSP_RFCR1 0x5250
|
||||
#define mmACP_I2SMICSP_TFCR1 0x5251
|
||||
#define mmACP_I2SMICSP_RFF1 0x5252
|
||||
#define mmACP_I2SMICSP_TFF1 0x5253
|
||||
#define mmACP_I2SMICSP_RXDMA 0x5254
|
||||
#define mmACP_I2SMICSP_RRXDMA 0x5255
|
||||
#define mmACP_I2SMICSP_TXDMA 0x5256
|
||||
#define mmACP_I2SMICSP_RTXDMA 0x5257
|
||||
#define mmACP_I2SMICSP_COMP_PARAM_2 0x5258
|
||||
#define mmACP_I2SMICSP_COMP_PARAM_1 0x5259
|
||||
#define mmACP_I2SMICSP_COMP_VERSION 0x525a
|
||||
#define mmACP_I2SMICSP_COMP_TYPE 0x525b
|
||||
#define mmACP_I2SBT_IER 0x525c
|
||||
#define mmACP_I2SBT_IRER 0x525d
|
||||
#define mmACP_I2SBT_ITER 0x525e
|
||||
#define mmACP_I2SBT_CER 0x525f
|
||||
#define mmACP_I2SBT_CCR 0x5260
|
||||
#define mmACP_I2SBT_RXFFR 0x5261
|
||||
#define mmACP_I2SBT_TXFFR 0x5262
|
||||
#define mmACP_I2SBT_LRBR0 0x5264
|
||||
#define mmACP_I2SBT_RRBR0 0x5265
|
||||
#define mmACP_I2SBT_RER0 0x5266
|
||||
#define mmACP_I2SBT_TER0 0x5267
|
||||
#define mmACP_I2SBT_RCR0 0x5268
|
||||
#define mmACP_I2SBT_TCR0 0x5269
|
||||
#define mmACP_I2SBT_ISR0 0x526a
|
||||
#define mmACP_I2SBT_IMR0 0x526b
|
||||
#define mmACP_I2SBT_ROR0 0x526c
|
||||
#define mmACP_I2SBT_TOR0 0x526d
|
||||
#define mmACP_I2SBT_RFCR0 0x526e
|
||||
#define mmACP_I2SBT_TFCR0 0x526f
|
||||
#define mmACP_I2SBT_RFF0 0x5270
|
||||
#define mmACP_I2SBT_TFF0 0x5271
|
||||
#define mmACP_I2SBT_LRBR1 0x5274
|
||||
#define mmACP_I2SBT_RRBR1 0x5275
|
||||
#define mmACP_I2SBT_RER1 0x5276
|
||||
#define mmACP_I2SBT_TER1 0x5277
|
||||
#define mmACP_I2SBT_RCR1 0x5278
|
||||
#define mmACP_I2SBT_TCR1 0x5279
|
||||
#define mmACP_I2SBT_ISR1 0x527a
|
||||
#define mmACP_I2SBT_IMR1 0x527b
|
||||
#define mmACP_I2SBT_ROR1 0x527c
|
||||
#define mmACP_I2SBT_TOR1 0x527d
|
||||
#define mmACP_I2SBT_RFCR1 0x527e
|
||||
#define mmACP_I2SBT_TFCR1 0x527f
|
||||
#define mmACP_I2SBT_RFF1 0x5280
|
||||
#define mmACP_I2SBT_TFF1 0x5281
|
||||
#define mmACP_I2SBT_RXDMA 0x5282
|
||||
#define mmACP_I2SBT_RRXDMA 0x5283
|
||||
#define mmACP_I2SBT_TXDMA 0x5284
|
||||
#define mmACP_I2SBT_RTXDMA 0x5285
|
||||
#define mmACP_I2SBT_COMP_PARAM_2 0x5286
|
||||
#define mmACP_I2SBT_COMP_PARAM_1 0x5287
|
||||
#define mmACP_I2SBT_COMP_VERSION 0x5288
|
||||
#define mmACP_I2SBT_COMP_TYPE 0x5289
|
||||
|
||||
#endif /* ACP_2_2_D_H */
|
1068
sound/soc/amd/include/acp_2_2_enum.h
Normal file
1068
sound/soc/amd/include/acp_2_2_enum.h
Normal file
File diff suppressed because it is too large
Load diff
2292
sound/soc/amd/include/acp_2_2_sh_mask.h
Normal file
2292
sound/soc/amd/include/acp_2_2_sh_mask.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -68,4 +68,13 @@ config SND_ATMEL_SOC_CLASSD
|
|||
help
|
||||
Say Y if you want to add support for Atmel ASoC driver for boards using
|
||||
CLASSD.
|
||||
|
||||
config SND_ATMEL_SOC_PDMIC
|
||||
tristate "Atmel ASoC driver for boards using PDMIC"
|
||||
depends on OF && (ARCH_AT91 || COMPILE_TEST)
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Say Y if you want to add support for Atmel ASoC driver for boards using
|
||||
PDMIC.
|
||||
endif
|
||||
|
|
|
@ -12,8 +12,10 @@ snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
|
|||
snd-atmel-soc-wm8904-objs := atmel_wm8904.o
|
||||
snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
|
||||
snd-atmel-soc-classd-objs := atmel-classd.o
|
||||
snd-atmel-soc-pdmic-objs := atmel-pdmic.o
|
||||
|
||||
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
|
||||
obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o
|
||||
|
|
|
@ -106,7 +106,7 @@ static const struct snd_pcm_hardware atmel_classd_hw = {
|
|||
.rates = ATMEL_CLASSD_RATES,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
.channels_min = 2,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 64 * 1024,
|
||||
.period_bytes_min = 256,
|
||||
|
@ -145,7 +145,7 @@ static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
|
|||
|
||||
static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ATMEL_CLASSD_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
|
@ -171,9 +171,13 @@ atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (params_channels(params) == 1)
|
||||
slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
else
|
||||
slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
||||
slave_config->direction = DMA_MEM_TO_DEV;
|
||||
slave_config->dst_addr = dd->phy_base + CLASSD_THR;
|
||||
slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
slave_config->dst_maxburst = 1;
|
||||
slave_config->src_maxburst = 1;
|
||||
slave_config->device_fc = false;
|
||||
|
@ -486,7 +490,7 @@ static struct snd_soc_dai_driver atmel_classd_codec_dai = {
|
|||
.name = ATMEL_CLASSD_CODEC_DAI_NAME,
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ATMEL_CLASSD_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
|
@ -636,8 +640,10 @@ static int atmel_classd_probe(struct platform_device *pdev)
|
|||
|
||||
/* register sound card */
|
||||
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
|
||||
if (!card)
|
||||
return -ENOMEM;
|
||||
if (!card) {
|
||||
ret = -ENOMEM;
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
snd_soc_card_set_drvdata(card, dd);
|
||||
platform_set_drvdata(pdev, card);
|
||||
|
@ -645,16 +651,20 @@ static int atmel_classd_probe(struct platform_device *pdev)
|
|||
ret = atmel_classd_asoc_card_init(dev, card);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to init sound card\n");
|
||||
return ret;
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_card(dev, card);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register sound card: %d\n", ret);
|
||||
return ret;
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_codec:
|
||||
snd_soc_unregister_codec(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_classd_remove(struct platform_device *pdev)
|
||||
|
|
738
sound/soc/atmel/atmel-pdmic.c
Normal file
738
sound/soc/atmel/atmel-pdmic.c
Normal file
|
@ -0,0 +1,738 @@
|
|||
/* Atmel PDMIC driver
|
||||
*
|
||||
* Copyright (C) 2015 Atmel
|
||||
*
|
||||
* Author: Songjun Wu <songjun.wu@atmel.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 or later
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "atmel-pdmic.h"
|
||||
|
||||
struct atmel_pdmic_pdata {
|
||||
u32 mic_min_freq;
|
||||
u32 mic_max_freq;
|
||||
s32 mic_offset;
|
||||
const char *card_name;
|
||||
};
|
||||
|
||||
struct atmel_pdmic {
|
||||
dma_addr_t phy_base;
|
||||
struct regmap *regmap;
|
||||
struct clk *pclk;
|
||||
struct clk *gclk;
|
||||
int irq;
|
||||
struct snd_pcm_substream *substream;
|
||||
const struct atmel_pdmic_pdata *pdata;
|
||||
};
|
||||
|
||||
static const struct of_device_id atmel_pdmic_of_match[] = {
|
||||
{
|
||||
.compatible = "atmel,sama5d2-pdmic",
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atmel_pdmic_of_match);
|
||||
|
||||
#define PDMIC_OFFSET_MAX_VAL S16_MAX
|
||||
#define PDMIC_OFFSET_MIN_VAL S16_MIN
|
||||
|
||||
static struct atmel_pdmic_pdata *atmel_pdmic_dt_init(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct atmel_pdmic_pdata *pdata;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "device node not found\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (of_property_read_string(np, "atmel,model", &pdata->card_name))
|
||||
pdata->card_name = "PDMIC";
|
||||
|
||||
if (of_property_read_u32(np, "atmel,mic-min-freq",
|
||||
&pdata->mic_min_freq)) {
|
||||
dev_err(dev, "failed to get mic-min-freq\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "atmel,mic-max-freq",
|
||||
&pdata->mic_max_freq)) {
|
||||
dev_err(dev, "failed to get mic-max-freq\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (pdata->mic_max_freq < pdata->mic_min_freq) {
|
||||
dev_err(dev,
|
||||
"mic-max-freq should not less than mic-min-freq\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (of_property_read_s32(np, "atmel,mic-offset", &pdata->mic_offset))
|
||||
pdata->mic_offset = 0;
|
||||
|
||||
if (pdata->mic_offset > PDMIC_OFFSET_MAX_VAL) {
|
||||
dev_warn(dev,
|
||||
"mic-offset value %d is larger than the max value %d, the max value is specified\n",
|
||||
pdata->mic_offset, PDMIC_OFFSET_MAX_VAL);
|
||||
pdata->mic_offset = PDMIC_OFFSET_MAX_VAL;
|
||||
} else if (pdata->mic_offset < PDMIC_OFFSET_MIN_VAL) {
|
||||
dev_warn(dev,
|
||||
"mic-offset value %d is less than the min value %d, the min value is specified\n",
|
||||
pdata->mic_offset, PDMIC_OFFSET_MIN_VAL);
|
||||
pdata->mic_offset = PDMIC_OFFSET_MIN_VAL;
|
||||
}
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
/* cpu dai component */
|
||||
static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(dd->gclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(dd->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Clear all bits in the Control Register(PDMIC_CR) */
|
||||
regmap_write(dd->regmap, PDMIC_CR, 0);
|
||||
|
||||
dd->substream = substream;
|
||||
|
||||
/* Enable the overrun error interrupt */
|
||||
regmap_write(dd->regmap, PDMIC_IER, PDMIC_IER_OVRE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_pdmic_cpu_dai_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
|
||||
|
||||
/* Disable the overrun error interrupt */
|
||||
regmap_write(dd->regmap, PDMIC_IDR, PDMIC_IDR_OVRE);
|
||||
|
||||
clk_disable_unprepare(dd->gclk);
|
||||
clk_disable_unprepare(dd->pclk);
|
||||
}
|
||||
|
||||
static int atmel_pdmic_cpu_dai_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
|
||||
u32 val;
|
||||
|
||||
/* Clean the PDMIC Converted Data Register */
|
||||
return regmap_read(dd->regmap, PDMIC_CDR, &val);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops atmel_pdmic_cpu_dai_ops = {
|
||||
.startup = atmel_pdmic_cpu_dai_startup,
|
||||
.shutdown = atmel_pdmic_cpu_dai_shutdown,
|
||||
.prepare = atmel_pdmic_cpu_dai_prepare,
|
||||
};
|
||||
|
||||
#define ATMEL_PDMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver atmel_pdmic_cpu_dai = {
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = ATMEL_PDMIC_FORMATS,},
|
||||
.ops = &atmel_pdmic_cpu_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = {
|
||||
.name = "atmel-pdmic",
|
||||
};
|
||||
|
||||
/* platform */
|
||||
#define ATMEL_PDMIC_MAX_BUF_SIZE (64 * 1024)
|
||||
#define ATMEL_PDMIC_PREALLOC_BUF_SIZE ATMEL_PDMIC_MAX_BUF_SIZE
|
||||
|
||||
static const struct snd_pcm_hardware atmel_pdmic_hw = {
|
||||
.info = SNDRV_PCM_INFO_MMAP
|
||||
| SNDRV_PCM_INFO_MMAP_VALID
|
||||
| SNDRV_PCM_INFO_INTERLEAVED
|
||||
| SNDRV_PCM_INFO_RESUME
|
||||
| SNDRV_PCM_INFO_PAUSE,
|
||||
.formats = ATMEL_PDMIC_FORMATS,
|
||||
.buffer_bytes_max = ATMEL_PDMIC_MAX_BUF_SIZE,
|
||||
.period_bytes_min = 256,
|
||||
.period_bytes_max = 32 * 1024,
|
||||
.periods_min = 2,
|
||||
.periods_max = 256,
|
||||
};
|
||||
|
||||
static int
|
||||
atmel_pdmic_platform_configure_dma(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct dma_slave_config *slave_config)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
|
||||
int ret;
|
||||
|
||||
ret = snd_hwparams_to_dma_slave_config(substream, params,
|
||||
slave_config);
|
||||
if (ret) {
|
||||
dev_err(rtd->platform->dev,
|
||||
"hw params to dma slave configure failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
slave_config->src_addr = dd->phy_base + PDMIC_CDR;
|
||||
slave_config->src_maxburst = 1;
|
||||
slave_config->dst_maxburst = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_dmaengine_pcm_config
|
||||
atmel_pdmic_dmaengine_pcm_config = {
|
||||
.prepare_slave_config = atmel_pdmic_platform_configure_dma,
|
||||
.pcm_hardware = &atmel_pdmic_hw,
|
||||
.prealloc_buffer_size = ATMEL_PDMIC_PREALLOC_BUF_SIZE,
|
||||
};
|
||||
|
||||
/* codec */
|
||||
/* Mic Gain = dgain * 2^(-scale) */
|
||||
struct mic_gain {
|
||||
unsigned int dgain;
|
||||
unsigned int scale;
|
||||
};
|
||||
|
||||
/* range from -90 dB to 90 dB */
|
||||
static const struct mic_gain mic_gain_table[] = {
|
||||
{ 1, 15}, { 1, 14}, /* -90, -84 dB */
|
||||
{ 3, 15}, { 1, 13}, { 3, 14}, { 1, 12}, /* -81, -78, -75, -72 dB */
|
||||
{ 5, 14}, { 13, 15}, /* -70, -68 dB */
|
||||
{ 9, 14}, { 21, 15}, { 23, 15}, { 13, 14}, /* -65 ~ -62 dB */
|
||||
{ 29, 15}, { 33, 15}, { 37, 15}, { 41, 15}, /* -61 ~ -58 dB */
|
||||
{ 23, 14}, { 13, 13}, { 58, 15}, { 65, 15}, /* -57 ~ -54 dB */
|
||||
{ 73, 15}, { 41, 14}, { 23, 13}, { 13, 12}, /* -53 ~ -50 dB */
|
||||
{ 29, 13}, { 65, 14}, { 73, 14}, { 41, 13}, /* -49 ~ -46 dB */
|
||||
{ 23, 12}, { 207, 15}, { 29, 12}, { 65, 13}, /* -45 ~ -42 dB */
|
||||
{ 73, 13}, { 41, 12}, { 23, 11}, { 413, 15}, /* -41 ~ -38 dB */
|
||||
{ 463, 15}, { 519, 15}, { 583, 15}, { 327, 14}, /* -37 ~ -34 dB */
|
||||
{ 367, 14}, { 823, 15}, { 231, 13}, { 1036, 15}, /* -33 ~ -30 dB */
|
||||
{ 1163, 15}, { 1305, 15}, { 183, 12}, { 1642, 15}, /* -29 ~ -26 dB */
|
||||
{ 1843, 15}, { 2068, 15}, { 145, 11}, { 2603, 15}, /* -25 ~ -22 dB */
|
||||
{ 365, 12}, { 3277, 15}, { 3677, 15}, { 4125, 15}, /* -21 ~ -18 dB */
|
||||
{ 4629, 15}, { 5193, 15}, { 5827, 15}, { 3269, 14}, /* -17 ~ -14 dB */
|
||||
{ 917, 12}, { 8231, 15}, { 9235, 15}, { 5181, 14}, /* -13 ~ -10 dB */
|
||||
{11627, 15}, {13045, 15}, {14637, 15}, {16423, 15}, /* -9 ~ -6 dB */
|
||||
{18427, 15}, {20675, 15}, { 5799, 13}, {26029, 15}, /* -5 ~ -2 dB */
|
||||
{ 7301, 13}, { 1, 0}, {18383, 14}, {10313, 13}, /* -1 ~ 2 dB */
|
||||
{23143, 14}, {25967, 14}, {29135, 14}, {16345, 13}, /* 3 ~ 6 dB */
|
||||
{ 4585, 11}, {20577, 13}, { 1443, 9}, {25905, 13}, /* 7 ~ 10 dB */
|
||||
{14533, 12}, { 8153, 11}, { 2287, 9}, {20529, 12}, /* 11 ~ 14 dB */
|
||||
{11517, 11}, { 6461, 10}, {28997, 12}, { 4067, 9}, /* 15 ~ 18 dB */
|
||||
{18253, 11}, { 10, 0}, {22979, 11}, {25783, 11}, /* 19 ~ 22 dB */
|
||||
{28929, 11}, {32459, 11}, { 9105, 9}, {20431, 10}, /* 23 ~ 26 dB */
|
||||
{22925, 10}, {12861, 9}, { 7215, 8}, {16191, 9}, /* 27 ~ 30 dB */
|
||||
{ 9083, 8}, {20383, 9}, {11435, 8}, { 6145, 7}, /* 31 ~ 34 dB */
|
||||
{ 3599, 6}, {32305, 9}, {18123, 8}, {20335, 8}, /* 35 ~ 38 dB */
|
||||
{ 713, 3}, { 100, 0}, { 7181, 6}, { 8057, 6}, /* 39 ~ 42 dB */
|
||||
{ 565, 2}, {20287, 7}, {11381, 6}, {25539, 7}, /* 43 ~ 46 dB */
|
||||
{ 1791, 3}, { 4019, 4}, { 9019, 5}, {20239, 6}, /* 47 ~ 50 dB */
|
||||
{ 5677, 4}, {25479, 6}, { 7147, 4}, { 8019, 4}, /* 51 ~ 54 dB */
|
||||
{17995, 5}, {20191, 5}, {11327, 4}, {12709, 4}, /* 55 ~ 58 dB */
|
||||
{ 3565, 2}, { 1000, 0}, { 1122, 0}, { 1259, 0}, /* 59 ~ 62 dB */
|
||||
{ 2825, 1}, {12679, 3}, { 7113, 2}, { 7981, 2}, /* 63 ~ 66 dB */
|
||||
{ 8955, 2}, {20095, 3}, {22547, 3}, {12649, 2}, /* 67 ~ 70 dB */
|
||||
{28385, 3}, { 3981, 0}, {17867, 2}, {20047, 2}, /* 71 ~ 74 dB */
|
||||
{11247, 1}, {12619, 1}, {14159, 1}, {31773, 2}, /* 75 ~ 78 dB */
|
||||
{17825, 1}, {10000, 0}, {11220, 0}, {12589, 0}, /* 79 ~ 82 dB */
|
||||
{28251, 1}, {15849, 0}, {17783, 0}, {19953, 0}, /* 83 ~ 86 dB */
|
||||
{22387, 0}, {25119, 0}, {28184, 0}, {31623, 0}, /* 87 ~ 90 dB */
|
||||
};
|
||||
|
||||
static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
|
||||
0, 1, TLV_DB_SCALE_ITEM(-9000, 600, 0),
|
||||
2, 5, TLV_DB_SCALE_ITEM(-8100, 300, 0),
|
||||
6, 7, TLV_DB_SCALE_ITEM(-7000, 200, 0),
|
||||
8, ARRAY_SIZE(mic_gain_table)-1, TLV_DB_SCALE_ITEM(-6500, 100, 0),
|
||||
);
|
||||
|
||||
int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
unsigned int dgain_val, scale_val;
|
||||
int i;
|
||||
|
||||
dgain_val = (snd_soc_read(codec, PDMIC_DSPR1) & PDMIC_DSPR1_DGAIN_MASK)
|
||||
>> PDMIC_DSPR1_DGAIN_SHIFT;
|
||||
|
||||
scale_val = (snd_soc_read(codec, PDMIC_DSPR0) & PDMIC_DSPR0_SCALE_MASK)
|
||||
>> PDMIC_DSPR0_SCALE_SHIFT;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mic_gain_table); i++) {
|
||||
if ((mic_gain_table[i].dgain == dgain_val) &&
|
||||
(mic_gain_table[i].scale == scale_val))
|
||||
ucontrol->value.integer.value[0] = i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pdmic_put_mic_volsw(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
int max = mc->max;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
val = ucontrol->value.integer.value[0];
|
||||
|
||||
if (val > max)
|
||||
return -EINVAL;
|
||||
|
||||
ret = snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_MASK,
|
||||
mic_gain_table[val].dgain << PDMIC_DSPR1_DGAIN_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_update_bits(codec, PDMIC_DSPR0, PDMIC_DSPR0_SCALE_MASK,
|
||||
mic_gain_table[val].scale << PDMIC_DSPR0_SCALE_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new atmel_pdmic_snd_controls[] = {
|
||||
SOC_SINGLE_EXT_TLV("Mic Capture Volume", PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_SHIFT,
|
||||
ARRAY_SIZE(mic_gain_table)-1, 0,
|
||||
pdmic_get_mic_volsw, pdmic_put_mic_volsw, mic_gain_tlv),
|
||||
|
||||
SOC_SINGLE("High Pass Filter Switch", PDMIC_DSPR0,
|
||||
PDMIC_DSPR0_HPFBYP_SHIFT, 1, 1),
|
||||
|
||||
SOC_SINGLE("SINCC Filter Switch", PDMIC_DSPR0, PDMIC_DSPR0_SINBYP_SHIFT, 1, 1),
|
||||
};
|
||||
|
||||
static int atmel_pdmic_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
|
||||
|
||||
snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_OFFSET_MASK,
|
||||
(u32)(dd->pdata->mic_offset << PDMIC_DSPR1_OFFSET_SHIFT));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_pdmic = {
|
||||
.probe = atmel_pdmic_codec_probe,
|
||||
.controls = atmel_pdmic_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls),
|
||||
};
|
||||
|
||||
/* codec dai component */
|
||||
#define PDMIC_MR_PRESCAL_MAX_VAL 127
|
||||
|
||||
static int
|
||||
atmel_pdmic_codec_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *codec_dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
unsigned int rate_min = substream->runtime->hw.rate_min;
|
||||
unsigned int rate_max = substream->runtime->hw.rate_max;
|
||||
int fs = params_rate(params);
|
||||
int bits = params_width(params);
|
||||
unsigned long pclk_rate, gclk_rate;
|
||||
unsigned int f_pdmic;
|
||||
u32 mr_val, dspr0_val, pclk_prescal, gclk_prescal;
|
||||
|
||||
if (params_channels(params) != 1) {
|
||||
dev_err(codec->dev,
|
||||
"only supports one channel\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((fs < rate_min) || (fs > rate_max)) {
|
||||
dev_err(codec->dev,
|
||||
"sample rate is %dHz, min rate is %dHz, max rate is %dHz\n",
|
||||
fs, rate_min, rate_max);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (bits) {
|
||||
case 16:
|
||||
dspr0_val = (PDMIC_DSPR0_SIZE_16_BITS
|
||||
<< PDMIC_DSPR0_SIZE_SHIFT);
|
||||
break;
|
||||
case 32:
|
||||
dspr0_val = (PDMIC_DSPR0_SIZE_32_BITS
|
||||
<< PDMIC_DSPR0_SIZE_SHIFT);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((fs << 7) > (rate_max << 6)) {
|
||||
f_pdmic = fs << 6;
|
||||
dspr0_val |= PDMIC_DSPR0_OSR_64 << PDMIC_DSPR0_OSR_SHIFT;
|
||||
} else {
|
||||
f_pdmic = fs << 7;
|
||||
dspr0_val |= PDMIC_DSPR0_OSR_128 << PDMIC_DSPR0_OSR_SHIFT;
|
||||
}
|
||||
|
||||
pclk_rate = clk_get_rate(dd->pclk);
|
||||
gclk_rate = clk_get_rate(dd->gclk);
|
||||
|
||||
/* PRESCAL = SELCK/(2*f_pdmic) - 1*/
|
||||
pclk_prescal = (u32)(pclk_rate/(f_pdmic << 1)) - 1;
|
||||
gclk_prescal = (u32)(gclk_rate/(f_pdmic << 1)) - 1;
|
||||
|
||||
if ((pclk_prescal > PDMIC_MR_PRESCAL_MAX_VAL) ||
|
||||
(gclk_rate/((gclk_prescal + 1) << 1) <
|
||||
pclk_rate/((pclk_prescal + 1) << 1))) {
|
||||
mr_val = gclk_prescal << PDMIC_MR_PRESCAL_SHIFT;
|
||||
mr_val |= PDMIC_MR_CLKS_GCK << PDMIC_MR_CLKS_SHIFT;
|
||||
} else {
|
||||
mr_val = pclk_prescal << PDMIC_MR_PRESCAL_SHIFT;
|
||||
mr_val |= PDMIC_MR_CLKS_PCK << PDMIC_MR_CLKS_SHIFT;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, PDMIC_MR,
|
||||
PDMIC_MR_PRESCAL_MASK | PDMIC_MR_CLKS_MASK, mr_val);
|
||||
|
||||
snd_soc_update_bits(codec, PDMIC_DSPR0,
|
||||
PDMIC_DSPR0_OSR_MASK | PDMIC_DSPR0_SIZE_MASK, dspr0_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pdmic_codec_dai_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *codec_dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
|
||||
snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
|
||||
PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pdmic_codec_dai_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *codec_dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
u32 val;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
val = PDMIC_CR_ENPDM_EN << PDMIC_CR_ENPDM_SHIFT;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
val = PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops atmel_pdmic_codec_dai_ops = {
|
||||
.hw_params = atmel_pdmic_codec_dai_hw_params,
|
||||
.prepare = atmel_pdmic_codec_dai_prepare,
|
||||
.trigger = atmel_pdmic_codec_dai_trigger,
|
||||
};
|
||||
|
||||
#define ATMEL_PDMIC_CODEC_DAI_NAME "atmel-pdmic-hifi"
|
||||
|
||||
static struct snd_soc_dai_driver atmel_pdmic_codec_dai = {
|
||||
.name = ATMEL_PDMIC_CODEC_DAI_NAME,
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = ATMEL_PDMIC_FORMATS,
|
||||
},
|
||||
.ops = &atmel_pdmic_codec_dai_ops,
|
||||
};
|
||||
|
||||
/* ASoC sound card */
|
||||
static int atmel_pdmic_asoc_card_init(struct device *dev,
|
||||
struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
|
||||
|
||||
dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
|
||||
if (!dai_link)
|
||||
return -ENOMEM;
|
||||
|
||||
dai_link->name = "PDMIC";
|
||||
dai_link->stream_name = "PDMIC PCM";
|
||||
dai_link->codec_dai_name = ATMEL_PDMIC_CODEC_DAI_NAME;
|
||||
dai_link->cpu_dai_name = dev_name(dev);
|
||||
dai_link->codec_name = dev_name(dev);
|
||||
dai_link->platform_name = dev_name(dev);
|
||||
|
||||
card->dai_link = dai_link;
|
||||
card->num_links = 1;
|
||||
card->name = dd->pdata->card_name;
|
||||
card->dev = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_pdmic_get_sample_rate(struct atmel_pdmic *dd,
|
||||
unsigned int *rate_min, unsigned int *rate_max)
|
||||
{
|
||||
u32 mic_min_freq = dd->pdata->mic_min_freq;
|
||||
u32 mic_max_freq = dd->pdata->mic_max_freq;
|
||||
u32 clk_max_rate = (u32)(clk_get_rate(dd->pclk) >> 1);
|
||||
u32 clk_min_rate = (u32)(clk_get_rate(dd->gclk) >> 8);
|
||||
|
||||
if (mic_max_freq > clk_max_rate)
|
||||
mic_max_freq = clk_max_rate;
|
||||
|
||||
if (mic_min_freq < clk_min_rate)
|
||||
mic_min_freq = clk_min_rate;
|
||||
|
||||
*rate_min = DIV_ROUND_CLOSEST(mic_min_freq, 128);
|
||||
*rate_max = mic_max_freq >> 6;
|
||||
}
|
||||
|
||||
/* PDMIC interrupt handler */
|
||||
static irqreturn_t atmel_pdmic_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct atmel_pdmic *dd = (struct atmel_pdmic *)dev_id;
|
||||
u32 pdmic_isr;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
regmap_read(dd->regmap, PDMIC_ISR, &pdmic_isr);
|
||||
|
||||
if (pdmic_isr & PDMIC_ISR_OVRE) {
|
||||
regmap_update_bits(dd->regmap, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
|
||||
PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
|
||||
|
||||
snd_pcm_stop_xrun(dd->substream);
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* regmap configuration */
|
||||
#define ATMEL_PDMIC_REG_MAX 0x124
|
||||
static const struct regmap_config atmel_pdmic_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = ATMEL_PDMIC_REG_MAX,
|
||||
};
|
||||
|
||||
static int atmel_pdmic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct atmel_pdmic *dd;
|
||||
struct resource *res;
|
||||
void __iomem *io_base;
|
||||
const struct atmel_pdmic_pdata *pdata;
|
||||
struct snd_soc_card *card;
|
||||
unsigned int rate_min, rate_max;
|
||||
int ret;
|
||||
|
||||
pdata = atmel_pdmic_dt_init(dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
|
||||
dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
|
||||
if (!dd)
|
||||
return -ENOMEM;
|
||||
|
||||
dd->pdata = pdata;
|
||||
|
||||
dd->irq = platform_get_irq(pdev, 0);
|
||||
if (dd->irq < 0) {
|
||||
ret = dd->irq;
|
||||
dev_err(dev, "failed to could not get irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dd->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(dd->pclk)) {
|
||||
ret = PTR_ERR(dd->pclk);
|
||||
dev_err(dev, "failed to get peripheral clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dd->gclk = devm_clk_get(dev, "gclk");
|
||||
if (IS_ERR(dd->gclk)) {
|
||||
ret = PTR_ERR(dd->gclk);
|
||||
dev_err(dev, "failed to get GCK: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The gclk clock frequency must always be tree times
|
||||
* lower than the pclk clock frequency
|
||||
*/
|
||||
ret = clk_set_rate(dd->gclk, clk_get_rate(dd->pclk)/3);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set GCK clock rate: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "no memory resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
io_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(io_base)) {
|
||||
ret = PTR_ERR(io_base);
|
||||
dev_err(dev, "failed to remap register memory: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dd->phy_base = res->start;
|
||||
|
||||
dd->regmap = devm_regmap_init_mmio(dev, io_base,
|
||||
&atmel_pdmic_regmap_config);
|
||||
if (IS_ERR(dd->regmap)) {
|
||||
ret = PTR_ERR(dd->regmap);
|
||||
dev_err(dev, "failed to init register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, dd->irq, atmel_pdmic_interrupt, 0,
|
||||
"PDMIC", (void *)dd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
|
||||
dd->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the minimal and maximal sample rate that micphone supports */
|
||||
atmel_pdmic_get_sample_rate(dd, &rate_min, &rate_max);
|
||||
|
||||
/* register cpu dai */
|
||||
atmel_pdmic_cpu_dai.capture.rate_min = rate_min;
|
||||
atmel_pdmic_cpu_dai.capture.rate_max = rate_max;
|
||||
ret = devm_snd_soc_register_component(dev,
|
||||
&atmel_pdmic_cpu_dai_component,
|
||||
&atmel_pdmic_cpu_dai, 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not register CPU DAI: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register platform */
|
||||
ret = devm_snd_dmaengine_pcm_register(dev,
|
||||
&atmel_pdmic_dmaengine_pcm_config,
|
||||
0);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not register platform: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register codec and codec dai */
|
||||
atmel_pdmic_codec_dai.capture.rate_min = rate_min;
|
||||
atmel_pdmic_codec_dai.capture.rate_max = rate_max;
|
||||
ret = snd_soc_register_codec(dev, &soc_codec_dev_pdmic,
|
||||
&atmel_pdmic_codec_dai, 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not register codec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register sound card */
|
||||
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
|
||||
if (!card) {
|
||||
ret = -ENOMEM;
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
snd_soc_card_set_drvdata(card, dd);
|
||||
platform_set_drvdata(pdev, card);
|
||||
|
||||
ret = atmel_pdmic_asoc_card_init(dev, card);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to init sound card: %d\n", ret);
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_card(dev, card);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register sound card: %d\n", ret);
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_codec:
|
||||
snd_soc_unregister_codec(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_pdmic_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver atmel_pdmic_driver = {
|
||||
.driver = {
|
||||
.name = "atmel-pdmic",
|
||||
.of_match_table = of_match_ptr(atmel_pdmic_of_match),
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = atmel_pdmic_probe,
|
||||
.remove = atmel_pdmic_remove,
|
||||
};
|
||||
module_platform_driver(atmel_pdmic_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Atmel PDMIC driver under ALSA SoC architecture");
|
||||
MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
80
sound/soc/atmel/atmel-pdmic.h
Normal file
80
sound/soc/atmel/atmel-pdmic.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
#ifndef __ATMEL_PDMIC_H_
|
||||
#define __ATMEL_PDMIC_H_
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define PDMIC_CR 0x00000000
|
||||
|
||||
#define PDMIC_CR_SWRST 0x1
|
||||
#define PDMIC_CR_SWRST_MASK BIT(0)
|
||||
#define PDMIC_CR_SWRST_SHIFT (0)
|
||||
|
||||
#define PDMIC_CR_ENPDM_DIS 0x0
|
||||
#define PDMIC_CR_ENPDM_EN 0x1
|
||||
#define PDMIC_CR_ENPDM_MASK BIT(4)
|
||||
#define PDMIC_CR_ENPDM_SHIFT (4)
|
||||
|
||||
#define PDMIC_MR 0x00000004
|
||||
|
||||
#define PDMIC_MR_CLKS_PCK 0x0
|
||||
#define PDMIC_MR_CLKS_GCK 0x1
|
||||
#define PDMIC_MR_CLKS_MASK BIT(4)
|
||||
#define PDMIC_MR_CLKS_SHIFT (4)
|
||||
|
||||
#define PDMIC_MR_PRESCAL_MASK GENMASK(14, 8)
|
||||
#define PDMIC_MR_PRESCAL_SHIFT (8)
|
||||
|
||||
#define PDMIC_CDR 0x00000014
|
||||
|
||||
#define PDMIC_IER 0x00000018
|
||||
#define PDMIC_IER_OVRE BIT(25)
|
||||
|
||||
#define PDMIC_IDR 0x0000001c
|
||||
#define PDMIC_IDR_OVRE BIT(25)
|
||||
|
||||
#define PDMIC_IMR 0x00000020
|
||||
|
||||
#define PDMIC_ISR 0x00000024
|
||||
#define PDMIC_ISR_OVRE BIT(25)
|
||||
|
||||
#define PDMIC_DSPR0 0x00000058
|
||||
|
||||
#define PDMIC_DSPR0_HPFBYP_DIS 0x1
|
||||
#define PDMIC_DSPR0_HPFBYP_EN 0x0
|
||||
#define PDMIC_DSPR0_HPFBYP_MASK BIT(1)
|
||||
#define PDMIC_DSPR0_HPFBYP_SHIFT (1)
|
||||
|
||||
#define PDMIC_DSPR0_SINBYP_DIS 0x1
|
||||
#define PDMIC_DSPR0_SINBYP_EN 0x0
|
||||
#define PDMIC_DSPR0_SINBYP_MASK BIT(2)
|
||||
#define PDMIC_DSPR0_SINBYP_SHIFT (2)
|
||||
|
||||
#define PDMIC_DSPR0_SIZE_16_BITS 0x0
|
||||
#define PDMIC_DSPR0_SIZE_32_BITS 0x1
|
||||
#define PDMIC_DSPR0_SIZE_MASK BIT(3)
|
||||
#define PDMIC_DSPR0_SIZE_SHIFT (3)
|
||||
|
||||
#define PDMIC_DSPR0_OSR_128 0x0
|
||||
#define PDMIC_DSPR0_OSR_64 0x1
|
||||
#define PDMIC_DSPR0_OSR_MASK GENMASK(6, 4)
|
||||
#define PDMIC_DSPR0_OSR_SHIFT (4)
|
||||
|
||||
#define PDMIC_DSPR0_SCALE_MASK GENMASK(11, 8)
|
||||
#define PDMIC_DSPR0_SCALE_SHIFT (8)
|
||||
|
||||
#define PDMIC_DSPR0_SHIFT_MASK GENMASK(15, 12)
|
||||
#define PDMIC_DSPR0_SHIFT_SHIFT (12)
|
||||
|
||||
#define PDMIC_DSPR1 0x0000005c
|
||||
|
||||
#define PDMIC_DSPR1_DGAIN_MASK GENMASK(14, 0)
|
||||
#define PDMIC_DSPR1_DGAIN_SHIFT (0)
|
||||
|
||||
#define PDMIC_DSPR1_OFFSET_MASK GENMASK(31, 16)
|
||||
#define PDMIC_DSPR1_OFFSET_SHIFT (16)
|
||||
|
||||
#define PDMIC_WPMR 0x000000e4
|
||||
|
||||
#define PDMIC_WPSR 0x000000e8
|
||||
|
||||
#endif
|
|
@ -183,6 +183,7 @@ static struct platform_driver atmel_asoc_wm8904_driver = {
|
|||
.driver = {
|
||||
.name = "atmel-wm8904-audio",
|
||||
.of_match_table = of_match_ptr(atmel_asoc_wm8904_dt_ids),
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = atmel_asoc_wm8904_probe,
|
||||
.remove = atmel_asoc_wm8904_remove,
|
||||
|
|
|
@ -31,20 +31,20 @@
|
|||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
/* Clock registers */
|
||||
#define BCM2835_CLK_PCMCTL_REG 0x00
|
||||
|
|
|
@ -55,9 +55,11 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_CS4271_SPI if SPI_MASTER
|
||||
select SND_SOC_CS42XX8_I2C if I2C
|
||||
select SND_SOC_CS4349 if I2C
|
||||
select SND_SOC_CS47L24 if MFD_CS47L24
|
||||
select SND_SOC_CX20442 if TTY
|
||||
select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
|
||||
select SND_SOC_DA7213 if I2C
|
||||
select SND_SOC_DA7218 if I2C
|
||||
select SND_SOC_DA7219 if I2C
|
||||
select SND_SOC_DA732X if I2C
|
||||
select SND_SOC_DA9055 if I2C
|
||||
|
@ -66,7 +68,9 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_ES8328_SPI if SPI_MASTER
|
||||
select SND_SOC_ES8328_I2C if I2C
|
||||
select SND_SOC_GTM601
|
||||
select SND_SOC_HDAC_HDMI
|
||||
select SND_SOC_ICS43432
|
||||
select SND_SOC_INNO_RK3036
|
||||
select SND_SOC_ISABELLE if I2C
|
||||
select SND_SOC_JZ4740_CODEC
|
||||
select SND_SOC_LM4857 if I2C
|
||||
|
@ -83,16 +87,20 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_ML26124 if I2C
|
||||
select SND_SOC_NAU8825 if I2C
|
||||
select SND_SOC_PCM1681 if I2C
|
||||
select SND_SOC_PCM1792A if SPI_MASTER
|
||||
select SND_SOC_PCM179X if SPI_MASTER
|
||||
select SND_SOC_PCM3008
|
||||
select SND_SOC_PCM3168A_I2C if I2C
|
||||
select SND_SOC_PCM3168A_SPI if SPI_MASTER
|
||||
select SND_SOC_PCM512x_I2C if I2C
|
||||
select SND_SOC_PCM512x_SPI if SPI_MASTER
|
||||
select SND_SOC_RT286 if I2C
|
||||
select SND_SOC_RT298 if I2C
|
||||
select SND_SOC_RT5616 if I2C
|
||||
select SND_SOC_RT5631 if I2C
|
||||
select SND_SOC_RT5640 if I2C
|
||||
select SND_SOC_RT5645 if I2C
|
||||
select SND_SOC_RT5651 if I2C
|
||||
select SND_SOC_RT5659 if I2C
|
||||
select SND_SOC_RT5670 if I2C
|
||||
select SND_SOC_RT5677 if I2C && SPI_MASTER
|
||||
select SND_SOC_SGTL5000 if I2C
|
||||
|
@ -195,10 +203,12 @@ config SND_SOC_88PM860X
|
|||
|
||||
config SND_SOC_ARIZONA
|
||||
tristate
|
||||
default y if SND_SOC_CS47L24=y
|
||||
default y if SND_SOC_WM5102=y
|
||||
default y if SND_SOC_WM5110=y
|
||||
default y if SND_SOC_WM8997=y
|
||||
default y if SND_SOC_WM8998=y
|
||||
default m if SND_SOC_CS47L24=m
|
||||
default m if SND_SOC_WM5102=m
|
||||
default m if SND_SOC_WM5110=m
|
||||
default m if SND_SOC_WM8997=m
|
||||
|
@ -211,9 +221,12 @@ config SND_SOC_WM_HUBS
|
|||
|
||||
config SND_SOC_WM_ADSP
|
||||
tristate
|
||||
select SND_SOC_COMPRESS
|
||||
default y if SND_SOC_CS47L24=y
|
||||
default y if SND_SOC_WM5102=y
|
||||
default y if SND_SOC_WM5110=y
|
||||
default y if SND_SOC_WM2200=y
|
||||
default m if SND_SOC_CS47L24=m
|
||||
default m if SND_SOC_WM5102=m
|
||||
default m if SND_SOC_WM5110=m
|
||||
default m if SND_SOC_WM2200=m
|
||||
|
@ -422,6 +435,9 @@ config SND_SOC_CS4349
|
|||
tristate "Cirrus Logic CS4349 CODEC"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_CS47L24
|
||||
tristate
|
||||
|
||||
config SND_SOC_CX20442
|
||||
tristate
|
||||
depends on TTY
|
||||
|
@ -439,6 +455,9 @@ config SND_SOC_DA7210
|
|||
config SND_SOC_DA7213
|
||||
tristate
|
||||
|
||||
config SND_SOC_DA7218
|
||||
tristate
|
||||
|
||||
config SND_SOC_DA7219
|
||||
tristate
|
||||
|
||||
|
@ -468,9 +487,17 @@ config SND_SOC_ES8328_SPI
|
|||
config SND_SOC_GTM601
|
||||
tristate 'GTM601 UMTS modem audio codec'
|
||||
|
||||
config SND_SOC_HDAC_HDMI
|
||||
tristate
|
||||
select SND_HDA_EXT_CORE
|
||||
select HDMI
|
||||
|
||||
config SND_SOC_ICS43432
|
||||
tristate
|
||||
|
||||
config SND_SOC_INNO_RK3036
|
||||
tristate "Inno codec driver for RK3036 SoC"
|
||||
|
||||
config SND_SOC_ISABELLE
|
||||
tristate
|
||||
|
||||
|
@ -499,13 +526,28 @@ config SND_SOC_PCM1681
|
|||
tristate "Texas Instruments PCM1681 CODEC"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_PCM1792A
|
||||
tristate "Texas Instruments PCM1792A CODEC"
|
||||
config SND_SOC_PCM179X
|
||||
tristate "Texas Instruments PCM179X CODEC"
|
||||
depends on SPI_MASTER
|
||||
|
||||
config SND_SOC_PCM3008
|
||||
tristate
|
||||
|
||||
config SND_SOC_PCM3168A
|
||||
tristate
|
||||
|
||||
config SND_SOC_PCM3168A_I2C
|
||||
tristate "Texas Instruments PCM3168A CODEC - I2C"
|
||||
depends on I2C
|
||||
select SND_SOC_PCM3168A
|
||||
select REGMAP_I2C
|
||||
|
||||
config SND_SOC_PCM3168A_SPI
|
||||
tristate "Texas Instruments PCM3168A CODEC - SPI"
|
||||
depends on SPI_MASTER
|
||||
select SND_SOC_PCM3168A
|
||||
select REGMAP_SPI
|
||||
|
||||
config SND_SOC_PCM512x
|
||||
tristate
|
||||
|
||||
|
@ -523,14 +565,18 @@ config SND_SOC_PCM512x_SPI
|
|||
|
||||
config SND_SOC_RL6231
|
||||
tristate
|
||||
default y if SND_SOC_RT5616=y
|
||||
default y if SND_SOC_RT5640=y
|
||||
default y if SND_SOC_RT5645=y
|
||||
default y if SND_SOC_RT5651=y
|
||||
default y if SND_SOC_RT5659=y
|
||||
default y if SND_SOC_RT5670=y
|
||||
default y if SND_SOC_RT5677=y
|
||||
default m if SND_SOC_RT5616=m
|
||||
default m if SND_SOC_RT5640=m
|
||||
default m if SND_SOC_RT5645=m
|
||||
default m if SND_SOC_RT5651=m
|
||||
default m if SND_SOC_RT5659=m
|
||||
default m if SND_SOC_RT5670=m
|
||||
default m if SND_SOC_RT5677=m
|
||||
|
||||
|
@ -549,6 +595,9 @@ config SND_SOC_RT298
|
|||
tristate
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_RT5616
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT5631
|
||||
tristate "Realtek ALC5631/RT5631 CODEC"
|
||||
depends on I2C
|
||||
|
@ -562,6 +611,9 @@ config SND_SOC_RT5645
|
|||
config SND_SOC_RT5651
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT5659
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT5670
|
||||
tristate
|
||||
|
||||
|
@ -838,7 +890,8 @@ config SND_SOC_WM8971
|
|||
tristate
|
||||
|
||||
config SND_SOC_WM8974
|
||||
tristate
|
||||
tristate "Wolfson Microelectronics WM8974 codec"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_WM8978
|
||||
tristate "Wolfson Microelectronics WM8978 codec"
|
||||
|
@ -891,6 +944,7 @@ config SND_SOC_WM9712
|
|||
|
||||
config SND_SOC_WM9713
|
||||
tristate
|
||||
select REGMAP_AC97
|
||||
|
||||
# Amp
|
||||
config SND_SOC_LM4857
|
||||
|
|
|
@ -47,9 +47,11 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o
|
|||
snd-soc-cs42xx8-objs := cs42xx8.o
|
||||
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
|
||||
snd-soc-cs4349-objs := cs4349.o
|
||||
snd-soc-cs47l24-objs := cs47l24.o
|
||||
snd-soc-cx20442-objs := cx20442.o
|
||||
snd-soc-da7210-objs := da7210.o
|
||||
snd-soc-da7213-objs := da7213.o
|
||||
snd-soc-da7218-objs := da7218.o
|
||||
snd-soc-da7219-objs := da7219.o da7219-aad.o
|
||||
snd-soc-da732x-objs := da732x.o
|
||||
snd-soc-da9055-objs := da9055.o
|
||||
|
@ -59,7 +61,9 @@ snd-soc-es8328-objs := es8328.o
|
|||
snd-soc-es8328-i2c-objs := es8328-i2c.o
|
||||
snd-soc-es8328-spi-objs := es8328-spi.o
|
||||
snd-soc-gtm601-objs := gtm601.o
|
||||
snd-soc-hdac-hdmi-objs := hdac_hdmi.o
|
||||
snd-soc-ics43432-objs := ics43432.o
|
||||
snd-soc-inno-rk3036-objs := inno_rk3036.o
|
||||
snd-soc-isabelle-objs := isabelle.o
|
||||
snd-soc-jz4740-codec-objs := jz4740.o
|
||||
snd-soc-l3-objs := l3.o
|
||||
|
@ -76,8 +80,11 @@ snd-soc-mc13783-objs := mc13783.o
|
|||
snd-soc-ml26124-objs := ml26124.o
|
||||
snd-soc-nau8825-objs := nau8825.o
|
||||
snd-soc-pcm1681-objs := pcm1681.o
|
||||
snd-soc-pcm1792a-codec-objs := pcm1792a.o
|
||||
snd-soc-pcm179x-codec-objs := pcm179x.o
|
||||
snd-soc-pcm3008-objs := pcm3008.o
|
||||
snd-soc-pcm3168a-objs := pcm3168a.o
|
||||
snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
|
||||
snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
|
||||
snd-soc-pcm512x-objs := pcm512x.o
|
||||
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
|
||||
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
|
||||
|
@ -85,10 +92,12 @@ snd-soc-rl6231-objs := rl6231.o
|
|||
snd-soc-rl6347a-objs := rl6347a.o
|
||||
snd-soc-rt286-objs := rt286.o
|
||||
snd-soc-rt298-objs := rt298.o
|
||||
snd-soc-rt5616-objs := rt5616.o
|
||||
snd-soc-rt5631-objs := rt5631.o
|
||||
snd-soc-rt5640-objs := rt5640.o
|
||||
snd-soc-rt5645-objs := rt5645.o
|
||||
snd-soc-rt5651-objs := rt5651.o
|
||||
snd-soc-rt5659-objs := rt5659.o
|
||||
snd-soc-rt5670-objs := rt5670.o
|
||||
snd-soc-rt5677-objs := rt5677.o
|
||||
snd-soc-rt5677-spi-objs := rt5677-spi.o
|
||||
|
@ -242,9 +251,11 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
|
|||
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
|
||||
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
|
||||
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
|
||||
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
|
||||
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
|
||||
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
|
||||
obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o
|
||||
obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
|
||||
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
|
||||
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
|
||||
|
@ -254,7 +265,9 @@ obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
|
|||
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
|
||||
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
|
||||
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
|
||||
obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o
|
||||
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
|
||||
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
|
||||
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
|
||||
|
@ -271,8 +284,11 @@ obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
|
|||
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
|
||||
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
|
||||
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
|
||||
obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
|
||||
obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
|
||||
|
@ -280,10 +296,12 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
|
|||
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
|
||||
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
|
||||
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
|
||||
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
|
||||
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
|
||||
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
|
||||
obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
|
||||
obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
|
||||
obj-$(CONFIG_SND_SOC_RT5659) += snd-soc-rt5659.o
|
||||
obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o
|
||||
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
|
||||
obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o
|
||||
|
|
|
@ -70,18 +70,11 @@
|
|||
#define FMT_MASK (0xf8)
|
||||
|
||||
/* CTRL2 */
|
||||
#define DFS_MASK (3 << 2)
|
||||
#define DFS_NORMAL_SPEED (0 << 2)
|
||||
#define DFS_DOUBLE_SPEED (1 << 2)
|
||||
#define DFS_QUAD_SPEED (2 << 2)
|
||||
|
||||
struct ak4613_priv {
|
||||
struct mutex lock;
|
||||
|
||||
unsigned int fmt;
|
||||
u8 fmt_ctrl;
|
||||
int cnt;
|
||||
};
|
||||
|
||||
struct ak4613_formats {
|
||||
unsigned int width;
|
||||
unsigned int fmt;
|
||||
|
@ -92,6 +85,16 @@ struct ak4613_interface {
|
|||
struct ak4613_formats playback;
|
||||
};
|
||||
|
||||
struct ak4613_priv {
|
||||
struct mutex lock;
|
||||
const struct ak4613_interface *iface;
|
||||
|
||||
unsigned int fmt;
|
||||
u8 oc;
|
||||
u8 ic;
|
||||
int cnt;
|
||||
};
|
||||
|
||||
/*
|
||||
* Playback Volume
|
||||
*
|
||||
|
@ -126,7 +129,7 @@ static const struct reg_default ak4613_reg[] = {
|
|||
{ 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
|
||||
};
|
||||
|
||||
#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3)
|
||||
#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3)
|
||||
#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
|
||||
static const struct ak4613_interface ak4613_iface[] = {
|
||||
/* capture */ /* playback */
|
||||
|
@ -240,7 +243,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
|
|||
priv->cnt = 0;
|
||||
}
|
||||
if (!priv->cnt)
|
||||
priv->fmt_ctrl = NO_FMT;
|
||||
priv->iface = NULL;
|
||||
mutex_unlock(&priv->lock);
|
||||
}
|
||||
|
||||
|
@ -265,13 +268,35 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface,
|
||||
int is_play,
|
||||
unsigned int fmt, unsigned int width)
|
||||
{
|
||||
const struct ak4613_formats *fmts;
|
||||
|
||||
fmts = (is_play) ? &iface->playback : &iface->capture;
|
||||
|
||||
if (fmts->fmt != fmt)
|
||||
return false;
|
||||
|
||||
if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
|
||||
if (fmts->width != width)
|
||||
return false;
|
||||
} else {
|
||||
if (fmts->width < width)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||
const struct ak4613_formats *fmts;
|
||||
const struct ak4613_interface *iface;
|
||||
struct device *dev = codec->dev;
|
||||
unsigned int width = params_width(params);
|
||||
unsigned int fmt = priv->fmt;
|
||||
|
@ -305,33 +330,27 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
* It doesn't support TDM at this point
|
||||
*/
|
||||
fmt_ctrl = NO_FMT;
|
||||
for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) {
|
||||
fmts = (is_play) ? &ak4613_iface[i].playback :
|
||||
&ak4613_iface[i].capture;
|
||||
|
||||
if (fmts->fmt != fmt)
|
||||
continue;
|
||||
|
||||
if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
|
||||
if (fmts->width != width)
|
||||
continue;
|
||||
} else {
|
||||
if (fmts->width < width)
|
||||
continue;
|
||||
}
|
||||
|
||||
fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
if (fmt_ctrl == NO_FMT)
|
||||
goto hw_params_end;
|
||||
iface = NULL;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
if ((priv->fmt_ctrl == NO_FMT) ||
|
||||
(priv->fmt_ctrl == fmt_ctrl)) {
|
||||
priv->fmt_ctrl = fmt_ctrl;
|
||||
if (priv->iface) {
|
||||
if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width))
|
||||
iface = priv->iface;
|
||||
} else {
|
||||
for (i = ARRAY_SIZE(ak4613_iface); i >= 0; i--) {
|
||||
if (!ak4613_dai_fmt_matching(ak4613_iface + i,
|
||||
is_play,
|
||||
fmt, width))
|
||||
continue;
|
||||
iface = ak4613_iface + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((priv->iface == NULL) ||
|
||||
(priv->iface == iface)) {
|
||||
priv->iface = iface;
|
||||
priv->cnt++;
|
||||
ret = 0;
|
||||
}
|
||||
|
@ -340,8 +359,13 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
if (ret < 0)
|
||||
goto hw_params_end;
|
||||
|
||||
fmt_ctrl = AUDIO_IFACE_TO_VAL(iface);
|
||||
|
||||
snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
|
||||
snd_soc_write(codec, CTRL2, ctrl2);
|
||||
snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2);
|
||||
|
||||
snd_soc_write(codec, ICTRL, priv->ic);
|
||||
snd_soc_write(codec, OCTRL, priv->oc);
|
||||
|
||||
hw_params_end:
|
||||
if (ret < 0)
|
||||
|
@ -431,6 +455,28 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
|
|||
.num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
|
||||
};
|
||||
|
||||
static void ak4613_parse_of(struct ak4613_priv *priv,
|
||||
struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
char prop[32];
|
||||
int i;
|
||||
|
||||
/* Input 1 - 2 */
|
||||
for (i = 0; i < 2; i++) {
|
||||
snprintf(prop, sizeof(prop), "asahi-kasei,in%d-single-end", i + 1);
|
||||
if (!of_get_property(np, prop, NULL))
|
||||
priv->ic |= 1 << i;
|
||||
}
|
||||
|
||||
/* Output 1 - 6 */
|
||||
for (i = 0; i < 6; i++) {
|
||||
snprintf(prop, sizeof(prop), "asahi-kasei,out%d-single-end", i + 1);
|
||||
if (!of_get_property(np, prop, NULL))
|
||||
priv->oc |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
static int ak4613_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -458,7 +504,9 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->fmt_ctrl = NO_FMT;
|
||||
ak4613_parse_of(priv, dev);
|
||||
|
||||
priv->iface = NULL;
|
||||
priv->cnt = 0;
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
|
|
|
@ -310,7 +310,7 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_init_gpio);
|
||||
|
||||
const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
|
||||
const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
|
||||
"None",
|
||||
"Tone Generator 1",
|
||||
"Tone Generator 2",
|
||||
|
@ -418,7 +418,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_mixer_texts);
|
||||
|
||||
int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
|
||||
unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
|
||||
0x00, /* None */
|
||||
0x04, /* Tone */
|
||||
0x05,
|
||||
|
@ -555,12 +555,12 @@ const char *arizona_sample_rate_val_to_name(unsigned int rate_val)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name);
|
||||
|
||||
const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
|
||||
const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
|
||||
"SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_rate_text);
|
||||
|
||||
const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
|
||||
const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
|
||||
0, 1, 2, 8,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_rate_val);
|
||||
|
@ -702,6 +702,100 @@ const struct soc_enum arizona_in_dmic_osr[] = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_in_dmic_osr);
|
||||
|
||||
static const char * const arizona_anc_input_src_text[] = {
|
||||
"None", "IN1", "IN2", "IN3", "IN4",
|
||||
};
|
||||
|
||||
static const char * const arizona_anc_channel_src_text[] = {
|
||||
"None", "Left", "Right", "Combine",
|
||||
};
|
||||
|
||||
const struct soc_enum arizona_anc_input_src[] = {
|
||||
SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
|
||||
ARIZONA_IN_RXANCL_SEL_SHIFT,
|
||||
ARRAY_SIZE(arizona_anc_input_src_text),
|
||||
arizona_anc_input_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_FCL_ADC_REFORMATTER_CONTROL,
|
||||
ARIZONA_FCL_MIC_MODE_SEL,
|
||||
ARRAY_SIZE(arizona_anc_channel_src_text),
|
||||
arizona_anc_channel_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
|
||||
ARIZONA_IN_RXANCR_SEL_SHIFT,
|
||||
ARRAY_SIZE(arizona_anc_input_src_text),
|
||||
arizona_anc_input_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_FCR_ADC_REFORMATTER_CONTROL,
|
||||
ARIZONA_FCR_MIC_MODE_SEL,
|
||||
ARRAY_SIZE(arizona_anc_channel_src_text),
|
||||
arizona_anc_channel_src_text),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_anc_input_src);
|
||||
|
||||
static const char * const arizona_anc_ng_texts[] = {
|
||||
"None",
|
||||
"Internal",
|
||||
"External",
|
||||
};
|
||||
|
||||
SOC_ENUM_SINGLE_DECL(arizona_anc_ng_enum, SND_SOC_NOPM, 0,
|
||||
arizona_anc_ng_texts);
|
||||
EXPORT_SYMBOL_GPL(arizona_anc_ng_enum);
|
||||
|
||||
static const char * const arizona_output_anc_src_text[] = {
|
||||
"None", "RXANCL", "RXANCR",
|
||||
};
|
||||
|
||||
const struct soc_enum arizona_output_anc_src[] = {
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
|
||||
ARIZONA_OUT1L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1R,
|
||||
ARIZONA_OUT1R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L,
|
||||
ARIZONA_OUT2L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2R,
|
||||
ARIZONA_OUT2R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
|
||||
ARIZONA_OUT3L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_DAC_VOLUME_LIMIT_3R,
|
||||
ARIZONA_OUT3R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4L,
|
||||
ARIZONA_OUT4L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4R,
|
||||
ARIZONA_OUT4R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5L,
|
||||
ARIZONA_OUT5L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5R,
|
||||
ARIZONA_OUT5R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6L,
|
||||
ARIZONA_OUT6L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6R,
|
||||
ARIZONA_OUT6R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_output_anc_src);
|
||||
|
||||
static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
|
||||
{
|
||||
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||
|
@ -1023,6 +1117,31 @@ void arizona_init_dvfs(struct arizona_priv *priv)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_init_dvfs);
|
||||
|
||||
int arizona_anc_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
|
||||
unsigned int mask = 0x3 << w->shift;
|
||||
unsigned int val;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
val = 1 << w->shift;
|
||||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
val = 1 << (w->shift + 1);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_anc_ev);
|
||||
|
||||
static unsigned int arizona_opclk_ref_48k_rates[] = {
|
||||
6144000,
|
||||
12288000,
|
||||
|
@ -1095,7 +1214,7 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
|
|||
unsigned int reg;
|
||||
unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK;
|
||||
unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT;
|
||||
unsigned int *clk;
|
||||
int *clk;
|
||||
|
||||
switch (clk_id) {
|
||||
case ARIZONA_CLK_SYSCLK:
|
||||
|
@ -1375,6 +1494,9 @@ static int arizona_startup(struct snd_pcm_substream *substream,
|
|||
const struct snd_pcm_hw_constraint_list *constraint;
|
||||
unsigned int base_rate;
|
||||
|
||||
if (!substream->runtime)
|
||||
return 0;
|
||||
|
||||
switch (dai_priv->clk) {
|
||||
case ARIZONA_CLK_SYSCLK:
|
||||
base_rate = priv->sysclk;
|
||||
|
@ -1901,18 +2023,18 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
|
|||
}
|
||||
|
||||
switch (fll->arizona->type) {
|
||||
case WM5102:
|
||||
case WM8997:
|
||||
return init_ratio;
|
||||
case WM5110:
|
||||
case WM8280:
|
||||
if (fll->arizona->rev < 3 || sync)
|
||||
return init_ratio;
|
||||
break;
|
||||
case WM8998:
|
||||
case WM1814:
|
||||
default:
|
||||
if (sync)
|
||||
return init_ratio;
|
||||
break;
|
||||
default:
|
||||
return init_ratio;
|
||||
}
|
||||
|
||||
cfg->fratio = init_ratio - 1;
|
||||
|
@ -2093,9 +2215,9 @@ static int arizona_enable_fll(struct arizona_fll *fll)
|
|||
/* Facilitate smooth refclk across the transition */
|
||||
regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
|
||||
ARIZONA_FLL1_GAIN_MASK, 0);
|
||||
regmap_update_bits_async(fll->arizona->regmap, fll->base + 1,
|
||||
ARIZONA_FLL1_FREERUN,
|
||||
ARIZONA_FLL1_FREERUN);
|
||||
regmap_update_bits(fll->arizona->regmap, fll->base + 1,
|
||||
ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
|
||||
udelay(32);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
#define ARIZONA_CLK_98MHZ 5
|
||||
#define ARIZONA_CLK_147MHZ 6
|
||||
|
||||
#define ARIZONA_MAX_DAI 6
|
||||
#define ARIZONA_MAX_DAI 8
|
||||
#define ARIZONA_MAX_ADSP 4
|
||||
|
||||
#define ARIZONA_DVFS_SR1_RQ 0x001
|
||||
|
@ -96,8 +96,8 @@ struct arizona_priv {
|
|||
#define ARIZONA_NUM_MIXER_INPUTS 104
|
||||
|
||||
extern const unsigned int arizona_mixer_tlv[];
|
||||
extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
|
||||
extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
|
||||
extern const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
|
||||
extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
|
||||
|
||||
#define ARIZONA_GAINMUX_CONTROLS(name, base) \
|
||||
SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
|
||||
|
@ -216,8 +216,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
|
|||
#define ARIZONA_RATE_ENUM_SIZE 4
|
||||
#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
|
||||
|
||||
extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
|
||||
extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
|
||||
extern const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
|
||||
extern const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
|
||||
extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
|
||||
extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
|
||||
|
||||
|
@ -242,6 +242,10 @@ extern const struct soc_enum arizona_in_dmic_osr[];
|
|||
|
||||
extern const struct snd_kcontrol_new arizona_adsp2_rate_controls[];
|
||||
|
||||
extern const struct soc_enum arizona_anc_input_src[];
|
||||
extern const struct soc_enum arizona_anc_ng_enum;
|
||||
extern const struct soc_enum arizona_output_anc_src[];
|
||||
|
||||
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event);
|
||||
|
@ -251,6 +255,9 @@ extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
|
|||
extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event);
|
||||
extern int arizona_anc_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event);
|
||||
|
||||
extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
|
|
1148
sound/soc/codecs/cs47l24.c
Normal file
1148
sound/soc/codecs/cs47l24.c
Normal file
File diff suppressed because it is too large
Load diff
23
sound/soc/codecs/cs47l24.h
Normal file
23
sound/soc/codecs/cs47l24.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24
|
||||
*
|
||||
* Copyright 2015 Cirrus Logic Inc.
|
||||
*
|
||||
* Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.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.
|
||||
*/
|
||||
|
||||
#ifndef _CS47L24_H
|
||||
#define _CS47L24_H
|
||||
|
||||
#include "arizona.h"
|
||||
|
||||
#define CS47L24_FLL1 1
|
||||
#define CS47L24_FLL2 2
|
||||
#define CS47L24_FLL1_REFCLK 3
|
||||
#define CS47L24_FLL2_REFCLK 4
|
||||
|
||||
#endif
|
3341
sound/soc/codecs/da7218.c
Normal file
3341
sound/soc/codecs/da7218.c
Normal file
File diff suppressed because it is too large
Load diff
1414
sound/soc/codecs/da7218.h
Normal file
1414
sound/soc/codecs/da7218.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -968,10 +968,11 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = {
|
|||
{"Mixin PGA", NULL, "Mic PGA"},
|
||||
{"ADC", NULL, "Mixin PGA"},
|
||||
|
||||
{"Sidetone Filter", NULL, "ADC"},
|
||||
{"Mixer In", NULL, "Mixer In Supply"},
|
||||
{"Mixer In", "Mic Switch", "ADC"},
|
||||
|
||||
{"Sidetone Filter", NULL, "Mixer In"},
|
||||
|
||||
{"Tone Generator", NULL, "TONE"},
|
||||
|
||||
DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"),
|
||||
|
@ -1073,11 +1074,8 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
|
|||
u32 freq_ref;
|
||||
u64 frac_div;
|
||||
|
||||
/* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */
|
||||
if (da7219->mclk_rate == 32768) {
|
||||
indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
|
||||
} else if (da7219->mclk_rate < 2000000) {
|
||||
/* Verify 2MHz - 54MHz MCLK provided, and set input divider */
|
||||
if (da7219->mclk_rate < 2000000) {
|
||||
dev_err(codec->dev, "PLL input clock %d below valid range\n",
|
||||
da7219->mclk_rate);
|
||||
return -EINVAL;
|
||||
|
@ -1118,9 +1116,6 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
|
|||
case DA7219_SYSCLK_PLL_SRM:
|
||||
pll_ctrl |= DA7219_PLL_MODE_SRM;
|
||||
break;
|
||||
case DA7219_SYSCLK_PLL_32KHZ:
|
||||
pll_ctrl |= DA7219_PLL_MODE_32KHZ;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "Invalid PLL config\n");
|
||||
return -EINVAL;
|
||||
|
@ -1161,18 +1156,44 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
|
||||
DA7219_DAI_CLK_POL_INV;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
|
||||
DA7219_DAI_CLK_POL_INV;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
|
||||
DA7219_DAI_CLK_POL_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -1306,7 +1327,7 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
channels = params_channels(params);
|
||||
if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) {
|
||||
if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
|
||||
dev_err(codec->dev,
|
||||
"Invalid number of channels, only 1 to %d supported\n",
|
||||
DA7219_DAI_CH_NUM_MAX);
|
||||
|
@ -1405,28 +1426,12 @@ static const struct of_device_id da7219_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, da7219_of_match);
|
||||
|
||||
static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec,
|
||||
u32 val)
|
||||
{
|
||||
switch (val) {
|
||||
case 1050:
|
||||
return DA7219_LDO_LVL_SEL_1_05V;
|
||||
case 1100:
|
||||
return DA7219_LDO_LVL_SEL_1_10V;
|
||||
case 1200:
|
||||
return DA7219_LDO_LVL_SEL_1_20V;
|
||||
case 1400:
|
||||
return DA7219_LDO_LVL_SEL_1_40V;
|
||||
default:
|
||||
dev_warn(codec->dev, "Invalid LDO level");
|
||||
return DA7219_LDO_LVL_SEL_1_05V;
|
||||
}
|
||||
}
|
||||
|
||||
static enum da7219_micbias_voltage
|
||||
da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
|
||||
{
|
||||
switch (val) {
|
||||
case 1600:
|
||||
return DA7219_MICBIAS_1_6V;
|
||||
case 1800:
|
||||
return DA7219_MICBIAS_1_8V;
|
||||
case 2000:
|
||||
|
@ -1469,9 +1474,6 @@ static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
|
|||
if (!pdata)
|
||||
return NULL;
|
||||
|
||||
if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0)
|
||||
pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32);
|
||||
|
||||
if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0)
|
||||
pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32);
|
||||
else
|
||||
|
@ -1516,24 +1518,13 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec,
|
|||
snd_soc_update_bits(codec, DA7219_REFERENCES,
|
||||
DA7219_BIAS_EN_MASK,
|
||||
DA7219_BIAS_EN_MASK);
|
||||
|
||||
/* Enable Internal Digital LDO */
|
||||
snd_soc_update_bits(codec, DA7219_LDO_CTRL,
|
||||
DA7219_LDO_EN_MASK,
|
||||
DA7219_LDO_EN_MASK);
|
||||
}
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
/* Only disable if jack detection not active */
|
||||
if (!da7219->aad->jack) {
|
||||
/* Bypass Internal Digital LDO */
|
||||
snd_soc_update_bits(codec, DA7219_LDO_CTRL,
|
||||
DA7219_LDO_EN_MASK, 0);
|
||||
|
||||
/* Master bias */
|
||||
/* Only disable master bias if jack detection not active */
|
||||
if (!da7219->aad->jack)
|
||||
snd_soc_update_bits(codec, DA7219_REFERENCES,
|
||||
DA7219_BIAS_EN_MASK, 0);
|
||||
}
|
||||
|
||||
/* MCLK */
|
||||
if (da7219->mclk)
|
||||
|
@ -1600,21 +1591,9 @@ static void da7219_handle_pdata(struct snd_soc_codec *codec)
|
|||
if (pdata) {
|
||||
u8 micbias_lvl = 0;
|
||||
|
||||
/* Internal LDO */
|
||||
switch (pdata->ldo_lvl_sel) {
|
||||
case DA7219_LDO_LVL_SEL_1_05V:
|
||||
case DA7219_LDO_LVL_SEL_1_10V:
|
||||
case DA7219_LDO_LVL_SEL_1_20V:
|
||||
case DA7219_LDO_LVL_SEL_1_40V:
|
||||
snd_soc_update_bits(codec, DA7219_LDO_CTRL,
|
||||
DA7219_LDO_LEVEL_SELECT_MASK,
|
||||
(pdata->ldo_lvl_sel <<
|
||||
DA7219_LDO_LEVEL_SELECT_SHIFT));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Mic Bias voltages */
|
||||
switch (pdata->micbias_lvl) {
|
||||
case DA7219_MICBIAS_1_6V:
|
||||
case DA7219_MICBIAS_1_8V:
|
||||
case DA7219_MICBIAS_2_0V:
|
||||
case DA7219_MICBIAS_2_2V:
|
||||
|
@ -1639,9 +1618,14 @@ static void da7219_handle_pdata(struct snd_soc_codec *codec)
|
|||
}
|
||||
}
|
||||
|
||||
static struct reg_sequence da7219_rev_aa_patch[] = {
|
||||
{ DA7219_REFERENCES, 0x08 },
|
||||
};
|
||||
|
||||
static int da7219_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int rev;
|
||||
int ret;
|
||||
|
||||
mutex_init(&da7219->lock);
|
||||
|
@ -1651,6 +1635,26 @@ static int da7219_probe(struct snd_soc_codec *codec)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(da7219->regmap, DA7219_CHIP_REVISION, &rev);
|
||||
if (ret) {
|
||||
dev_err(codec->dev, "Failed to read chip revision: %d\n", ret);
|
||||
goto err_disable_reg;
|
||||
}
|
||||
|
||||
switch (rev & DA7219_CHIP_MINOR_MASK) {
|
||||
case 0:
|
||||
ret = regmap_register_patch(da7219->regmap, da7219_rev_aa_patch,
|
||||
ARRAY_SIZE(da7219_rev_aa_patch));
|
||||
if (ret) {
|
||||
dev_err(codec->dev, "Failed to register AA patch: %d\n",
|
||||
ret);
|
||||
goto err_disable_reg;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Handle DT/Platform data */
|
||||
if (codec->dev->of_node)
|
||||
da7219->pdata = da7219_of_to_pdata(codec);
|
||||
|
@ -1662,10 +1666,12 @@ static int da7219_probe(struct snd_soc_codec *codec)
|
|||
/* Check if MCLK provided */
|
||||
da7219->mclk = devm_clk_get(codec->dev, "mclk");
|
||||
if (IS_ERR(da7219->mclk)) {
|
||||
if (PTR_ERR(da7219->mclk) != -ENOENT)
|
||||
return PTR_ERR(da7219->mclk);
|
||||
else
|
||||
if (PTR_ERR(da7219->mclk) != -ENOENT) {
|
||||
ret = PTR_ERR(da7219->mclk);
|
||||
goto err_disable_reg;
|
||||
} else {
|
||||
da7219->mclk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Default PC counter to free-running */
|
||||
|
@ -1693,7 +1699,16 @@ static int da7219_probe(struct snd_soc_codec *codec)
|
|||
snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
|
||||
|
||||
/* Initialise AAD block */
|
||||
return da7219_aad_init(codec);
|
||||
ret = da7219_aad_init(codec);
|
||||
if (ret)
|
||||
goto err_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_reg:
|
||||
regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int da7219_remove(struct snd_soc_codec *codec)
|
||||
|
@ -1776,7 +1791,7 @@ static struct reg_default da7219_reg_defaults[] = {
|
|||
{ DA7219_DIG_ROUTING_DAC, 0x32 },
|
||||
{ DA7219_DAI_OFFSET_LOWER, 0x00 },
|
||||
{ DA7219_DAI_OFFSET_UPPER, 0x00 },
|
||||
{ DA7219_REFERENCES, 0x00 },
|
||||
{ DA7219_REFERENCES, 0x08 },
|
||||
{ DA7219_MIXIN_L_SELECT, 0x00 },
|
||||
{ DA7219_MIXIN_L_GAIN, 0x03 },
|
||||
{ DA7219_ADC_L_GAIN, 0x6F },
|
||||
|
@ -1810,8 +1825,6 @@ static struct reg_default da7219_reg_defaults[] = {
|
|||
{ DA7219_MIXOUT_R_CTRL, 0x10 },
|
||||
{ DA7219_CHIP_ID1, 0x23 },
|
||||
{ DA7219_CHIP_ID2, 0x93 },
|
||||
{ DA7219_CHIP_REVISION, 0x00 },
|
||||
{ DA7219_LDO_CTRL, 0x00 },
|
||||
{ DA7219_IO_CTRL, 0x00 },
|
||||
{ DA7219_GAIN_RAMP_CTRL, 0x00 },
|
||||
{ DA7219_PC_COUNT, 0x02 },
|
||||
|
|
|
@ -85,7 +85,6 @@
|
|||
#define DA7219_CHIP_ID1 0x81
|
||||
#define DA7219_CHIP_ID2 0x82
|
||||
#define DA7219_CHIP_REVISION 0x83
|
||||
#define DA7219_LDO_CTRL 0x90
|
||||
#define DA7219_IO_CTRL 0x91
|
||||
#define DA7219_GAIN_RAMP_CTRL 0x92
|
||||
#define DA7219_PC_COUNT 0x94
|
||||
|
@ -207,7 +206,6 @@
|
|||
#define DA7219_PLL_MODE_BYPASS (0x0 << 6)
|
||||
#define DA7219_PLL_MODE_NORMAL (0x1 << 6)
|
||||
#define DA7219_PLL_MODE_SRM (0x2 << 6)
|
||||
#define DA7219_PLL_MODE_32KHZ (0x3 << 6)
|
||||
|
||||
/* DA7219_PLL_FRAC_TOP = 0x22 */
|
||||
#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0
|
||||
|
@ -569,12 +567,6 @@
|
|||
#define DA7219_CHIP_MAJOR_SHIFT 4
|
||||
#define DA7219_CHIP_MAJOR_MASK (0xF << 4)
|
||||
|
||||
/* DA7219_LDO_CTRL = 0x90 */
|
||||
#define DA7219_LDO_LEVEL_SELECT_SHIFT 4
|
||||
#define DA7219_LDO_LEVEL_SELECT_MASK (0x3 << 4)
|
||||
#define DA7219_LDO_EN_SHIFT 7
|
||||
#define DA7219_LDO_EN_MASK (0x1 << 7)
|
||||
|
||||
/* DA7219_IO_CTRL = 0x91 */
|
||||
#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0
|
||||
#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0)
|
||||
|
@ -787,7 +779,6 @@ enum da7219_sys_clk {
|
|||
DA7219_SYSCLK_MCLK = 0,
|
||||
DA7219_SYSCLK_PLL,
|
||||
DA7219_SYSCLK_PLL_SRM,
|
||||
DA7219_SYSCLK_PLL_32KHZ
|
||||
};
|
||||
|
||||
/* Regulators */
|
||||
|
|
697
sound/soc/codecs/hdac_hdmi.c
Normal file
697
sound/soc/codecs/hdac_hdmi.c
Normal file
|
@ -0,0 +1,697 @@
|
|||
/*
|
||||
* hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
|
||||
*
|
||||
* Copyright (C) 2014-2015 Intel Corp
|
||||
* Author: Samreen Nilofer <samreen.nilofer@intel.com>
|
||||
* Subhransu S. Prusty <subhransu.s.prusty@intel.com>
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/hdaudio_ext.h>
|
||||
#include <sound/hda_i915.h>
|
||||
#include "../../hda/local.h"
|
||||
|
||||
#define AMP_OUT_MUTE 0xb080
|
||||
#define AMP_OUT_UNMUTE 0xb000
|
||||
#define PIN_OUT (AC_PINCTL_OUT_EN)
|
||||
|
||||
#define HDA_MAX_CONNECTIONS 32
|
||||
|
||||
struct hdac_hdmi_cvt_params {
|
||||
unsigned int channels_min;
|
||||
unsigned int channels_max;
|
||||
u32 rates;
|
||||
u64 formats;
|
||||
unsigned int maxbps;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_cvt {
|
||||
struct list_head head;
|
||||
hda_nid_t nid;
|
||||
struct hdac_hdmi_cvt_params params;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_pin {
|
||||
struct list_head head;
|
||||
hda_nid_t nid;
|
||||
int num_mux_nids;
|
||||
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
|
||||
};
|
||||
|
||||
struct hdac_hdmi_dai_pin_map {
|
||||
int dai_id;
|
||||
struct hdac_hdmi_pin *pin;
|
||||
struct hdac_hdmi_cvt *cvt;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_priv {
|
||||
struct hdac_hdmi_dai_pin_map dai_map[3];
|
||||
struct list_head pin_list;
|
||||
struct list_head cvt_list;
|
||||
int num_pin;
|
||||
int num_cvt;
|
||||
};
|
||||
|
||||
static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
|
||||
{
|
||||
struct hdac_device *hdac = dev_to_hdac_dev(dev);
|
||||
|
||||
return to_ehdac_device(hdac);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
|
||||
hda_nid_t cvt_nid, hda_nid_t pin_nid,
|
||||
u32 stream_tag, int format)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n",
|
||||
cvt_nid, pin_nid, stream_tag, format);
|
||||
|
||||
val = (stream_tag << 4);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, val);
|
||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
|
||||
int packet_index, int byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = (packet_index << 5) | (byte_index & 0x1f);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_INDEX, val);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
|
||||
hda_nid_t cvt_nid, hda_nid_t pin_nid)
|
||||
{
|
||||
uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
|
||||
struct hdmi_audio_infoframe frame;
|
||||
u8 *dip = (u8 *)&frame;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
hdmi_audio_infoframe_init(&frame);
|
||||
|
||||
/* Default stereo for now */
|
||||
frame.channels = 2;
|
||||
|
||||
/* setup channel count */
|
||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1);
|
||||
|
||||
ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* stop infoframe transmission */
|
||||
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE);
|
||||
|
||||
|
||||
/* Fill infoframe. Index auto-incremented */
|
||||
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(frame); i++)
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
|
||||
|
||||
/* Start infoframe */
|
||||
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
|
||||
struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state)
|
||||
{
|
||||
/* Power up pin widget */
|
||||
if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin->nid,
|
||||
pwr_state))
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->pin->nid, 0,
|
||||
AC_VERB_SET_POWER_STATE, pwr_state);
|
||||
|
||||
/* Power up converter */
|
||||
if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt->nid,
|
||||
pwr_state))
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
|
||||
AC_VERB_SET_POWER_STATE, pwr_state);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
struct hdac_ext_dma_params *dd;
|
||||
int ret;
|
||||
|
||||
if (dai->id > 0) {
|
||||
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
|
||||
dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
|
||||
dd->stream_tag, dd->format);
|
||||
|
||||
ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
|
||||
dai_map->pin->nid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return hdac_hdmi_setup_stream(hdac, dai_map->cvt->nid,
|
||||
dai_map->pin->nid, dd->stream_tag, dd->format);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_ext_dma_params *dd;
|
||||
|
||||
if (dai->id > 0) {
|
||||
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dd = kzalloc(sizeof(*dd), GFP_KERNEL);
|
||||
if (!dd)
|
||||
return -ENOMEM;
|
||||
dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
|
||||
params_channels(hparams), params_format(hparams),
|
||||
24, 0);
|
||||
|
||||
snd_soc_dai_set_dma_data(dai, substream, (void *)dd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_ext_dma_params *dd;
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, 0);
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, 0);
|
||||
|
||||
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
|
||||
snd_soc_dai_set_dma_data(dai, substream, NULL);
|
||||
|
||||
kfree(dd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
int val;
|
||||
|
||||
if (dai->id > 0) {
|
||||
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0);
|
||||
dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
|
||||
|
||||
if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
|
||||
dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
|
||||
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
}
|
||||
|
||||
static int
|
||||
hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Only stereo supported as of now */
|
||||
cvt->params.channels_min = cvt->params.channels_max = 2;
|
||||
|
||||
err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
|
||||
&cvt->params.rates,
|
||||
&cvt->params.formats,
|
||||
&cvt->params.maxbps);
|
||||
if (err < 0)
|
||||
dev_err(&hdac->dev,
|
||||
"Failed to query pcm params for nid %d: %d\n",
|
||||
cvt->nid, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
|
||||
enum snd_soc_dapm_type id,
|
||||
const char *wname, const char *stream)
|
||||
{
|
||||
w->id = id;
|
||||
w->name = wname;
|
||||
w->sname = stream;
|
||||
w->reg = SND_SOC_NOPM;
|
||||
w->shift = 0;
|
||||
w->kcontrol_news = NULL;
|
||||
w->num_kcontrols = 0;
|
||||
w->priv = NULL;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
|
||||
const char *sink, const char *control, const char *src)
|
||||
{
|
||||
route->sink = sink;
|
||||
route->source = src;
|
||||
route->control = control;
|
||||
route->connected = NULL;
|
||||
}
|
||||
|
||||
static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
|
||||
struct hdac_hdmi_dai_pin_map *dai_map)
|
||||
{
|
||||
struct snd_soc_dapm_route route[1];
|
||||
struct snd_soc_dapm_widget widgets[2] = { {0} };
|
||||
|
||||
memset(&route, 0, sizeof(route));
|
||||
|
||||
hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
|
||||
"hif1 Output", NULL);
|
||||
hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
|
||||
"Coverter 1", "hif1");
|
||||
|
||||
hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
|
||||
|
||||
snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
|
||||
snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
|
||||
}
|
||||
|
||||
static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
|
||||
{
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0];
|
||||
struct hdac_hdmi_cvt *cvt;
|
||||
struct hdac_hdmi_pin *pin;
|
||||
|
||||
if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Currently on board only 1 pin and 1 converter is enabled for
|
||||
* simplification, more will be added eventually
|
||||
* So using fixed map for dai_id:pin:cvt
|
||||
*/
|
||||
cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head);
|
||||
pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head);
|
||||
|
||||
dai_map->dai_id = 0;
|
||||
dai_map->pin = pin;
|
||||
|
||||
dai_map->cvt = cvt;
|
||||
|
||||
/* Enable out path for this pin widget */
|
||||
snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
|
||||
/* Enable transmission */
|
||||
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1, 1);
|
||||
|
||||
/* Category Code (CC) to zero */
|
||||
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
|
||||
AC_VERB_SET_DIGI_CONVERT_2, 0);
|
||||
|
||||
snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
|
||||
AC_VERB_SET_CONNECT_SEL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
|
||||
{
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_cvt *cvt;
|
||||
|
||||
cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
|
||||
if (!cvt)
|
||||
return -ENOMEM;
|
||||
|
||||
cvt->nid = nid;
|
||||
|
||||
list_add_tail(&cvt->head, &hdmi->cvt_list);
|
||||
hdmi->num_cvt++;
|
||||
|
||||
return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
|
||||
{
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_pin *pin;
|
||||
|
||||
pin = kzalloc(sizeof(*pin), GFP_KERNEL);
|
||||
if (!pin)
|
||||
return -ENOMEM;
|
||||
|
||||
pin->nid = nid;
|
||||
|
||||
list_add_tail(&pin->head, &hdmi->pin_list);
|
||||
hdmi->num_pin++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse all nodes and store the cvt/pin nids in array
|
||||
* Add one time initialization for pin and cvt widgets
|
||||
*/
|
||||
static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
|
||||
{
|
||||
hda_nid_t nid;
|
||||
int i, num_nodes;
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
int ret;
|
||||
|
||||
num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
|
||||
if (!nid || num_nodes <= 0) {
|
||||
dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdac->num_nodes = num_nodes;
|
||||
hdac->start_nid = nid;
|
||||
|
||||
for (i = 0; i < hdac->num_nodes; i++, nid++) {
|
||||
unsigned int caps;
|
||||
unsigned int type;
|
||||
|
||||
caps = get_wcaps(hdac, nid);
|
||||
type = get_wcaps_type(caps);
|
||||
|
||||
if (!(caps & AC_WCAP_DIGITAL))
|
||||
continue;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case AC_WID_AUD_OUT:
|
||||
ret = hdac_hdmi_add_cvt(edev, nid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case AC_WID_PIN:
|
||||
ret = hdac_hdmi_add_pin(edev, nid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hdac->end_nid = nid;
|
||||
|
||||
if (!hdmi->num_pin || !hdmi->num_cvt)
|
||||
return -EIO;
|
||||
|
||||
return hdac_hdmi_init_dai_map(edev);
|
||||
}
|
||||
|
||||
static int hdmi_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
snd_soc_component_get_dapm(&codec->component);
|
||||
|
||||
edev->scodec = codec;
|
||||
|
||||
create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
|
||||
|
||||
/* Imp: Store the card pointer in hda_codec */
|
||||
edev->card = dapm->card->snd_card;
|
||||
|
||||
/*
|
||||
* hdac_device core already sets the state to active and calls
|
||||
* get_noresume. So enable runtime and set the device to suspend.
|
||||
*/
|
||||
pm_runtime_enable(&edev->hdac.dev);
|
||||
pm_runtime_put(&edev->hdac.dev);
|
||||
pm_runtime_suspend(&edev->hdac.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_codec_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
pm_runtime_disable(&edev->hdac.dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver hdmi_hda_codec = {
|
||||
.probe = hdmi_codec_probe,
|
||||
.remove = hdmi_codec_remove,
|
||||
.idle_bias_off = true,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops hdmi_dai_ops = {
|
||||
.startup = hdac_hdmi_pcm_open,
|
||||
.shutdown = hdac_hdmi_pcm_close,
|
||||
.hw_params = hdac_hdmi_set_hw_params,
|
||||
.prepare = hdac_hdmi_playback_prepare,
|
||||
.hw_free = hdac_hdmi_playback_cleanup,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver hdmi_dais[] = {
|
||||
{ .name = "intel-hdmi-hif1",
|
||||
.playback = {
|
||||
.stream_name = "hif1",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_32000 |
|
||||
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
|
||||
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S20_3LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
|
||||
},
|
||||
.ops = &hdmi_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
|
||||
{
|
||||
struct hdac_device *codec = &edev->hdac;
|
||||
struct hdac_hdmi_priv *hdmi_priv;
|
||||
int ret = 0;
|
||||
|
||||
hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
|
||||
if (hdmi_priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
edev->private_data = hdmi_priv;
|
||||
|
||||
dev_set_drvdata(&codec->dev, edev);
|
||||
|
||||
INIT_LIST_HEAD(&hdmi_priv->pin_list);
|
||||
INIT_LIST_HEAD(&hdmi_priv->cvt_list);
|
||||
|
||||
ret = hdac_hdmi_parse_and_map_nid(edev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* ASoC specific initialization */
|
||||
return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
|
||||
hdmi_dais, ARRAY_SIZE(hdmi_dais));
|
||||
}
|
||||
|
||||
static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
|
||||
{
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_pin *pin, *pin_next;
|
||||
struct hdac_hdmi_cvt *cvt, *cvt_next;
|
||||
|
||||
snd_soc_unregister_codec(&edev->hdac.dev);
|
||||
|
||||
list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
|
||||
list_del(&cvt->head);
|
||||
kfree(cvt);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) {
|
||||
list_del(&pin->head);
|
||||
kfree(pin);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int hdac_hdmi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct hdac_ext_device *edev = to_hda_ext_device(dev);
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_bus *bus = hdac->bus;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "Enter: %s\n", __func__);
|
||||
|
||||
/* controller may not have been initialized for the first time */
|
||||
if (!bus)
|
||||
return 0;
|
||||
|
||||
/* Power down afg */
|
||||
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
|
||||
snd_hdac_codec_write(hdac, hdac->afg, 0,
|
||||
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
|
||||
|
||||
err = snd_hdac_display_power(bus, false);
|
||||
if (err < 0) {
|
||||
dev_err(bus->dev, "Cannot turn on display power on i915\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct hdac_ext_device *edev = to_hda_ext_device(dev);
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_bus *bus = hdac->bus;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "Enter: %s\n", __func__);
|
||||
|
||||
/* controller may not have been initialized for the first time */
|
||||
if (!bus)
|
||||
return 0;
|
||||
|
||||
err = snd_hdac_display_power(bus, true);
|
||||
if (err < 0) {
|
||||
dev_err(bus->dev, "Cannot turn on display power on i915\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Power up afg */
|
||||
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
|
||||
snd_hdac_codec_write(hdac, hdac->afg, 0,
|
||||
AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define hdac_hdmi_runtime_suspend NULL
|
||||
#define hdac_hdmi_runtime_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops hdac_hdmi_pm = {
|
||||
SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct hda_device_id hdmi_list[] = {
|
||||
HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hdaudio, hdmi_list);
|
||||
|
||||
static struct hdac_ext_driver hdmi_driver = {
|
||||
. hdac = {
|
||||
.driver = {
|
||||
.name = "HDMI HDA Codec",
|
||||
.pm = &hdac_hdmi_pm,
|
||||
},
|
||||
.id_table = hdmi_list,
|
||||
},
|
||||
.probe = hdac_hdmi_dev_probe,
|
||||
.remove = hdac_hdmi_dev_remove,
|
||||
};
|
||||
|
||||
static int __init hdmi_init(void)
|
||||
{
|
||||
return snd_hda_ext_driver_register(&hdmi_driver);
|
||||
}
|
||||
|
||||
static void __exit hdmi_exit(void)
|
||||
{
|
||||
snd_hda_ext_driver_unregister(&hdmi_driver);
|
||||
}
|
||||
|
||||
module_init(hdmi_init);
|
||||
module_exit(hdmi_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("HDMI HD codec");
|
||||
MODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>");
|
||||
MODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>");
|
490
sound/soc/codecs/inno_rk3036.c
Normal file
490
sound/soc/codecs/inno_rk3036.c
Normal file
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Driver of Inno codec for rk3036 by Rockchip Inc.
|
||||
*
|
||||
* Author: Rockchip Inc.
|
||||
* Author: Zheng ShunQian<zhengsq@rock-chips.com>
|
||||
*/
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/soc-dai.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "inno_rk3036.h"
|
||||
|
||||
struct rk3036_codec_priv {
|
||||
void __iomem *base;
|
||||
struct clk *pclk;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0);
|
||||
|
||||
static int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 2;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
int val, ret, regval;
|
||||
|
||||
ret = snd_soc_component_read(component, INNO_R09, ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) &
|
||||
INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) &
|
||||
INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
|
||||
ucontrol->value.integer.value[1] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
int val, ret, regmsk;
|
||||
|
||||
val = (ucontrol->value.integer.value[0] ?
|
||||
INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
|
||||
INNO_R09_HPL_ANITPOP_SHIFT;
|
||||
val |= (ucontrol->value.integer.value[1] ?
|
||||
INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
|
||||
INNO_R09_HPR_ANITPOP_SHIFT;
|
||||
|
||||
regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT |
|
||||
INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT;
|
||||
|
||||
ret = snd_soc_component_update_bits(component, INNO_R09,
|
||||
regmsk, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
.info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \
|
||||
.put = rk3036_codec_antipop_put, }
|
||||
|
||||
static const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = {
|
||||
SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08,
|
||||
INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB,
|
||||
INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv),
|
||||
SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT,
|
||||
INNO_R06_VOUTR_CZ_SHIFT, 1, 0),
|
||||
SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT,
|
||||
INNO_R09_HPR_MUTE_SHIFT, 1, 0),
|
||||
SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09,
|
||||
INNO_R09_DACL_SWITCH_SHIFT, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09,
|
||||
INNO_R09_DACR_SWITCH_SHIFT, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = {
|
||||
SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05,
|
||||
INNO_R05_HPL_WORK_SHIFT, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = {
|
||||
SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05,
|
||||
INNO_R05_HPR_WORK_SHIFT, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06,
|
||||
INNO_R06_DAC_EN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04,
|
||||
INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04,
|
||||
INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06,
|
||||
INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06,
|
||||
INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04,
|
||||
INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04,
|
||||
INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04,
|
||||
INNO_R04_DACL_SW_SHIFT, 0),
|
||||
SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04,
|
||||
INNO_R04_DACR_SW_SHIFT, 0),
|
||||
|
||||
SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
|
||||
rk3036_codec_hpl_mixer_controls,
|
||||
ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
|
||||
rk3036_codec_hpr_mixer_controls,
|
||||
ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)),
|
||||
|
||||
SND_SOC_DAPM_PGA("HP Left Out", INNO_R05,
|
||||
INNO_R05_HPL_EN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("HP Right Out", INNO_R05,
|
||||
INNO_R05_HPR_EN_SHIFT, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_MIXER("HP Left Switch", SND_SOC_NOPM, 0, 0,
|
||||
rk3036_codec_hpl_switch_controls,
|
||||
ARRAY_SIZE(rk3036_codec_hpl_switch_controls)),
|
||||
SND_SOC_DAPM_MIXER("HP Right Switch", SND_SOC_NOPM, 0, 0,
|
||||
rk3036_codec_hpr_switch_controls,
|
||||
ARRAY_SIZE(rk3036_codec_hpr_switch_controls)),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("HPL"),
|
||||
SND_SOC_DAPM_OUTPUT("HPR"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
|
||||
{"DACL VREF", NULL, "DAC PWR"},
|
||||
{"DACR VREF", NULL, "DAC PWR"},
|
||||
{"DACL HiLo VREF", NULL, "DAC PWR"},
|
||||
{"DACR HiLo VREF", NULL, "DAC PWR"},
|
||||
{"DACL CLK", NULL, "DAC PWR"},
|
||||
{"DACR CLK", NULL, "DAC PWR"},
|
||||
|
||||
{"DACL", NULL, "DACL VREF"},
|
||||
{"DACL", NULL, "DACL HiLo VREF"},
|
||||
{"DACL", NULL, "DACL CLK"},
|
||||
{"DACR", NULL, "DACR VREF"},
|
||||
{"DACR", NULL, "DACR HiLo VREF"},
|
||||
{"DACR", NULL, "DACR CLK"},
|
||||
|
||||
{"Left Headphone Mixer", "DAC Left Out Switch", "DACL"},
|
||||
{"Right Headphone Mixer", "DAC Right Out Switch", "DACR"},
|
||||
{"HP Left Out", NULL, "Left Headphone Mixer"},
|
||||
{"HP Right Out", NULL, "Right Headphone Mixer"},
|
||||
|
||||
{"HP Left Switch", "HP Left Out Switch", "HP Left Out"},
|
||||
{"HP Right Switch", "HP Right Out Switch", "HP Right Out"},
|
||||
|
||||
{"HPL", NULL, "HP Left Switch"},
|
||||
{"HPR", NULL, "HP Right Switch"},
|
||||
};
|
||||
|
||||
static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0;
|
||||
|
||||
dev_dbg(codec->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
reg01_val |= INNO_R01_PINDIR_IN_SLAVE |
|
||||
INNO_R01_I2SMODE_SLAVE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
|
||||
INNO_R01_I2SMODE_MASTER;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "invalid fmt\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
reg02_val |= INNO_R02_DACM_PCM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
reg02_val |= INNO_R02_DACM_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
reg02_val |= INNO_R02_DACM_RJM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
reg02_val |= INNO_R02_DACM_LJM;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "set dai format failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
reg02_val |= INNO_R02_LRCP_NORMAL;
|
||||
reg03_val |= INNO_R03_BCP_NORMAL;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
reg02_val |= INNO_R02_LRCP_REVERSAL;
|
||||
reg03_val |= INNO_R03_BCP_REVERSAL;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
reg02_val |= INNO_R02_LRCP_REVERSAL;
|
||||
reg03_val |= INNO_R03_BCP_NORMAL;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
reg02_val |= INNO_R02_LRCP_NORMAL;
|
||||
reg03_val |= INNO_R03_BCP_REVERSAL;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "set dai format failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, INNO_R01, INNO_R01_I2SMODE_MSK |
|
||||
INNO_R01_PINDIR_MSK, reg01_val);
|
||||
snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
|
||||
INNO_R02_DACM_MSK, reg02_val);
|
||||
snd_soc_update_bits(codec, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
unsigned int reg02_val = 0, reg03_val = 0;
|
||||
|
||||
switch (params_format(hw_params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
reg02_val |= INNO_R02_VWL_16BIT;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
reg02_val |= INNO_R02_VWL_20BIT;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
reg02_val |= INNO_R02_VWL_24BIT;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
reg02_val |= INNO_R02_VWL_32BIT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg02_val |= INNO_R02_LRCP_NORMAL;
|
||||
reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
|
||||
|
||||
snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
|
||||
INNO_R02_VWL_MSK, reg02_val);
|
||||
snd_soc_update_bits(codec, INNO_R03, INNO_R03_DACR_MSK |
|
||||
INNO_R03_FWL_MSK, reg03_val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000 | \
|
||||
SNDRV_PCM_RATE_16000 | \
|
||||
SNDRV_PCM_RATE_32000 | \
|
||||
SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | \
|
||||
SNDRV_PCM_RATE_96000)
|
||||
|
||||
#define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_ops rk3036_codec_dai_ops = {
|
||||
.set_fmt = rk3036_codec_dai_set_fmt,
|
||||
.hw_params = rk3036_codec_dai_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
|
||||
{
|
||||
.name = "rk3036-codec-dai",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = RK3036_CODEC_RATES,
|
||||
.formats = RK3036_CODEC_FMTS,
|
||||
},
|
||||
.ops = &rk3036_codec_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static void rk3036_codec_reset(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_soc_write(codec, INNO_R00,
|
||||
INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
|
||||
snd_soc_write(codec, INNO_R00,
|
||||
INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
|
||||
}
|
||||
|
||||
static int rk3036_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
rk3036_codec_reset(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_codec_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
rk3036_codec_reset(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_codec_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
/* set a big current for capacitor charging. */
|
||||
snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
|
||||
/* start precharge */
|
||||
snd_soc_write(codec, INNO_R06, INNO_R06_DAC_PRECHARGE);
|
||||
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
/* set a big current for capacitor discharging. */
|
||||
snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
|
||||
/* start discharge. */
|
||||
snd_soc_write(codec, INNO_R06, INNO_R06_DAC_DISCHARGE);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver rk3036_codec_driver = {
|
||||
.probe = rk3036_codec_probe,
|
||||
.remove = rk3036_codec_remove,
|
||||
.set_bias_level = rk3036_codec_set_bias_level,
|
||||
.controls = rk3036_codec_dapm_controls,
|
||||
.num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls),
|
||||
.dapm_routes = rk3036_codec_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes),
|
||||
.dapm_widgets = rk3036_codec_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets),
|
||||
};
|
||||
|
||||
static const struct regmap_config rk3036_codec_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
};
|
||||
|
||||
#define GRF_SOC_CON0 0x00140
|
||||
#define GRF_ACODEC_SEL (BIT(10) | BIT(16 + 10))
|
||||
|
||||
static int rk3036_codec_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rk3036_codec_priv *priv;
|
||||
struct device_node *of_node = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
struct regmap *grf;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->base = base;
|
||||
priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
|
||||
&rk3036_codec_regmap_config);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
dev_err(&pdev->dev, "init regmap failed\n");
|
||||
return PTR_ERR(priv->regmap);
|
||||
}
|
||||
|
||||
grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf");
|
||||
if (IS_ERR(grf)) {
|
||||
dev_err(&pdev->dev, "needs 'rockchip,grf' property\n");
|
||||
return PTR_ERR(grf);
|
||||
}
|
||||
ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk");
|
||||
if (IS_ERR(priv->pclk))
|
||||
return PTR_ERR(priv->pclk);
|
||||
|
||||
ret = clk_prepare_enable(priv->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
ret = snd_soc_register_codec(&pdev->dev, &rk3036_codec_driver,
|
||||
rk3036_codec_dai_driver,
|
||||
ARRAY_SIZE(rk3036_codec_dai_driver));
|
||||
if (ret) {
|
||||
clk_disable_unprepare(priv->pclk);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rk3036_codec_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
clk_disable_unprepare(priv->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rk3036_codec_of_match[] = {
|
||||
{ .compatible = "rockchip,rk3036-codec", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rk3036_codec_of_match);
|
||||
|
||||
static struct platform_driver rk3036_codec_platform_driver = {
|
||||
.driver = {
|
||||
.name = "rk3036-codec-platform",
|
||||
.of_match_table = of_match_ptr(rk3036_codec_of_match),
|
||||
},
|
||||
.probe = rk3036_codec_platform_probe,
|
||||
.remove = rk3036_codec_platform_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(rk3036_codec_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Rockchip Inc.");
|
||||
MODULE_DESCRIPTION("Rockchip rk3036 codec driver");
|
||||
MODULE_LICENSE("GPL");
|
123
sound/soc/codecs/inno_rk3036.h
Normal file
123
sound/soc/codecs/inno_rk3036.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Driver of Inno Codec for rk3036 by Rockchip Inc.
|
||||
*
|
||||
* Author: Zheng ShunQian<zhengsq@rock-chips.com>
|
||||
*/
|
||||
|
||||
#ifndef _INNO_RK3036_CODEC_H
|
||||
#define _INNO_RK3036_CODEC_H
|
||||
|
||||
/* codec registers */
|
||||
#define INNO_R00 0x00
|
||||
#define INNO_R01 0x0c
|
||||
#define INNO_R02 0x10
|
||||
#define INNO_R03 0x14
|
||||
#define INNO_R04 0x88
|
||||
#define INNO_R05 0x8c
|
||||
#define INNO_R06 0x90
|
||||
#define INNO_R07 0x94
|
||||
#define INNO_R08 0x98
|
||||
#define INNO_R09 0x9c
|
||||
#define INNO_R10 0xa0
|
||||
|
||||
/* register bit filed */
|
||||
#define INNO_R00_CSR_RESET (0x0 << 0) /*codec system reset*/
|
||||
#define INNO_R00_CSR_WORK (0x1 << 0)
|
||||
#define INNO_R00_CDCR_RESET (0x0 << 1) /*codec digital core reset*/
|
||||
#define INNO_R00_CDCR_WORK (0x1 << 1)
|
||||
#define INNO_R00_PRB_DISABLE (0x0 << 6) /*power reset bypass*/
|
||||
#define INNO_R00_PRB_ENABLE (0x1 << 6)
|
||||
|
||||
#define INNO_R01_I2SMODE_MSK (0x1 << 4)
|
||||
#define INNO_R01_I2SMODE_SLAVE (0x0 << 4)
|
||||
#define INNO_R01_I2SMODE_MASTER (0x1 << 4)
|
||||
#define INNO_R01_PINDIR_MSK (0x1 << 5)
|
||||
#define INNO_R01_PINDIR_IN_SLAVE (0x0 << 5) /*direction of pin*/
|
||||
#define INNO_R01_PINDIR_OUT_MASTER (0x1 << 5)
|
||||
|
||||
#define INNO_R02_LRS_MSK (0x1 << 2)
|
||||
#define INNO_R02_LRS_NORMAL (0x0 << 2) /*DAC Left Right Swap*/
|
||||
#define INNO_R02_LRS_SWAP (0x1 << 2)
|
||||
#define INNO_R02_DACM_MSK (0x3 << 3)
|
||||
#define INNO_R02_DACM_PCM (0x3 << 3) /*DAC Mode*/
|
||||
#define INNO_R02_DACM_I2S (0x2 << 3)
|
||||
#define INNO_R02_DACM_LJM (0x1 << 3)
|
||||
#define INNO_R02_DACM_RJM (0x0 << 3)
|
||||
#define INNO_R02_VWL_MSK (0x3 << 5)
|
||||
#define INNO_R02_VWL_32BIT (0x3 << 5) /*1/2Frame Valid Word Len*/
|
||||
#define INNO_R02_VWL_24BIT (0x2 << 5)
|
||||
#define INNO_R02_VWL_20BIT (0x1 << 5)
|
||||
#define INNO_R02_VWL_16BIT (0x0 << 5)
|
||||
#define INNO_R02_LRCP_MSK (0x1 << 7)
|
||||
#define INNO_R02_LRCP_NORMAL (0x0 << 7) /*Left Right Polarity*/
|
||||
#define INNO_R02_LRCP_REVERSAL (0x1 << 7)
|
||||
|
||||
#define INNO_R03_BCP_MSK (0x1 << 0)
|
||||
#define INNO_R03_BCP_NORMAL (0x0 << 0) /*DAC bit clock polarity*/
|
||||
#define INNO_R03_BCP_REVERSAL (0x1 << 0)
|
||||
#define INNO_R03_DACR_MSK (0x1 << 1)
|
||||
#define INNO_R03_DACR_RESET (0x0 << 1) /*DAC Reset*/
|
||||
#define INNO_R03_DACR_WORK (0x1 << 1)
|
||||
#define INNO_R03_FWL_MSK (0x3 << 2)
|
||||
#define INNO_R03_FWL_32BIT (0x3 << 2) /*1/2Frame Word Length*/
|
||||
#define INNO_R03_FWL_24BIT (0x2 << 2)
|
||||
#define INNO_R03_FWL_20BIT (0x1 << 2)
|
||||
#define INNO_R03_FWL_16BIT (0x0 << 2)
|
||||
|
||||
#define INNO_R04_DACR_SW_SHIFT 0
|
||||
#define INNO_R04_DACL_SW_SHIFT 1
|
||||
#define INNO_R04_DACR_CLK_SHIFT 2
|
||||
#define INNO_R04_DACL_CLK_SHIFT 3
|
||||
#define INNO_R04_DACR_VREF_SHIFT 4
|
||||
#define INNO_R04_DACL_VREF_SHIFT 5
|
||||
|
||||
#define INNO_R05_HPR_EN_SHIFT 0
|
||||
#define INNO_R05_HPL_EN_SHIFT 1
|
||||
#define INNO_R05_HPR_WORK_SHIFT 2
|
||||
#define INNO_R05_HPL_WORK_SHIFT 3
|
||||
|
||||
#define INNO_R06_VOUTR_CZ_SHIFT 0
|
||||
#define INNO_R06_VOUTL_CZ_SHIFT 1
|
||||
#define INNO_R06_DACR_HILO_VREF_SHIFT 2
|
||||
#define INNO_R06_DACL_HILO_VREF_SHIFT 3
|
||||
#define INNO_R06_DAC_EN_SHIFT 5
|
||||
|
||||
#define INNO_R06_DAC_PRECHARGE (0x0 << 4) /*PreCharge control for DAC*/
|
||||
#define INNO_R06_DAC_DISCHARGE (0x1 << 4)
|
||||
|
||||
#define INNO_HP_GAIN_SHIFT 0
|
||||
/* Gain of output, 1.5db step: -39db(0x0) ~ 0db(0x1a) ~ 6db(0x1f) */
|
||||
#define INNO_HP_GAIN_0DB 0x1a
|
||||
#define INNO_HP_GAIN_N39DB 0x0
|
||||
|
||||
#define INNO_R09_HP_ANTIPOP_MSK 0x3
|
||||
#define INNO_R09_HP_ANTIPOP_OFF 0x1
|
||||
#define INNO_R09_HP_ANTIPOP_ON 0x2
|
||||
#define INNO_R09_HPR_ANITPOP_SHIFT 0
|
||||
#define INNO_R09_HPL_ANITPOP_SHIFT 2
|
||||
#define INNO_R09_HPR_MUTE_SHIFT 4
|
||||
#define INNO_R09_HPL_MUTE_SHIFT 5
|
||||
#define INNO_R09_DACR_SWITCH_SHIFT 6
|
||||
#define INNO_R09_DACL_SWITCH_SHIFT 7
|
||||
|
||||
#define INNO_R10_CHARGE_SEL_CUR_400I_YES (0x0 << 0)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_400I_NO (0x1 << 0)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_260I_YES (0x0 << 1)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_260I_NO (0x1 << 1)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_130I_YES (0x0 << 2)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_130I_NO (0x1 << 2)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_100I_YES (0x0 << 3)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_100I_NO (0x1 << 3)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_050I_YES (0x0 << 4)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_050I_NO (0x1 << 4)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_027I_YES (0x0 << 5)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_027I_NO (0x1 << 5)
|
||||
|
||||
#define INNO_R10_MAX_CUR (INNO_R10_CHARGE_SEL_CUR_400I_YES | \
|
||||
INNO_R10_CHARGE_SEL_CUR_260I_YES | \
|
||||
INNO_R10_CHARGE_SEL_CUR_130I_YES | \
|
||||
INNO_R10_CHARGE_SEL_CUR_100I_YES | \
|
||||
INNO_R10_CHARGE_SEL_CUR_050I_YES | \
|
||||
INNO_R10_CHARGE_SEL_CUR_027I_YES)
|
||||
|
||||
#endif
|
|
@ -12,6 +12,7 @@
|
|||
* max98357a.c -- MAX98357A ALSA SoC Codec driver
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
|
@ -123,10 +124,19 @@ static const struct of_device_id max98357a_device_id[] = {
|
|||
MODULE_DEVICE_TABLE(of, max98357a_device_id);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id max98357a_acpi_match[] = {
|
||||
{ "MX98357A", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, max98357a_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver max98357a_platform_driver = {
|
||||
.driver = {
|
||||
.name = "max98357a",
|
||||
.of_match_table = of_match_ptr(max98357a_device_id),
|
||||
.acpi_match_table = ACPI_PTR(max98357a_acpi_match),
|
||||
},
|
||||
.probe = max98357a_platform_probe,
|
||||
.remove = max98357a_platform_remove,
|
||||
|
|
|
@ -1,271 +0,0 @@
|
|||
/*
|
||||
* PCM1792A ASoC codec driver
|
||||
*
|
||||
* Copyright (c) Amarula Solutions B.V. 2013
|
||||
*
|
||||
* Michael Trimarchi <michael@amarulasolutions.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "pcm1792a.h"
|
||||
|
||||
#define PCM1792A_DAC_VOL_LEFT 0x10
|
||||
#define PCM1792A_DAC_VOL_RIGHT 0x11
|
||||
#define PCM1792A_FMT_CONTROL 0x12
|
||||
#define PCM1792A_MODE_CONTROL 0x13
|
||||
#define PCM1792A_SOFT_MUTE PCM1792A_FMT_CONTROL
|
||||
|
||||
#define PCM1792A_FMT_MASK 0x70
|
||||
#define PCM1792A_FMT_SHIFT 4
|
||||
#define PCM1792A_MUTE_MASK 0x01
|
||||
#define PCM1792A_MUTE_SHIFT 0
|
||||
#define PCM1792A_ATLD_ENABLE (1 << 7)
|
||||
|
||||
static const struct reg_default pcm1792a_reg_defaults[] = {
|
||||
{ 0x10, 0xff },
|
||||
{ 0x11, 0xff },
|
||||
{ 0x12, 0x50 },
|
||||
{ 0x13, 0x00 },
|
||||
{ 0x14, 0x00 },
|
||||
{ 0x15, 0x01 },
|
||||
{ 0x16, 0x00 },
|
||||
{ 0x17, 0x00 },
|
||||
};
|
||||
|
||||
static bool pcm1792a_accessible_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return reg >= 0x10 && reg <= 0x17;
|
||||
}
|
||||
|
||||
static bool pcm1792a_writeable_reg(struct device *dev, unsigned register reg)
|
||||
{
|
||||
bool accessible;
|
||||
|
||||
accessible = pcm1792a_accessible_reg(dev, reg);
|
||||
|
||||
return accessible && reg != 0x16 && reg != 0x17;
|
||||
}
|
||||
|
||||
struct pcm1792a_private {
|
||||
struct regmap *regmap;
|
||||
unsigned int format;
|
||||
unsigned int rate;
|
||||
};
|
||||
|
||||
static int pcm1792a_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int format)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
priv->format = format;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm1792a_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(priv->regmap, PCM1792A_SOFT_MUTE,
|
||||
PCM1792A_MUTE_MASK, !!mute);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm1792a_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
|
||||
int val = 0, ret;
|
||||
|
||||
priv->rate = params_rate(params);
|
||||
|
||||
switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
switch (params_width(params)) {
|
||||
case 24:
|
||||
case 32:
|
||||
val = 2;
|
||||
break;
|
||||
case 16:
|
||||
val = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
switch (params_width(params)) {
|
||||
case 24:
|
||||
case 32:
|
||||
val = 5;
|
||||
break;
|
||||
case 16:
|
||||
val = 4;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "Invalid DAI format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = val << PCM1792A_FMT_SHIFT | PCM1792A_ATLD_ENABLE;
|
||||
|
||||
ret = regmap_update_bits(priv->regmap, PCM1792A_FMT_CONTROL,
|
||||
PCM1792A_FMT_MASK | PCM1792A_ATLD_ENABLE, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops pcm1792a_dai_ops = {
|
||||
.set_fmt = pcm1792a_set_dai_fmt,
|
||||
.hw_params = pcm1792a_hw_params,
|
||||
.digital_mute = pcm1792a_digital_mute,
|
||||
};
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(pcm1792a_dac_tlv, -12000, 50, 1);
|
||||
|
||||
static const struct snd_kcontrol_new pcm1792a_controls[] = {
|
||||
SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1792A_DAC_VOL_LEFT,
|
||||
PCM1792A_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
|
||||
pcm1792a_dac_tlv),
|
||||
SOC_SINGLE("DAC Invert Output Switch", PCM1792A_MODE_CONTROL, 7, 1, 0),
|
||||
SOC_SINGLE("DAC Rolloff Filter Switch", PCM1792A_MODE_CONTROL, 1, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget pcm1792a_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_OUTPUT("IOUTL+"),
|
||||
SND_SOC_DAPM_OUTPUT("IOUTL-"),
|
||||
SND_SOC_DAPM_OUTPUT("IOUTR+"),
|
||||
SND_SOC_DAPM_OUTPUT("IOUTR-"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route pcm1792a_dapm_routes[] = {
|
||||
{ "IOUTL+", NULL, "Playback" },
|
||||
{ "IOUTL-", NULL, "Playback" },
|
||||
{ "IOUTR+", NULL, "Playback" },
|
||||
{ "IOUTR-", NULL, "Playback" },
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver pcm1792a_dai = {
|
||||
.name = "pcm1792a-hifi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = PCM1792A_RATES,
|
||||
.formats = PCM1792A_FORMATS, },
|
||||
.ops = &pcm1792a_dai_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id pcm1792a_of_match[] = {
|
||||
{ .compatible = "ti,pcm1792a", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcm1792a_of_match);
|
||||
|
||||
static const struct regmap_config pcm1792a_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 23,
|
||||
.reg_defaults = pcm1792a_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(pcm1792a_reg_defaults),
|
||||
.writeable_reg = pcm1792a_writeable_reg,
|
||||
.readable_reg = pcm1792a_accessible_reg,
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = {
|
||||
.controls = pcm1792a_controls,
|
||||
.num_controls = ARRAY_SIZE(pcm1792a_controls),
|
||||
.dapm_widgets = pcm1792a_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(pcm1792a_dapm_widgets),
|
||||
.dapm_routes = pcm1792a_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(pcm1792a_dapm_routes),
|
||||
};
|
||||
|
||||
static int pcm1792a_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct pcm1792a_private *pcm1792a;
|
||||
int ret;
|
||||
|
||||
pcm1792a = devm_kzalloc(&spi->dev, sizeof(struct pcm1792a_private),
|
||||
GFP_KERNEL);
|
||||
if (!pcm1792a)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, pcm1792a);
|
||||
|
||||
pcm1792a->regmap = devm_regmap_init_spi(spi, &pcm1792a_regmap);
|
||||
if (IS_ERR(pcm1792a->regmap)) {
|
||||
ret = PTR_ERR(pcm1792a->regmap);
|
||||
dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return snd_soc_register_codec(&spi->dev,
|
||||
&soc_codec_dev_pcm1792a, &pcm1792a_dai, 1);
|
||||
}
|
||||
|
||||
static int pcm1792a_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
snd_soc_unregister_codec(&spi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id pcm1792a_spi_ids[] = {
|
||||
{ "pcm1792a", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, pcm1792a_spi_ids);
|
||||
|
||||
static struct spi_driver pcm1792a_codec_driver = {
|
||||
.driver = {
|
||||
.name = "pcm1792a",
|
||||
.of_match_table = of_match_ptr(pcm1792a_of_match),
|
||||
},
|
||||
.id_table = pcm1792a_spi_ids,
|
||||
.probe = pcm1792a_spi_probe,
|
||||
.remove = pcm1792a_spi_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(pcm1792a_codec_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC PCM1792A driver");
|
||||
MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
|
||||
MODULE_LICENSE("GPL");
|
271
sound/soc/codecs/pcm179x.c
Normal file
271
sound/soc/codecs/pcm179x.c
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* PCM179X ASoC codec driver
|
||||
*
|
||||
* Copyright (c) Amarula Solutions B.V. 2013
|
||||
*
|
||||
* Michael Trimarchi <michael@amarulasolutions.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "pcm179x.h"
|
||||
|
||||
#define PCM179X_DAC_VOL_LEFT 0x10
|
||||
#define PCM179X_DAC_VOL_RIGHT 0x11
|
||||
#define PCM179X_FMT_CONTROL 0x12
|
||||
#define PCM179X_MODE_CONTROL 0x13
|
||||
#define PCM179X_SOFT_MUTE PCM179X_FMT_CONTROL
|
||||
|
||||
#define PCM179X_FMT_MASK 0x70
|
||||
#define PCM179X_FMT_SHIFT 4
|
||||
#define PCM179X_MUTE_MASK 0x01
|
||||
#define PCM179X_MUTE_SHIFT 0
|
||||
#define PCM179X_ATLD_ENABLE (1 << 7)
|
||||
|
||||
static const struct reg_default pcm179x_reg_defaults[] = {
|
||||
{ 0x10, 0xff },
|
||||
{ 0x11, 0xff },
|
||||
{ 0x12, 0x50 },
|
||||
{ 0x13, 0x00 },
|
||||
{ 0x14, 0x00 },
|
||||
{ 0x15, 0x01 },
|
||||
{ 0x16, 0x00 },
|
||||
{ 0x17, 0x00 },
|
||||
};
|
||||
|
||||
static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return reg >= 0x10 && reg <= 0x17;
|
||||
}
|
||||
|
||||
static bool pcm179x_writeable_reg(struct device *dev, unsigned register reg)
|
||||
{
|
||||
bool accessible;
|
||||
|
||||
accessible = pcm179x_accessible_reg(dev, reg);
|
||||
|
||||
return accessible && reg != 0x16 && reg != 0x17;
|
||||
}
|
||||
|
||||
struct pcm179x_private {
|
||||
struct regmap *regmap;
|
||||
unsigned int format;
|
||||
unsigned int rate;
|
||||
};
|
||||
|
||||
static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int format)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
priv->format = format;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm179x_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(priv->regmap, PCM179X_SOFT_MUTE,
|
||||
PCM179X_MUTE_MASK, !!mute);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm179x_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
|
||||
int val = 0, ret;
|
||||
|
||||
priv->rate = params_rate(params);
|
||||
|
||||
switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
switch (params_width(params)) {
|
||||
case 24:
|
||||
case 32:
|
||||
val = 2;
|
||||
break;
|
||||
case 16:
|
||||
val = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
switch (params_width(params)) {
|
||||
case 24:
|
||||
case 32:
|
||||
val = 5;
|
||||
break;
|
||||
case 16:
|
||||
val = 4;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "Invalid DAI format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = val << PCM179X_FMT_SHIFT | PCM179X_ATLD_ENABLE;
|
||||
|
||||
ret = regmap_update_bits(priv->regmap, PCM179X_FMT_CONTROL,
|
||||
PCM179X_FMT_MASK | PCM179X_ATLD_ENABLE, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops pcm179x_dai_ops = {
|
||||
.set_fmt = pcm179x_set_dai_fmt,
|
||||
.hw_params = pcm179x_hw_params,
|
||||
.digital_mute = pcm179x_digital_mute,
|
||||
};
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(pcm179x_dac_tlv, -12000, 50, 1);
|
||||
|
||||
static const struct snd_kcontrol_new pcm179x_controls[] = {
|
||||
SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM179X_DAC_VOL_LEFT,
|
||||
PCM179X_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
|
||||
pcm179x_dac_tlv),
|
||||
SOC_SINGLE("DAC Invert Output Switch", PCM179X_MODE_CONTROL, 7, 1, 0),
|
||||
SOC_SINGLE("DAC Rolloff Filter Switch", PCM179X_MODE_CONTROL, 1, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget pcm179x_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_OUTPUT("IOUTL+"),
|
||||
SND_SOC_DAPM_OUTPUT("IOUTL-"),
|
||||
SND_SOC_DAPM_OUTPUT("IOUTR+"),
|
||||
SND_SOC_DAPM_OUTPUT("IOUTR-"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route pcm179x_dapm_routes[] = {
|
||||
{ "IOUTL+", NULL, "Playback" },
|
||||
{ "IOUTL-", NULL, "Playback" },
|
||||
{ "IOUTR+", NULL, "Playback" },
|
||||
{ "IOUTR-", NULL, "Playback" },
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver pcm179x_dai = {
|
||||
.name = "pcm179x-hifi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = PCM1792A_RATES,
|
||||
.formats = PCM1792A_FORMATS, },
|
||||
.ops = &pcm179x_dai_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id pcm179x_of_match[] = {
|
||||
{ .compatible = "ti,pcm1792a", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcm179x_of_match);
|
||||
|
||||
static const struct regmap_config pcm179x_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 23,
|
||||
.reg_defaults = pcm179x_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(pcm179x_reg_defaults),
|
||||
.writeable_reg = pcm179x_writeable_reg,
|
||||
.readable_reg = pcm179x_accessible_reg,
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_pcm179x = {
|
||||
.controls = pcm179x_controls,
|
||||
.num_controls = ARRAY_SIZE(pcm179x_controls),
|
||||
.dapm_widgets = pcm179x_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(pcm179x_dapm_widgets),
|
||||
.dapm_routes = pcm179x_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(pcm179x_dapm_routes),
|
||||
};
|
||||
|
||||
static int pcm179x_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct pcm179x_private *pcm179x;
|
||||
int ret;
|
||||
|
||||
pcm179x = devm_kzalloc(&spi->dev, sizeof(struct pcm179x_private),
|
||||
GFP_KERNEL);
|
||||
if (!pcm179x)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, pcm179x);
|
||||
|
||||
pcm179x->regmap = devm_regmap_init_spi(spi, &pcm179x_regmap);
|
||||
if (IS_ERR(pcm179x->regmap)) {
|
||||
ret = PTR_ERR(pcm179x->regmap);
|
||||
dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return snd_soc_register_codec(&spi->dev,
|
||||
&soc_codec_dev_pcm179x, &pcm179x_dai, 1);
|
||||
}
|
||||
|
||||
static int pcm179x_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
snd_soc_unregister_codec(&spi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id pcm179x_spi_ids[] = {
|
||||
{ "pcm179x", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids);
|
||||
|
||||
static struct spi_driver pcm179x_codec_driver = {
|
||||
.driver = {
|
||||
.name = "pcm179x",
|
||||
.of_match_table = of_match_ptr(pcm179x_of_match),
|
||||
},
|
||||
.id_table = pcm179x_spi_ids,
|
||||
.probe = pcm179x_spi_probe,
|
||||
.remove = pcm179x_spi_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(pcm179x_codec_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC PCM179X driver");
|
||||
MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* definitions for PCM1792A
|
||||
* definitions for PCM179X
|
||||
*
|
||||
* Copyright 2013 Amarula Solutions
|
||||
*
|
||||
|
@ -14,8 +14,8 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __PCM1792A_H__
|
||||
#define __PCM1792A_H__
|
||||
#ifndef __PCM179X_H__
|
||||
#define __PCM179X_H__
|
||||
|
||||
#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \
|
||||
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
|
66
sound/soc/codecs/pcm3168a-i2c.c
Normal file
66
sound/soc/codecs/pcm3168a-i2c.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* PCM3168A codec i2c driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "pcm3168a.h"
|
||||
|
||||
static int pcm3168a_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &pcm3168a_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return pcm3168a_probe(&i2c->dev, regmap);
|
||||
}
|
||||
|
||||
static int pcm3168a_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
pcm3168a_remove(&i2c->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id pcm3168a_i2c_id[] = {
|
||||
{ "pcm3168a", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
|
||||
|
||||
static const struct of_device_id pcm3168a_of_match[] = {
|
||||
{ .compatible = "ti,pcm3168a", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
|
||||
|
||||
static struct i2c_driver pcm3168a_i2c_driver = {
|
||||
.probe = pcm3168a_i2c_probe,
|
||||
.remove = pcm3168a_i2c_remove,
|
||||
.id_table = pcm3168a_i2c_id,
|
||||
.driver = {
|
||||
.name = "pcm3168a",
|
||||
.of_match_table = pcm3168a_of_match,
|
||||
.pm = &pcm3168a_pm_ops,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(pcm3168a_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PCM3168A I2C codec driver");
|
||||
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
65
sound/soc/codecs/pcm3168a-spi.c
Normal file
65
sound/soc/codecs/pcm3168a-spi.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* PCM3168A codec spi driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "pcm3168a.h"
|
||||
|
||||
static int pcm3168a_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &pcm3168a_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return pcm3168a_probe(&spi->dev, regmap);
|
||||
}
|
||||
|
||||
static int pcm3168a_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
pcm3168a_remove(&spi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id pcm3168a_spi_id[] = {
|
||||
{ "pcm3168a", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, pcm3168a_spi_id);
|
||||
|
||||
static const struct of_device_id pcm3168a_of_match[] = {
|
||||
{ .compatible = "ti,pcm3168a", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
|
||||
|
||||
static struct spi_driver pcm3168a_spi_driver = {
|
||||
.probe = pcm3168a_spi_probe,
|
||||
.remove = pcm3168a_spi_remove,
|
||||
.id_table = pcm3168a_spi_id,
|
||||
.driver = {
|
||||
.name = "pcm3168a",
|
||||
.of_match_table = pcm3168a_of_match,
|
||||
.pm = &pcm3168a_pm_ops,
|
||||
},
|
||||
};
|
||||
module_spi_driver(pcm3168a_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PCM3168A SPI codec driver");
|
||||
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
767
sound/soc/codecs/pcm3168a.c
Normal file
767
sound/soc/codecs/pcm3168a.c
Normal file
|
@ -0,0 +1,767 @@
|
|||
/*
|
||||
* PCM3168A codec driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "pcm3168a.h"
|
||||
|
||||
#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
#define PCM3168A_FMT_I2S 0x0
|
||||
#define PCM3168A_FMT_LEFT_J 0x1
|
||||
#define PCM3168A_FMT_RIGHT_J 0x2
|
||||
#define PCM3168A_FMT_RIGHT_J_16 0x3
|
||||
#define PCM3168A_FMT_DSP_A 0x4
|
||||
#define PCM3168A_FMT_DSP_B 0x5
|
||||
#define PCM3168A_FMT_DSP_MASK 0x4
|
||||
|
||||
#define PCM3168A_NUM_SUPPLIES 6
|
||||
static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
|
||||
"VDD1",
|
||||
"VDD2",
|
||||
"VCCAD1",
|
||||
"VCCAD2",
|
||||
"VCCDA1",
|
||||
"VCCDA2"
|
||||
};
|
||||
|
||||
struct pcm3168a_priv {
|
||||
struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
|
||||
struct regmap *regmap;
|
||||
struct clk *scki;
|
||||
bool adc_master_mode;
|
||||
bool dac_master_mode;
|
||||
unsigned long sysclk;
|
||||
unsigned int adc_fmt;
|
||||
unsigned int dac_fmt;
|
||||
};
|
||||
|
||||
static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_d1_roll_off, PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_FLT_SHIFT, pcm3168a_roll_off);
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_d2_roll_off, PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_FLT_SHIFT + 1, pcm3168a_roll_off);
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_d3_roll_off, PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_FLT_SHIFT + 2, pcm3168a_roll_off);
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_d4_roll_off, PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_FLT_SHIFT + 3, pcm3168a_roll_off);
|
||||
|
||||
static const char *const pcm3168a_volume_type[] = {
|
||||
"Individual", "Master + Individual" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_volume_type, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_ATMDDA_SHIFT, pcm3168a_volume_type);
|
||||
|
||||
static const char *const pcm3168a_att_speed_mult[] = { "2048", "4096" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_att_mult, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_att_speed_mult);
|
||||
|
||||
static const char *const pcm3168a_demp[] = {
|
||||
"Disabled", "48khz", "44.1khz", "32khz" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_demp, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_DEMP_SHIFT, pcm3168a_demp);
|
||||
|
||||
static const char *const pcm3168a_zf_func[] = {
|
||||
"DAC 1/2/3/4 AND", "DAC 1/2/3/4 OR", "DAC 1/2/3 AND",
|
||||
"DAC 1/2/3 OR", "DAC 4 AND", "DAC 4 OR" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_AZRO_SHIFT, pcm3168a_zf_func);
|
||||
|
||||
static const char *const pcm3168a_pol[] = { "Active High", "Active Low" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol);
|
||||
|
||||
static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" };
|
||||
|
||||
static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc1_con, PCM3168A_ADC_SEAD,
|
||||
0, 1, pcm3168a_con);
|
||||
static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc2_con, PCM3168A_ADC_SEAD,
|
||||
2, 3, pcm3168a_con);
|
||||
static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc3_con, PCM3168A_ADC_SEAD,
|
||||
4, 5, pcm3168a_con);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_volume_type, PCM3168A_ADC_ATT_OVF,
|
||||
PCM3168A_ADC_ATMDAD_SHIFT, pcm3168a_volume_type);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_att_mult, PCM3168A_ADC_ATT_OVF,
|
||||
PCM3168A_ADC_ATSPAD_SHIFT, pcm3168a_att_speed_mult);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_ov_pol, PCM3168A_ADC_ATT_OVF,
|
||||
PCM3168A_ADC_OVFP_SHIFT, pcm3168a_pol);
|
||||
|
||||
/* -100db to 0db, register values 0-54 cause mute */
|
||||
static const DECLARE_TLV_DB_SCALE(pcm3168a_dac_tlv, -10050, 50, 1);
|
||||
|
||||
/* -100db to 20db, register values 0-14 cause mute */
|
||||
static const DECLARE_TLV_DB_SCALE(pcm3168a_adc_tlv, -10050, 50, 1);
|
||||
|
||||
static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
|
||||
SOC_SINGLE("DAC Power-Save Switch", PCM3168A_DAC_PWR_MST_FMT,
|
||||
PCM3168A_DAC_PSMDA_SHIFT, 1, 1),
|
||||
SOC_ENUM("DAC1 Digital Filter roll-off", pcm3168a_d1_roll_off),
|
||||
SOC_ENUM("DAC2 Digital Filter roll-off", pcm3168a_d2_roll_off),
|
||||
SOC_ENUM("DAC3 Digital Filter roll-off", pcm3168a_d3_roll_off),
|
||||
SOC_ENUM("DAC4 Digital Filter roll-off", pcm3168a_d4_roll_off),
|
||||
SOC_DOUBLE("DAC1 Invert Switch", PCM3168A_DAC_INV, 0, 1, 1, 0),
|
||||
SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0),
|
||||
SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0),
|
||||
SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0),
|
||||
SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0),
|
||||
SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0),
|
||||
SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0),
|
||||
SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0),
|
||||
SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type),
|
||||
SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult),
|
||||
SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp),
|
||||
SOC_ENUM("DAC Zero Flag Function", pcm3168a_dac_zf_func),
|
||||
SOC_ENUM("DAC Zero Flag Polarity", pcm3168a_dac_zf_pol),
|
||||
SOC_SINGLE_RANGE_TLV("Master Playback Volume",
|
||||
PCM3168A_DAC_VOL_MASTER, 0, 54, 255, 0,
|
||||
pcm3168a_dac_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("DAC1 Playback Volume",
|
||||
PCM3168A_DAC_VOL_CHAN_START,
|
||||
PCM3168A_DAC_VOL_CHAN_START + 1,
|
||||
0, 54, 255, 0, pcm3168a_dac_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("DAC2 Playback Volume",
|
||||
PCM3168A_DAC_VOL_CHAN_START + 2,
|
||||
PCM3168A_DAC_VOL_CHAN_START + 3,
|
||||
0, 54, 255, 0, pcm3168a_dac_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("DAC3 Playback Volume",
|
||||
PCM3168A_DAC_VOL_CHAN_START + 4,
|
||||
PCM3168A_DAC_VOL_CHAN_START + 5,
|
||||
0, 54, 255, 0, pcm3168a_dac_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("DAC4 Playback Volume",
|
||||
PCM3168A_DAC_VOL_CHAN_START + 6,
|
||||
PCM3168A_DAC_VOL_CHAN_START + 7,
|
||||
0, 54, 255, 0, pcm3168a_dac_tlv),
|
||||
SOC_SINGLE("ADC1 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_BYP_SHIFT, 1, 1),
|
||||
SOC_SINGLE("ADC2 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_BYP_SHIFT + 1, 1, 1),
|
||||
SOC_SINGLE("ADC3 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_BYP_SHIFT + 2, 1, 1),
|
||||
SOC_ENUM("ADC1 Connection Type", pcm3168a_adc1_con),
|
||||
SOC_ENUM("ADC2 Connection Type", pcm3168a_adc2_con),
|
||||
SOC_ENUM("ADC3 Connection Type", pcm3168a_adc3_con),
|
||||
SOC_DOUBLE("ADC1 Invert Switch", PCM3168A_ADC_INV, 0, 1, 1, 0),
|
||||
SOC_DOUBLE("ADC2 Invert Switch", PCM3168A_ADC_INV, 2, 3, 1, 0),
|
||||
SOC_DOUBLE("ADC3 Invert Switch", PCM3168A_ADC_INV, 4, 5, 1, 0),
|
||||
SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0),
|
||||
SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0),
|
||||
SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0),
|
||||
SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0),
|
||||
SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0),
|
||||
SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0),
|
||||
SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type),
|
||||
SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult),
|
||||
SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol),
|
||||
SOC_SINGLE_RANGE_TLV("Master Capture Volume",
|
||||
PCM3168A_ADC_VOL_MASTER, 0, 14, 255, 0,
|
||||
pcm3168a_adc_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("ADC1 Capture Volume",
|
||||
PCM3168A_ADC_VOL_CHAN_START,
|
||||
PCM3168A_ADC_VOL_CHAN_START + 1,
|
||||
0, 14, 255, 0, pcm3168a_adc_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("ADC2 Capture Volume",
|
||||
PCM3168A_ADC_VOL_CHAN_START + 2,
|
||||
PCM3168A_ADC_VOL_CHAN_START + 3,
|
||||
0, 14, 255, 0, pcm3168a_adc_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("ADC3 Capture Volume",
|
||||
PCM3168A_ADC_VOL_CHAN_START + 4,
|
||||
PCM3168A_ADC_VOL_CHAN_START + 5,
|
||||
0, 14, 255, 0, pcm3168a_adc_tlv)
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget pcm3168a_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DAC1", "Playback", PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_OPEDA_SHIFT, 1),
|
||||
SND_SOC_DAPM_DAC("DAC2", "Playback", PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_OPEDA_SHIFT + 1, 1),
|
||||
SND_SOC_DAPM_DAC("DAC3", "Playback", PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_OPEDA_SHIFT + 2, 1),
|
||||
SND_SOC_DAPM_DAC("DAC4", "Playback", PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_OPEDA_SHIFT + 3, 1),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("AOUT1L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT1R"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT2L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT2R"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT3L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT3R"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT4L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT4R"),
|
||||
|
||||
SND_SOC_DAPM_ADC("ADC1", "Capture", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_PSVAD_SHIFT, 1),
|
||||
SND_SOC_DAPM_ADC("ADC2", "Capture", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_PSVAD_SHIFT + 1, 1),
|
||||
SND_SOC_DAPM_ADC("ADC3", "Capture", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_PSVAD_SHIFT + 2, 1),
|
||||
|
||||
SND_SOC_DAPM_INPUT("AIN1L"),
|
||||
SND_SOC_DAPM_INPUT("AIN1R"),
|
||||
SND_SOC_DAPM_INPUT("AIN2L"),
|
||||
SND_SOC_DAPM_INPUT("AIN2R"),
|
||||
SND_SOC_DAPM_INPUT("AIN3L"),
|
||||
SND_SOC_DAPM_INPUT("AIN3R")
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route pcm3168a_dapm_routes[] = {
|
||||
/* Playback */
|
||||
{ "AOUT1L", NULL, "DAC1" },
|
||||
{ "AOUT1R", NULL, "DAC1" },
|
||||
|
||||
{ "AOUT2L", NULL, "DAC2" },
|
||||
{ "AOUT2R", NULL, "DAC2" },
|
||||
|
||||
{ "AOUT3L", NULL, "DAC3" },
|
||||
{ "AOUT3R", NULL, "DAC3" },
|
||||
|
||||
{ "AOUT4L", NULL, "DAC4" },
|
||||
{ "AOUT4R", NULL, "DAC4" },
|
||||
|
||||
/* Capture */
|
||||
{ "ADC1", NULL, "AIN1L" },
|
||||
{ "ADC1", NULL, "AIN1R" },
|
||||
|
||||
{ "ADC2", NULL, "AIN2L" },
|
||||
{ "ADC2", NULL, "AIN2R" },
|
||||
|
||||
{ "ADC3", NULL, "AIN3L" },
|
||||
{ "ADC3", NULL, "AIN3R" }
|
||||
};
|
||||
|
||||
static unsigned int pcm3168a_scki_ratios[] = {
|
||||
768,
|
||||
512,
|
||||
384,
|
||||
256,
|
||||
192,
|
||||
128
|
||||
};
|
||||
|
||||
#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios)
|
||||
#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2)
|
||||
|
||||
#define PCM1368A_MAX_SYSCLK 36864000
|
||||
|
||||
static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Internal reset is de-asserted after 3846 SCKI cycles */
|
||||
msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
|
||||
|
||||
return regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE,
|
||||
PCM3168A_MRST_MASK | PCM3168A_SRST_MASK);
|
||||
}
|
||||
|
||||
static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec);
|
||||
|
||||
if (freq > PCM1368A_MAX_SYSCLK)
|
||||
return -EINVAL;
|
||||
|
||||
pcm3168a->sysclk = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
|
||||
unsigned int format, bool dac)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
|
||||
u32 fmt, reg, mask, shift;
|
||||
bool master_mode;
|
||||
|
||||
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
fmt = PCM3168A_FMT_LEFT_J;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
fmt = PCM3168A_FMT_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
fmt = PCM3168A_FMT_RIGHT_J;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
fmt = PCM3168A_FMT_DSP_A;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
fmt = PCM3168A_FMT_DSP_B;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "unsupported dai format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
master_mode = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
master_mode = true;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "unsupported master/slave mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (format & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dac) {
|
||||
reg = PCM3168A_DAC_PWR_MST_FMT;
|
||||
mask = PCM3168A_DAC_FMT_MASK;
|
||||
shift = PCM3168A_DAC_FMT_SHIFT;
|
||||
pcm3168a->dac_master_mode = master_mode;
|
||||
pcm3168a->dac_fmt = fmt;
|
||||
} else {
|
||||
reg = PCM3168A_ADC_MST_FMT;
|
||||
mask = PCM3168A_ADC_FMTAD_MASK;
|
||||
shift = PCM3168A_ADC_FMTAD_SHIFT;
|
||||
pcm3168a->adc_master_mode = master_mode;
|
||||
pcm3168a->adc_fmt = fmt;
|
||||
}
|
||||
|
||||
regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
|
||||
unsigned int format)
|
||||
{
|
||||
return pcm3168a_set_dai_fmt(dai, format, true);
|
||||
}
|
||||
|
||||
static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
|
||||
unsigned int format)
|
||||
{
|
||||
return pcm3168a_set_dai_fmt(dai, format, false);
|
||||
}
|
||||
|
||||
static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
|
||||
bool tx, master_mode;
|
||||
u32 val, mask, shift, reg;
|
||||
unsigned int rate, channels, fmt, ratio, max_ratio;
|
||||
int i, min_frame_size;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
|
||||
ratio = pcm3168a->sysclk / rate;
|
||||
|
||||
tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
if (tx) {
|
||||
max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
|
||||
reg = PCM3168A_DAC_PWR_MST_FMT;
|
||||
mask = PCM3168A_DAC_MSDA_MASK;
|
||||
shift = PCM3168A_DAC_MSDA_SHIFT;
|
||||
master_mode = pcm3168a->dac_master_mode;
|
||||
fmt = pcm3168a->dac_fmt;
|
||||
} else {
|
||||
max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
|
||||
reg = PCM3168A_ADC_MST_FMT;
|
||||
mask = PCM3168A_ADC_MSAD_MASK;
|
||||
shift = PCM3168A_ADC_MSAD_SHIFT;
|
||||
master_mode = pcm3168a->adc_master_mode;
|
||||
fmt = pcm3168a->adc_fmt;
|
||||
}
|
||||
|
||||
for (i = 0; i < max_ratio; i++) {
|
||||
if (pcm3168a_scki_ratios[i] == ratio)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == max_ratio) {
|
||||
dev_err(codec->dev, "unsupported sysclk ratio\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
min_frame_size = params_width(params) * 2;
|
||||
switch (min_frame_size) {
|
||||
case 32:
|
||||
if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
|
||||
dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
fmt = PCM3168A_FMT_RIGHT_J_16;
|
||||
break;
|
||||
case 48:
|
||||
if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) {
|
||||
dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (master_mode)
|
||||
val = ((i + 1) << shift);
|
||||
else
|
||||
val = 0;
|
||||
|
||||
regmap_update_bits(pcm3168a->regmap, reg, mask, val);
|
||||
|
||||
if (tx) {
|
||||
mask = PCM3168A_DAC_FMT_MASK;
|
||||
shift = PCM3168A_DAC_FMT_SHIFT;
|
||||
} else {
|
||||
mask = PCM3168A_ADC_FMTAD_MASK;
|
||||
shift = PCM3168A_ADC_FMTAD_SHIFT;
|
||||
}
|
||||
|
||||
regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
|
||||
.set_fmt = pcm3168a_set_dai_fmt_dac,
|
||||
.set_sysclk = pcm3168a_set_dai_sysclk,
|
||||
.hw_params = pcm3168a_hw_params,
|
||||
.digital_mute = pcm3168a_digital_mute
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
|
||||
.set_fmt = pcm3168a_set_dai_fmt_adc,
|
||||
.set_sysclk = pcm3168a_set_dai_sysclk,
|
||||
.hw_params = pcm3168a_hw_params
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver pcm3168a_dais[] = {
|
||||
{
|
||||
.name = "pcm3168a-dac",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = PCM3168A_FORMATS
|
||||
},
|
||||
.ops = &pcm3168a_dac_dai_ops
|
||||
},
|
||||
{
|
||||
.name = "pcm3168a-adc",
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 6,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = PCM3168A_FORMATS
|
||||
},
|
||||
.ops = &pcm3168a_adc_dai_ops
|
||||
},
|
||||
};
|
||||
|
||||
static const struct reg_default pcm3168a_reg_default[] = {
|
||||
{ PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK },
|
||||
{ PCM3168A_DAC_PWR_MST_FMT, 0x00 },
|
||||
{ PCM3168A_DAC_OP_FLT, 0x00 },
|
||||
{ PCM3168A_DAC_INV, 0x00 },
|
||||
{ PCM3168A_DAC_MUTE, 0x00 },
|
||||
{ PCM3168A_DAC_ZERO, 0x00 },
|
||||
{ PCM3168A_DAC_ATT_DEMP_ZF, 0x00 },
|
||||
{ PCM3168A_DAC_VOL_MASTER, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 1, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 2, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 3, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 4, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 5, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 6, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 7, 0xff },
|
||||
{ PCM3168A_ADC_SMODE, 0x00 },
|
||||
{ PCM3168A_ADC_MST_FMT, 0x00 },
|
||||
{ PCM3168A_ADC_PWR_HPFB, 0x00 },
|
||||
{ PCM3168A_ADC_SEAD, 0x00 },
|
||||
{ PCM3168A_ADC_INV, 0x00 },
|
||||
{ PCM3168A_ADC_MUTE, 0x00 },
|
||||
{ PCM3168A_ADC_OV, 0x00 },
|
||||
{ PCM3168A_ADC_ATT_OVF, 0x00 },
|
||||
{ PCM3168A_ADC_VOL_MASTER, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START + 1, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START + 2, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START + 3, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START + 4, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START + 5, 0xd3 }
|
||||
};
|
||||
|
||||
static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if (reg >= PCM3168A_RST_SMODE)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case PCM3168A_DAC_ZERO:
|
||||
case PCM3168A_ADC_OV:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool pcm3168a_writeable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if (reg < PCM3168A_RST_SMODE)
|
||||
return false;
|
||||
|
||||
switch (reg) {
|
||||
case PCM3168A_DAC_ZERO:
|
||||
case PCM3168A_ADC_OV:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const struct regmap_config pcm3168a_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = PCM3168A_ADC_VOL_CHAN_START + 5,
|
||||
.reg_defaults = pcm3168a_reg_default,
|
||||
.num_reg_defaults = ARRAY_SIZE(pcm3168a_reg_default),
|
||||
.readable_reg = pcm3168a_readable_register,
|
||||
.volatile_reg = pcm3168a_volatile_register,
|
||||
.writeable_reg = pcm3168a_writeable_register,
|
||||
.cache_type = REGCACHE_FLAT
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pcm3168a_regmap);
|
||||
|
||||
static const struct snd_soc_codec_driver pcm3168a_driver = {
|
||||
.idle_bias_off = true,
|
||||
.controls = pcm3168a_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(pcm3168a_snd_controls),
|
||||
.dapm_widgets = pcm3168a_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets),
|
||||
.dapm_routes = pcm3168a_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes)
|
||||
};
|
||||
|
||||
int pcm3168a_probe(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
struct pcm3168a_priv *pcm3168a;
|
||||
int ret, i;
|
||||
|
||||
pcm3168a = devm_kzalloc(dev, sizeof(*pcm3168a), GFP_KERNEL);
|
||||
if (pcm3168a == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, pcm3168a);
|
||||
|
||||
pcm3168a->scki = devm_clk_get(dev, "scki");
|
||||
if (IS_ERR(pcm3168a->scki)) {
|
||||
ret = PTR_ERR(pcm3168a->scki);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to acquire clock 'scki': %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(pcm3168a->scki);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable mclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
|
||||
pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(dev,
|
||||
ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to request supplies: %d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable supplies: %d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
pcm3168a->regmap = regmap;
|
||||
if (IS_ERR(pcm3168a->regmap)) {
|
||||
ret = PTR_ERR(pcm3168a->regmap);
|
||||
dev_err(dev, "failed to allocate regmap: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
ret = pcm3168a_reset(pcm3168a);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to reset device: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_idle(dev);
|
||||
|
||||
ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais,
|
||||
ARRAY_SIZE(pcm3168a_dais));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register codec: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_regulator:
|
||||
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
err_clk:
|
||||
clk_disable_unprepare(pcm3168a->scki);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcm3168a_probe);
|
||||
|
||||
void pcm3168a_remove(struct device *dev)
|
||||
{
|
||||
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
|
||||
|
||||
snd_soc_unregister_codec(dev);
|
||||
pm_runtime_disable(dev);
|
||||
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
clk_disable_unprepare(pcm3168a->scki);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcm3168a_remove);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pcm3168a_rt_resume(struct device *dev)
|
||||
{
|
||||
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(pcm3168a->scki);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable mclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable supplies: %d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = pcm3168a_reset(pcm3168a);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to reset device: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
regcache_cache_only(pcm3168a->regmap, false);
|
||||
|
||||
regcache_mark_dirty(pcm3168a->regmap);
|
||||
|
||||
ret = regcache_sync(pcm3168a->regmap);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to sync regmap: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_regulator:
|
||||
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
err_clk:
|
||||
clk_disable_unprepare(pcm3168a->scki);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pcm3168a_rt_suspend(struct device *dev)
|
||||
{
|
||||
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(pcm3168a->regmap, true);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
|
||||
clk_disable_unprepare(pcm3168a->scki);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct dev_pm_ops pcm3168a_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pcm3168a_pm_ops);
|
||||
|
||||
MODULE_DESCRIPTION("PCM3168A codec driver");
|
||||
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
100
sound/soc/codecs/pcm3168a.h
Normal file
100
sound/soc/codecs/pcm3168a.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* PCM3168A codec driver header
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __PCM3168A_H__
|
||||
#define __PCM3168A_H__
|
||||
|
||||
extern const struct dev_pm_ops pcm3168a_pm_ops;
|
||||
extern const struct regmap_config pcm3168a_regmap;
|
||||
|
||||
extern int pcm3168a_probe(struct device *dev, struct regmap *regmap);
|
||||
extern void pcm3168a_remove(struct device *dev);
|
||||
|
||||
#define PCM3168A_RST_SMODE 0x40
|
||||
#define PCM3168A_MRST_MASK 0x80
|
||||
#define PCM3168A_SRST_MASK 0x40
|
||||
#define PCM3168A_DAC_SRDA_SHIFT 0
|
||||
#define PCM3168A_DAC_SRDA_MASK 0x3
|
||||
|
||||
#define PCM3168A_DAC_PWR_MST_FMT 0x41
|
||||
#define PCM3168A_DAC_PSMDA_SHIFT 7
|
||||
#define PCM3168A_DAC_PSMDA_MASK 0x80
|
||||
#define PCM3168A_DAC_MSDA_SHIFT 4
|
||||
#define PCM3168A_DAC_MSDA_MASK 0x70
|
||||
#define PCM3168A_DAC_FMT_SHIFT 0
|
||||
#define PCM3168A_DAC_FMT_MASK 0xf
|
||||
|
||||
#define PCM3168A_DAC_OP_FLT 0x42
|
||||
#define PCM3168A_DAC_OPEDA_SHIFT 4
|
||||
#define PCM3168A_DAC_OPEDA_MASK 0xf0
|
||||
#define PCM3168A_DAC_FLT_SHIFT 0
|
||||
#define PCM3168A_DAC_FLT_MASK 0xf
|
||||
|
||||
#define PCM3168A_DAC_INV 0x43
|
||||
|
||||
#define PCM3168A_DAC_MUTE 0x44
|
||||
|
||||
#define PCM3168A_DAC_ZERO 0x45
|
||||
|
||||
#define PCM3168A_DAC_ATT_DEMP_ZF 0x46
|
||||
#define PCM3168A_DAC_ATMDDA_MASK 0x80
|
||||
#define PCM3168A_DAC_ATMDDA_SHIFT 7
|
||||
#define PCM3168A_DAC_ATSPDA_MASK 0x40
|
||||
#define PCM3168A_DAC_ATSPDA_SHIFT 6
|
||||
#define PCM3168A_DAC_DEMP_SHIFT 4
|
||||
#define PCM3168A_DAC_DEMP_MASK 0x30
|
||||
#define PCM3168A_DAC_AZRO_SHIFT 1
|
||||
#define PCM3168A_DAC_AZRO_MASK 0xe
|
||||
#define PCM3168A_DAC_ZREV_MASK 0x1
|
||||
#define PCM3168A_DAC_ZREV_SHIFT 0
|
||||
|
||||
#define PCM3168A_DAC_VOL_MASTER 0x47
|
||||
|
||||
#define PCM3168A_DAC_VOL_CHAN_START 0x48
|
||||
|
||||
#define PCM3168A_ADC_SMODE 0x50
|
||||
#define PCM3168A_ADC_SRAD_SHIFT 0
|
||||
#define PCM3168A_ADC_SRAD_MASK 0x3
|
||||
|
||||
#define PCM3168A_ADC_MST_FMT 0x51
|
||||
#define PCM3168A_ADC_MSAD_SHIFT 4
|
||||
#define PCM3168A_ADC_MSAD_MASK 0x70
|
||||
#define PCM3168A_ADC_FMTAD_SHIFT 0
|
||||
#define PCM3168A_ADC_FMTAD_MASK 0x7
|
||||
|
||||
#define PCM3168A_ADC_PWR_HPFB 0x52
|
||||
#define PCM3168A_ADC_PSVAD_SHIFT 4
|
||||
#define PCM3168A_ADC_PSVAD_MASK 0x70
|
||||
#define PCM3168A_ADC_BYP_SHIFT 0
|
||||
#define PCM3168A_ADC_BYP_MASK 0x7
|
||||
|
||||
#define PCM3168A_ADC_SEAD 0x53
|
||||
|
||||
#define PCM3168A_ADC_INV 0x54
|
||||
|
||||
#define PCM3168A_ADC_MUTE 0x55
|
||||
|
||||
#define PCM3168A_ADC_OV 0x56
|
||||
|
||||
#define PCM3168A_ADC_ATT_OVF 0x57
|
||||
#define PCM3168A_ADC_ATMDAD_MASK 0x80
|
||||
#define PCM3168A_ADC_ATMDAD_SHIFT 7
|
||||
#define PCM3168A_ADC_ATSPAD_MASK 0x40
|
||||
#define PCM3168A_ADC_ATSPAD_SHIFT 6
|
||||
#define PCM3168A_ADC_OVFP_MASK 0x1
|
||||
#define PCM3168A_ADC_OVFP_SHIFT 0
|
||||
|
||||
#define PCM3168A_ADC_VOL_MASTER 0x58
|
||||
|
||||
#define PCM3168A_ADC_VOL_CHAN_START 0x59
|
||||
|
||||
#endif
|
|
@ -1114,6 +1114,12 @@ static const struct dmi_system_id force_combo_jack_table[] = {
|
|||
DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS")
|
||||
}
|
||||
},
|
||||
{
|
||||
.ident = "Intel Skylake RVP",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform")
|
||||
}
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -854,8 +854,6 @@ static int rt298_set_dai_sysclk(struct snd_soc_dai *dai,
|
|||
} else {
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_I2S_CTRL2, 0x0100, 0x0100);
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_PLL_CTRL, 0x4, 0x4);
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_PLL_CTRL1, 0x20, 0x0);
|
||||
}
|
||||
|
|
1381
sound/soc/codecs/rt5616.c
Normal file
1381
sound/soc/codecs/rt5616.c
Normal file
File diff suppressed because it is too large
Load diff
1819
sound/soc/codecs/rt5616.h
Normal file
1819
sound/soc/codecs/rt5616.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -488,6 +488,18 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int is_using_asrc(struct snd_soc_dapm_widget *source,
|
||||
struct snd_soc_dapm_widget *sink)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
|
||||
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (!rt5640->asrc_en)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Digital Mixer */
|
||||
static const struct snd_kcontrol_new rt5640_sto_adc_l_mix[] = {
|
||||
SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
|
||||
|
@ -1059,6 +1071,20 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
|
|||
static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
|
||||
RT5640_PWR_PLL_BIT, 0, NULL, 0),
|
||||
|
||||
/* ASRC */
|
||||
SND_SOC_DAPM_SUPPLY_S("Stereo Filter ASRC", 1, RT5640_ASRC_1,
|
||||
15, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("I2S2 Filter ASRC", 1, RT5640_ASRC_1,
|
||||
12, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5640_ASRC_1,
|
||||
11, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DMIC1 ASRC", 1, RT5640_ASRC_1,
|
||||
9, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DMIC2 ASRC", 1, RT5640_ASRC_1,
|
||||
8, 0, NULL, 0),
|
||||
|
||||
|
||||
/* Input Side */
|
||||
/* micbias */
|
||||
SND_SOC_DAPM_SUPPLY("LDO2", RT5640_PWR_ANLG1,
|
||||
|
@ -1319,6 +1345,12 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
|
|||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
|
||||
{ "I2S1", NULL, "Stereo Filter ASRC", is_using_asrc },
|
||||
{ "I2S2", NULL, "I2S2 ASRC", is_using_asrc },
|
||||
{ "I2S2", NULL, "I2S2 Filter ASRC", is_using_asrc },
|
||||
{ "DMIC1", NULL, "DMIC1 ASRC", is_using_asrc },
|
||||
{ "DMIC2", NULL, "DMIC2 ASRC", is_using_asrc },
|
||||
|
||||
{"IN1P", NULL, "LDO2"},
|
||||
{"IN2P", NULL, "LDO2"},
|
||||
{"IN3P", NULL, "LDO2"},
|
||||
|
@ -1981,6 +2013,76 @@ int rt5640_dmic_enable(struct snd_soc_codec *codec,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
|
||||
|
||||
int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
|
||||
unsigned int filter_mask, unsigned int clk_src)
|
||||
{
|
||||
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int asrc2_mask = 0;
|
||||
unsigned int asrc2_value = 0;
|
||||
|
||||
switch (clk_src) {
|
||||
case RT5640_CLK_SEL_SYS:
|
||||
case RT5640_CLK_SEL_ASRC:
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!filter_mask)
|
||||
return -EINVAL;
|
||||
|
||||
if (filter_mask & RT5640_DA_STEREO_FILTER) {
|
||||
asrc2_mask |= RT5640_STO_DAC_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_STO_DAC_M_MASK)
|
||||
| (clk_src << RT5640_STO_DAC_M_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5640_DA_MONO_L_FILTER) {
|
||||
asrc2_mask |= RT5640_MDA_L_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_MDA_L_M_MASK)
|
||||
| (clk_src << RT5640_MDA_L_M_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5640_DA_MONO_R_FILTER) {
|
||||
asrc2_mask |= RT5640_MDA_R_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_MDA_R_M_MASK)
|
||||
| (clk_src << RT5640_MDA_R_M_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5640_AD_STEREO_FILTER) {
|
||||
asrc2_mask |= RT5640_ADC_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_ADC_M_MASK)
|
||||
| (clk_src << RT5640_ADC_M_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5640_AD_MONO_L_FILTER) {
|
||||
asrc2_mask |= RT5640_MAD_L_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_MAD_L_M_MASK)
|
||||
| (clk_src << RT5640_MAD_L_M_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5640_AD_MONO_R_FILTER) {
|
||||
asrc2_mask |= RT5640_MAD_R_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_MAD_R_M_MASK)
|
||||
| (clk_src << RT5640_MAD_R_M_SFT);
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, RT5640_ASRC_2,
|
||||
asrc2_mask, asrc2_value);
|
||||
|
||||
if (snd_soc_read(codec, RT5640_ASRC_2)) {
|
||||
rt5640->asrc_en = true;
|
||||
snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x3);
|
||||
} else {
|
||||
rt5640->asrc_en = false;
|
||||
snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src);
|
||||
|
||||
static int rt5640_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||
|
@ -2175,6 +2277,7 @@ static const struct acpi_device_id rt5640_acpi_match[] = {
|
|||
{ "INT33CA", 0 },
|
||||
{ "10EC5640", 0 },
|
||||
{ "10EC5642", 0 },
|
||||
{ "INTCCFFD", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match);
|
||||
|
|
|
@ -1033,6 +1033,10 @@
|
|||
#define RT5640_DMIC_2_M_NOR (0x0 << 8)
|
||||
#define RT5640_DMIC_2_M_ASYN (0x1 << 8)
|
||||
|
||||
/* ASRC clock source selection (0x84) */
|
||||
#define RT5640_CLK_SEL_SYS (0x0)
|
||||
#define RT5640_CLK_SEL_ASRC (0x1)
|
||||
|
||||
/* ASRC Control 2 (0x84) */
|
||||
#define RT5640_MDA_L_M_MASK (0x1 << 15)
|
||||
#define RT5640_MDA_L_M_SFT 15
|
||||
|
@ -2079,6 +2083,16 @@ enum {
|
|||
RT5640_DMIC2,
|
||||
};
|
||||
|
||||
/* filter mask */
|
||||
enum {
|
||||
RT5640_DA_STEREO_FILTER = 0x1,
|
||||
RT5640_DA_MONO_L_FILTER = (0x1 << 1),
|
||||
RT5640_DA_MONO_R_FILTER = (0x1 << 2),
|
||||
RT5640_AD_STEREO_FILTER = (0x1 << 3),
|
||||
RT5640_AD_MONO_L_FILTER = (0x1 << 4),
|
||||
RT5640_AD_MONO_R_FILTER = (0x1 << 5),
|
||||
};
|
||||
|
||||
struct rt5640_priv {
|
||||
struct snd_soc_codec *codec;
|
||||
struct rt5640_platform_data pdata;
|
||||
|
@ -2095,9 +2109,12 @@ struct rt5640_priv {
|
|||
int pll_out;
|
||||
|
||||
bool hp_mute;
|
||||
bool asrc_en;
|
||||
};
|
||||
|
||||
int rt5640_dmic_enable(struct snd_soc_codec *codec,
|
||||
bool dmic1_data_pin, bool dmic2_data_pin);
|
||||
int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
|
||||
unsigned int filter_mask, unsigned int clk_src);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -64,7 +64,6 @@ static const struct reg_sequence init_list[] = {
|
|||
{RT5645_PR_BASE + 0x21, 0x4040},
|
||||
{RT5645_PR_BASE + 0x23, 0x0004},
|
||||
};
|
||||
#define RT5645_INIT_REG_LEN ARRAY_SIZE(init_list)
|
||||
|
||||
static const struct reg_sequence rt5650_init_list[] = {
|
||||
{0xf6, 0x0100},
|
||||
|
@ -226,6 +225,163 @@ static const struct reg_default rt5645_reg[] = {
|
|||
{ 0xff, 0x6308 },
|
||||
};
|
||||
|
||||
static const struct reg_default rt5650_reg[] = {
|
||||
{ 0x00, 0x0000 },
|
||||
{ 0x01, 0xc8c8 },
|
||||
{ 0x02, 0xc8c8 },
|
||||
{ 0x03, 0xc8c8 },
|
||||
{ 0x0a, 0x0002 },
|
||||
{ 0x0b, 0x2827 },
|
||||
{ 0x0c, 0xe000 },
|
||||
{ 0x0d, 0x0000 },
|
||||
{ 0x0e, 0x0000 },
|
||||
{ 0x0f, 0x0808 },
|
||||
{ 0x14, 0x3333 },
|
||||
{ 0x16, 0x4b00 },
|
||||
{ 0x18, 0x018b },
|
||||
{ 0x19, 0xafaf },
|
||||
{ 0x1a, 0xafaf },
|
||||
{ 0x1b, 0x0001 },
|
||||
{ 0x1c, 0x2f2f },
|
||||
{ 0x1d, 0x2f2f },
|
||||
{ 0x1e, 0x0000 },
|
||||
{ 0x20, 0x0000 },
|
||||
{ 0x27, 0x7060 },
|
||||
{ 0x28, 0x7070 },
|
||||
{ 0x29, 0x8080 },
|
||||
{ 0x2a, 0x5656 },
|
||||
{ 0x2b, 0x5454 },
|
||||
{ 0x2c, 0xaaa0 },
|
||||
{ 0x2d, 0x0000 },
|
||||
{ 0x2f, 0x1002 },
|
||||
{ 0x31, 0x5000 },
|
||||
{ 0x32, 0x0000 },
|
||||
{ 0x33, 0x0000 },
|
||||
{ 0x34, 0x0000 },
|
||||
{ 0x35, 0x0000 },
|
||||
{ 0x3b, 0x0000 },
|
||||
{ 0x3c, 0x007f },
|
||||
{ 0x3d, 0x0000 },
|
||||
{ 0x3e, 0x007f },
|
||||
{ 0x3f, 0x0000 },
|
||||
{ 0x40, 0x001f },
|
||||
{ 0x41, 0x0000 },
|
||||
{ 0x42, 0x001f },
|
||||
{ 0x45, 0x6000 },
|
||||
{ 0x46, 0x003e },
|
||||
{ 0x47, 0x003e },
|
||||
{ 0x48, 0xf807 },
|
||||
{ 0x4a, 0x0004 },
|
||||
{ 0x4d, 0x0000 },
|
||||
{ 0x4e, 0x0000 },
|
||||
{ 0x4f, 0x01ff },
|
||||
{ 0x50, 0x0000 },
|
||||
{ 0x51, 0x0000 },
|
||||
{ 0x52, 0x01ff },
|
||||
{ 0x53, 0xf000 },
|
||||
{ 0x56, 0x0111 },
|
||||
{ 0x57, 0x0064 },
|
||||
{ 0x58, 0xef0e },
|
||||
{ 0x59, 0xf0f0 },
|
||||
{ 0x5a, 0xef0e },
|
||||
{ 0x5b, 0xf0f0 },
|
||||
{ 0x5c, 0xef0e },
|
||||
{ 0x5d, 0xf0f0 },
|
||||
{ 0x5e, 0xf000 },
|
||||
{ 0x5f, 0x0000 },
|
||||
{ 0x61, 0x0300 },
|
||||
{ 0x62, 0x0000 },
|
||||
{ 0x63, 0x00c2 },
|
||||
{ 0x64, 0x0000 },
|
||||
{ 0x65, 0x0000 },
|
||||
{ 0x66, 0x0000 },
|
||||
{ 0x6a, 0x0000 },
|
||||
{ 0x6c, 0x0aaa },
|
||||
{ 0x70, 0x8000 },
|
||||
{ 0x71, 0x8000 },
|
||||
{ 0x72, 0x8000 },
|
||||
{ 0x73, 0x7770 },
|
||||
{ 0x74, 0x3e00 },
|
||||
{ 0x75, 0x2409 },
|
||||
{ 0x76, 0x000a },
|
||||
{ 0x77, 0x0c00 },
|
||||
{ 0x78, 0x0000 },
|
||||
{ 0x79, 0x0123 },
|
||||
{ 0x7a, 0x0123 },
|
||||
{ 0x80, 0x0000 },
|
||||
{ 0x81, 0x0000 },
|
||||
{ 0x82, 0x0000 },
|
||||
{ 0x83, 0x0000 },
|
||||
{ 0x84, 0x0000 },
|
||||
{ 0x85, 0x0000 },
|
||||
{ 0x8a, 0x0000 },
|
||||
{ 0x8e, 0x0004 },
|
||||
{ 0x8f, 0x1100 },
|
||||
{ 0x90, 0x0646 },
|
||||
{ 0x91, 0x0c06 },
|
||||
{ 0x93, 0x0000 },
|
||||
{ 0x94, 0x0200 },
|
||||
{ 0x95, 0x0000 },
|
||||
{ 0x9a, 0x2184 },
|
||||
{ 0x9b, 0x010a },
|
||||
{ 0x9c, 0x0aea },
|
||||
{ 0x9d, 0x000c },
|
||||
{ 0x9e, 0x0400 },
|
||||
{ 0xa0, 0xa0a8 },
|
||||
{ 0xa1, 0x0059 },
|
||||
{ 0xa2, 0x0001 },
|
||||
{ 0xae, 0x6000 },
|
||||
{ 0xaf, 0x0000 },
|
||||
{ 0xb0, 0x6000 },
|
||||
{ 0xb1, 0x0000 },
|
||||
{ 0xb2, 0x0000 },
|
||||
{ 0xb3, 0x001f },
|
||||
{ 0xb4, 0x020c },
|
||||
{ 0xb5, 0x1f00 },
|
||||
{ 0xb6, 0x0000 },
|
||||
{ 0xbb, 0x0000 },
|
||||
{ 0xbc, 0x0000 },
|
||||
{ 0xbd, 0x0000 },
|
||||
{ 0xbe, 0x0000 },
|
||||
{ 0xbf, 0x3100 },
|
||||
{ 0xc0, 0x0000 },
|
||||
{ 0xc1, 0x0000 },
|
||||
{ 0xc2, 0x0000 },
|
||||
{ 0xc3, 0x2000 },
|
||||
{ 0xcd, 0x0000 },
|
||||
{ 0xce, 0x0000 },
|
||||
{ 0xcf, 0x1813 },
|
||||
{ 0xd0, 0x0690 },
|
||||
{ 0xd1, 0x1c17 },
|
||||
{ 0xd3, 0xb320 },
|
||||
{ 0xd4, 0x0000 },
|
||||
{ 0xd6, 0x0400 },
|
||||
{ 0xd9, 0x0809 },
|
||||
{ 0xda, 0x0000 },
|
||||
{ 0xdb, 0x0003 },
|
||||
{ 0xdc, 0x0049 },
|
||||
{ 0xdd, 0x001b },
|
||||
{ 0xdf, 0x0008 },
|
||||
{ 0xe0, 0x4000 },
|
||||
{ 0xe6, 0x8000 },
|
||||
{ 0xe7, 0x0200 },
|
||||
{ 0xec, 0xb300 },
|
||||
{ 0xed, 0x0000 },
|
||||
{ 0xf0, 0x001f },
|
||||
{ 0xf1, 0x020c },
|
||||
{ 0xf2, 0x1f00 },
|
||||
{ 0xf3, 0x0000 },
|
||||
{ 0xf4, 0x4000 },
|
||||
{ 0xf8, 0x0000 },
|
||||
{ 0xf9, 0x0000 },
|
||||
{ 0xfa, 0x2060 },
|
||||
{ 0xfb, 0x4040 },
|
||||
{ 0xfc, 0x0000 },
|
||||
{ 0xfd, 0x0002 },
|
||||
{ 0xfe, 0x10ec },
|
||||
{ 0xff, 0x6308 },
|
||||
};
|
||||
|
||||
struct rt5645_eq_param_s {
|
||||
unsigned short reg;
|
||||
unsigned short val;
|
||||
|
@ -248,6 +404,7 @@ struct rt5645_priv {
|
|||
struct delayed_work jack_detect_work, rcclock_work;
|
||||
struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
|
||||
struct rt5645_eq_param_s *eq_param;
|
||||
struct timer_list btn_check_timer;
|
||||
|
||||
int codec_type;
|
||||
int sysclk;
|
||||
|
@ -572,14 +729,12 @@ static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol,
|
|||
struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
cancel_delayed_work_sync(&rt5645->rcclock_work);
|
||||
|
||||
regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
|
||||
RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU);
|
||||
|
||||
ret = snd_soc_put_volsw(kcontrol, ucontrol);
|
||||
|
||||
queue_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work,
|
||||
mod_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work,
|
||||
msecs_to_jiffies(200));
|
||||
|
||||
return ret;
|
||||
|
@ -2911,6 +3066,7 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
|
|||
snd_soc_dapm_force_enable_pin(dapm, "ADC R power");
|
||||
snd_soc_dapm_sync(dapm);
|
||||
|
||||
snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD1, 0x3, 0x3);
|
||||
snd_soc_update_bits(codec,
|
||||
RT5645_INT_IRQ_ST, 0x8, 0x8);
|
||||
snd_soc_update_bits(codec,
|
||||
|
@ -2979,7 +3135,7 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
|
|||
}
|
||||
if (rt5645->pdata.jd_invert)
|
||||
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
|
||||
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
|
||||
RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
|
||||
} else { /* jack out */
|
||||
rt5645->jack_type = 0;
|
||||
|
||||
|
@ -3000,7 +3156,7 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
|
|||
snd_soc_dapm_sync(dapm);
|
||||
if (rt5645->pdata.jd_invert)
|
||||
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
|
||||
RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
|
||||
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
|
||||
}
|
||||
|
||||
return rt5645->jack_type;
|
||||
|
@ -3124,6 +3280,12 @@ static void rt5645_jack_detect_work(struct work_struct *work)
|
|||
}
|
||||
if (btn_type == 0)/* button release */
|
||||
report = rt5645->jack_type;
|
||||
else {
|
||||
if (rt5645->pdata.jd_invert) {
|
||||
mod_timer(&rt5645->btn_check_timer,
|
||||
msecs_to_jiffies(100));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
/* jack out */
|
||||
|
@ -3166,6 +3328,14 @@ static irqreturn_t rt5645_irq(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void rt5645_btn_check_callback(unsigned long data)
|
||||
{
|
||||
struct rt5645_priv *rt5645 = (struct rt5645_priv *)data;
|
||||
|
||||
queue_delayed_work(system_power_efficient_wq,
|
||||
&rt5645->jack_detect_work, msecs_to_jiffies(5));
|
||||
}
|
||||
|
||||
static int rt5645_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||
|
@ -3322,6 +3492,31 @@ static const struct regmap_config rt5645_regmap = {
|
|||
.num_ranges = ARRAY_SIZE(rt5645_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config rt5650_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.use_single_rw = true,
|
||||
.max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) *
|
||||
RT5645_PR_SPACING),
|
||||
.volatile_reg = rt5645_volatile_register,
|
||||
.readable_reg = rt5645_readable_register,
|
||||
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.reg_defaults = rt5650_reg,
|
||||
.num_reg_defaults = ARRAY_SIZE(rt5650_reg),
|
||||
.ranges = rt5645_ranges,
|
||||
.num_ranges = ARRAY_SIZE(rt5645_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config temp_regmap = {
|
||||
.name="nocache",
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.use_single_rw = true,
|
||||
.max_register = RT5645_VENDOR_ID2 + 1,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id rt5645_i2c_id[] = {
|
||||
{ "rt5645", 0 },
|
||||
{ "rt5650", 0 },
|
||||
|
@ -3330,7 +3525,7 @@ static const struct i2c_device_id rt5645_i2c_id[] = {
|
|||
MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static struct acpi_device_id rt5645_acpi_match[] = {
|
||||
static const struct acpi_device_id rt5645_acpi_match[] = {
|
||||
{ "10EC5645", 0 },
|
||||
{ "10EC5650", 0 },
|
||||
{},
|
||||
|
@ -3338,69 +3533,23 @@ static struct acpi_device_id rt5645_acpi_match[] = {
|
|||
MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct rt5645_platform_data *rt5645_pdata;
|
||||
|
||||
static struct rt5645_platform_data strago_platform_data = {
|
||||
static struct rt5645_platform_data general_platform_data = {
|
||||
.dmic1_data_pin = RT5645_DMIC1_DISABLE,
|
||||
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
|
||||
.jd_mode = 3,
|
||||
};
|
||||
|
||||
static int strago_quirk_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
rt5645_pdata = &strago_platform_data;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id dmi_platform_intel_braswell[] = {
|
||||
{
|
||||
.ident = "Intel Strago",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Strago"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Celes",
|
||||
.callback = strago_quirk_cb,
|
||||
.ident = "Google Chrome",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Celes"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Ultima",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Reks",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Reks"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Edgar",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Edgar"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Wizpig",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Wizpig"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Terra",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Terra"),
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
|
@ -3413,17 +3562,9 @@ static struct rt5645_platform_data buddy_platform_data = {
|
|||
.jd_invert = true,
|
||||
};
|
||||
|
||||
static int buddy_quirk_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
rt5645_pdata = &buddy_platform_data;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dmi_system_id dmi_platform_intel_broadwell[] = {
|
||||
{
|
||||
.ident = "Chrome Buddy",
|
||||
.callback = buddy_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
|
||||
},
|
||||
|
@ -3431,6 +3572,16 @@ static struct dmi_system_id dmi_platform_intel_broadwell[] = {
|
|||
{ }
|
||||
};
|
||||
|
||||
static bool rt5645_check_dp(struct device *dev)
|
||||
{
|
||||
if (device_property_present(dev, "realtek,in2-differential") ||
|
||||
device_property_present(dev, "realtek,dmic1-data-pin") ||
|
||||
device_property_present(dev, "realtek,dmic2-data-pin") ||
|
||||
device_property_present(dev, "realtek,jd-mode"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
|
||||
{
|
||||
|
@ -3453,6 +3604,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
struct rt5645_priv *rt5645;
|
||||
int ret, i;
|
||||
unsigned int val;
|
||||
struct regmap *regmap;
|
||||
|
||||
rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv),
|
||||
GFP_KERNEL);
|
||||
|
@ -3464,11 +3616,12 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
|
||||
if (pdata)
|
||||
rt5645->pdata = *pdata;
|
||||
else if (dmi_check_system(dmi_platform_intel_braswell) ||
|
||||
dmi_check_system(dmi_platform_intel_broadwell))
|
||||
rt5645->pdata = *rt5645_pdata;
|
||||
else
|
||||
else if (dmi_check_system(dmi_platform_intel_broadwell))
|
||||
rt5645->pdata = buddy_platform_data;
|
||||
else if (rt5645_check_dp(&i2c->dev))
|
||||
rt5645_parse_dt(rt5645, &i2c->dev);
|
||||
else if (dmi_check_system(dmi_platform_intel_braswell))
|
||||
rt5645->pdata = general_platform_data;
|
||||
|
||||
rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect",
|
||||
GPIOD_IN);
|
||||
|
@ -3478,14 +3631,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
return PTR_ERR(rt5645->gpiod_hp_det);
|
||||
}
|
||||
|
||||
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
|
||||
if (IS_ERR(rt5645->regmap)) {
|
||||
ret = PTR_ERR(rt5645->regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++)
|
||||
rt5645->supplies[i].supply = rt5645_supply_names[i];
|
||||
|
||||
|
@ -3504,13 +3649,22 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
return ret;
|
||||
}
|
||||
|
||||
regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val);
|
||||
regmap = devm_regmap_init_i2c(i2c, &temp_regmap);
|
||||
if (IS_ERR(regmap)) {
|
||||
ret = PTR_ERR(regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
regmap_read(regmap, RT5645_VENDOR_ID2, &val);
|
||||
|
||||
switch (val) {
|
||||
case RT5645_DEVICE_ID:
|
||||
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
|
||||
rt5645->codec_type = CODEC_TYPE_RT5645;
|
||||
break;
|
||||
case RT5650_DEVICE_ID:
|
||||
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5650_regmap);
|
||||
rt5645->codec_type = CODEC_TYPE_RT5650;
|
||||
break;
|
||||
default:
|
||||
|
@ -3521,6 +3675,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
goto err_enable;
|
||||
}
|
||||
|
||||
if (IS_ERR(rt5645->regmap)) {
|
||||
ret = PTR_ERR(rt5645->regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_write(rt5645->regmap, RT5645_RESET, 0);
|
||||
|
||||
ret = regmap_register_patch(rt5645->regmap, init_list,
|
||||
|
@ -3641,6 +3802,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
}
|
||||
}
|
||||
|
||||
if (rt5645->pdata.jd_invert) {
|
||||
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
|
||||
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
|
||||
setup_timer(&rt5645->btn_check_timer,
|
||||
rt5645_btn_check_callback, (unsigned long)rt5645);
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
|
||||
INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
@ -1735,12 +1736,38 @@ static const struct regmap_config rt5651_regmap = {
|
|||
.num_ranges = ARRAY_SIZE(rt5651_ranges),
|
||||
};
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id rt5651_of_match[] = {
|
||||
{ .compatible = "realtek,rt5651", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rt5651_of_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id rt5651_acpi_match[] = {
|
||||
{ "10EC5651", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, rt5651_acpi_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id rt5651_i2c_id[] = {
|
||||
{ "rt5651", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id);
|
||||
|
||||
static int rt5651_parse_dt(struct rt5651_priv *rt5651, struct device_node *np)
|
||||
{
|
||||
rt5651->pdata.in2_diff = of_property_read_bool(np,
|
||||
"realtek,in2-differential");
|
||||
rt5651->pdata.dmic_en = of_property_read_bool(np,
|
||||
"realtek,dmic-en");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5651_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -1757,6 +1784,8 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
|
|||
|
||||
if (pdata)
|
||||
rt5651->pdata = *pdata;
|
||||
else if (i2c->dev.of_node)
|
||||
rt5651_parse_dt(rt5651, i2c->dev.of_node);
|
||||
|
||||
rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap);
|
||||
if (IS_ERR(rt5651->regmap)) {
|
||||
|
@ -1806,6 +1835,8 @@ static int rt5651_i2c_remove(struct i2c_client *i2c)
|
|||
static struct i2c_driver rt5651_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "rt5651",
|
||||
.acpi_match_table = ACPI_PTR(rt5651_acpi_match),
|
||||
.of_match_table = of_match_ptr(rt5651_of_match),
|
||||
},
|
||||
.probe = rt5651_i2c_probe,
|
||||
.remove = rt5651_i2c_remove,
|
||||
|
|
4223
sound/soc/codecs/rt5659.c
Normal file
4223
sound/soc/codecs/rt5659.c
Normal file
File diff suppressed because it is too large
Load diff
1819
sound/soc/codecs/rt5659.h
Normal file
1819
sound/soc/codecs/rt5659.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -297,8 +297,6 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
|
|||
case RT5677_HAP_GENE_CTRL2:
|
||||
case RT5677_PWR_DSP_ST:
|
||||
case RT5677_PRIV_DATA:
|
||||
case RT5677_PLL1_CTRL2:
|
||||
case RT5677_PLL2_CTRL2:
|
||||
case RT5677_ASRC_22:
|
||||
case RT5677_ASRC_23:
|
||||
case RT5677_VAD_CTRL5:
|
||||
|
@ -4788,7 +4786,7 @@ static int rt5677_remove(struct snd_soc_codec *codec)
|
|||
|
||||
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
|
||||
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 0);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -4803,7 +4801,7 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
|
|||
regcache_mark_dirty(rt5677->regmap);
|
||||
|
||||
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 0);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -4814,8 +4812,11 @@ static int rt5677_resume(struct snd_soc_codec *codec)
|
|||
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (!rt5677->dsp_vad_en) {
|
||||
rt5677->pll_src = 0;
|
||||
rt5677->pll_in = 0;
|
||||
rt5677->pll_out = 0;
|
||||
gpiod_set_value_cansleep(rt5677->pow_ldo2, 1);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 0);
|
||||
if (rt5677->pow_ldo2 || rt5677->reset_pin)
|
||||
msleep(10);
|
||||
|
||||
|
@ -5160,7 +5161,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
|
|||
return ret;
|
||||
}
|
||||
rt5677->reset_pin = devm_gpiod_get_optional(&i2c->dev,
|
||||
"realtek,reset", GPIOD_OUT_HIGH);
|
||||
"realtek,reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(rt5677->reset_pin)) {
|
||||
ret = PTR_ERR(rt5677->reset_pin);
|
||||
dev_err(&i2c->dev, "Failed to request RESET: %d\n", ret);
|
||||
|
|
|
@ -309,7 +309,7 @@ static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = {
|
|||
.count = ARRAY_SIZE(ssm2518_rates_12288000),
|
||||
};
|
||||
|
||||
static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
|
||||
static int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
|
||||
unsigned int rate)
|
||||
{
|
||||
const unsigned int *sysclks = NULL;
|
||||
|
|
|
@ -1097,8 +1097,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
|
|||
{
|
||||
struct twl6040_data *priv;
|
||||
struct twl6040 *twl6040 = dev_get_drvdata(codec->dev->parent);
|
||||
struct platform_device *pdev = container_of(codec->dev,
|
||||
struct platform_device, dev);
|
||||
struct platform_device *pdev = to_platform_device(codec->dev);
|
||||
int ret = 0;
|
||||
|
||||
priv = devm_kzalloc(codec->dev, sizeof(*priv), GFP_KERNEL);
|
||||
|
|
|
@ -360,15 +360,13 @@ static int wm5110_hp_ev(struct snd_soc_dapm_widget *w,
|
|||
|
||||
static int wm5110_clear_pga_volume(struct arizona *arizona, int output)
|
||||
{
|
||||
struct reg_sequence clear_pga = {
|
||||
ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4, 0x80
|
||||
};
|
||||
unsigned int reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4;
|
||||
int ret;
|
||||
|
||||
ret = regmap_multi_reg_write_bypassed(arizona->regmap, &clear_pga, 1);
|
||||
ret = regmap_write(arizona->regmap, reg, 0x80);
|
||||
if (ret)
|
||||
dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n",
|
||||
clear_pga.reg, ret);
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -439,18 +437,17 @@ static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
|
|||
{
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* PGA Volume is also used as part of the enable sequence, so
|
||||
* usage of it should be avoided whilst that is running.
|
||||
*/
|
||||
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
|
||||
snd_soc_dapm_mutex_lock(dapm);
|
||||
|
||||
ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
|
||||
|
||||
mutex_unlock(&card->dapm_mutex);
|
||||
snd_soc_dapm_mutex_unlock(dapm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -460,18 +457,17 @@ static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
|
|||
{
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* PGA Volume is also used as part of the enable sequence, so
|
||||
* usage of it should be avoided whilst that is running.
|
||||
*/
|
||||
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
|
||||
snd_soc_dapm_mutex_lock(dapm);
|
||||
|
||||
ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
|
||||
|
||||
mutex_unlock(&card->dapm_mutex);
|
||||
snd_soc_dapm_mutex_unlock(dapm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -575,6 +571,33 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
|
|||
SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
|
||||
SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
|
||||
|
||||
#define WM5110_RXANC_INPUT_ROUTES(widget, name) \
|
||||
{ widget, NULL, name " NG Mux" }, \
|
||||
{ name " NG Internal", NULL, "RXANC NG Clock" }, \
|
||||
{ name " NG Internal", NULL, name " Channel" }, \
|
||||
{ name " NG External", NULL, "RXANC NG External Clock" }, \
|
||||
{ name " NG External", NULL, name " Channel" }, \
|
||||
{ name " NG Mux", "None", name " Channel" }, \
|
||||
{ name " NG Mux", "Internal", name " NG Internal" }, \
|
||||
{ name " NG Mux", "External", name " NG External" }, \
|
||||
{ name " Channel", "Left", name " Left Input" }, \
|
||||
{ name " Channel", "Combine", name " Left Input" }, \
|
||||
{ name " Channel", "Right", name " Right Input" }, \
|
||||
{ name " Channel", "Combine", name " Right Input" }, \
|
||||
{ name " Left Input", "IN1", "IN1L PGA" }, \
|
||||
{ name " Right Input", "IN1", "IN1R PGA" }, \
|
||||
{ name " Left Input", "IN2", "IN2L PGA" }, \
|
||||
{ name " Right Input", "IN2", "IN2R PGA" }, \
|
||||
{ name " Left Input", "IN3", "IN3L PGA" }, \
|
||||
{ name " Right Input", "IN3", "IN3R PGA" }, \
|
||||
{ name " Left Input", "IN4", "IN4L PGA" }, \
|
||||
{ name " Right Input", "IN4", "IN4R PGA" }
|
||||
|
||||
#define WM5110_RXANC_OUTPUT_ROUTES(widget, name) \
|
||||
{ widget, NULL, name " ANC Source" }, \
|
||||
{ name " ANC Source", "RXANCL", "RXANCL" }, \
|
||||
{ name " ANC Source", "RXANCR", "RXANCR" }
|
||||
|
||||
static const struct snd_kcontrol_new wm5110_snd_controls[] = {
|
||||
SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
|
||||
SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
|
||||
|
@ -639,6 +662,15 @@ SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R,
|
|||
SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
|
||||
SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
|
||||
|
||||
SND_SOC_BYTES("RXANC Coefficients", ARIZONA_ANC_COEFF_START,
|
||||
ARIZONA_ANC_COEFF_END - ARIZONA_ANC_COEFF_START + 1),
|
||||
SND_SOC_BYTES("RXANCL Config", ARIZONA_FCL_FILTER_CONTROL, 1),
|
||||
SND_SOC_BYTES("RXANCL Coefficients", ARIZONA_FCL_COEFF_START,
|
||||
ARIZONA_FCL_COEFF_END - ARIZONA_FCL_COEFF_START + 1),
|
||||
SND_SOC_BYTES("RXANCR Config", ARIZONA_FCR_FILTER_CONTROL, 1),
|
||||
SND_SOC_BYTES("RXANCR Coefficients", ARIZONA_FCR_COEFF_START,
|
||||
ARIZONA_FCR_COEFF_END - ARIZONA_FCR_COEFF_START + 1),
|
||||
|
||||
ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
|
||||
ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
|
||||
ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
|
||||
|
@ -995,6 +1027,31 @@ static const struct soc_enum wm5110_aec_loopback =
|
|||
static const struct snd_kcontrol_new wm5110_aec_loopback_mux =
|
||||
SOC_DAPM_ENUM("AEC Loopback", wm5110_aec_loopback);
|
||||
|
||||
static const struct snd_kcontrol_new wm5110_anc_input_mux[] = {
|
||||
SOC_DAPM_ENUM("RXANCL Input", arizona_anc_input_src[0]),
|
||||
SOC_DAPM_ENUM("RXANCL Channel", arizona_anc_input_src[1]),
|
||||
SOC_DAPM_ENUM("RXANCR Input", arizona_anc_input_src[2]),
|
||||
SOC_DAPM_ENUM("RXANCR Channel", arizona_anc_input_src[3]),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm5110_anc_ng_mux =
|
||||
SOC_DAPM_ENUM("RXANC NG Source", arizona_anc_ng_enum);
|
||||
|
||||
static const struct snd_kcontrol_new wm5110_output_anc_src[] = {
|
||||
SOC_DAPM_ENUM("HPOUT1L ANC Source", arizona_output_anc_src[0]),
|
||||
SOC_DAPM_ENUM("HPOUT1R ANC Source", arizona_output_anc_src[1]),
|
||||
SOC_DAPM_ENUM("HPOUT2L ANC Source", arizona_output_anc_src[2]),
|
||||
SOC_DAPM_ENUM("HPOUT2R ANC Source", arizona_output_anc_src[3]),
|
||||
SOC_DAPM_ENUM("HPOUT3L ANC Source", arizona_output_anc_src[4]),
|
||||
SOC_DAPM_ENUM("HPOUT3R ANC Source", arizona_output_anc_src[5]),
|
||||
SOC_DAPM_ENUM("SPKOUTL ANC Source", arizona_output_anc_src[6]),
|
||||
SOC_DAPM_ENUM("SPKOUTR ANC Source", arizona_output_anc_src[7]),
|
||||
SOC_DAPM_ENUM("SPKDAT1L ANC Source", arizona_output_anc_src[8]),
|
||||
SOC_DAPM_ENUM("SPKDAT1R ANC Source", arizona_output_anc_src[9]),
|
||||
SOC_DAPM_ENUM("SPKDAT2L ANC Source", arizona_output_anc_src[10]),
|
||||
SOC_DAPM_ENUM("SPKDAT2R ANC Source", arizona_output_anc_src[11]),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
|
||||
0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU),
|
||||
|
@ -1185,6 +1242,65 @@ SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
|
|||
ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
|
||||
&wm5110_aec_loopback_mux),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM,
|
||||
ARIZONA_EXT_NG_SEL_SET_SHIFT, 0, arizona_anc_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM,
|
||||
ARIZONA_CLK_NG_ENA_SET_SHIFT, 0, arizona_anc_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[0]),
|
||||
SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[0]),
|
||||
SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[1]),
|
||||
SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux),
|
||||
SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[2]),
|
||||
SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[2]),
|
||||
SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[3]),
|
||||
SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux),
|
||||
|
||||
SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, ARIZONA_CLK_L_ENA_SET_SHIFT,
|
||||
0, NULL, 0, arizona_anc_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, ARIZONA_CLK_R_ENA_SET_SHIFT,
|
||||
0, NULL, 0, arizona_anc_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
|
||||
SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[0]),
|
||||
SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[1]),
|
||||
SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[2]),
|
||||
SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[3]),
|
||||
SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[4]),
|
||||
SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[5]),
|
||||
SND_SOC_DAPM_MUX("SPKOUTL ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[6]),
|
||||
SND_SOC_DAPM_MUX("SPKOUTR ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[7]),
|
||||
SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[8]),
|
||||
SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[9]),
|
||||
SND_SOC_DAPM_MUX("SPKDAT2L ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[10]),
|
||||
SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[11]),
|
||||
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
|
||||
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
|
||||
|
@ -1690,6 +1806,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
|
|||
{ "Slim2 Capture", NULL, "SYSCLK" },
|
||||
{ "Slim3 Capture", NULL, "SYSCLK" },
|
||||
|
||||
{ "Voice Control DSP", NULL, "DSP3" },
|
||||
{ "Voice Control DSP", NULL, "SYSCLK" },
|
||||
|
||||
{ "IN1L PGA", NULL, "IN1L" },
|
||||
{ "IN1R PGA", NULL, "IN1R" },
|
||||
|
||||
|
@ -1838,6 +1957,22 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
|
|||
{ "SPKDAT2L", NULL, "OUT6L" },
|
||||
{ "SPKDAT2R", NULL, "OUT6R" },
|
||||
|
||||
WM5110_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"),
|
||||
WM5110_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"),
|
||||
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT4L", "SPKOUTL"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT4R", "SPKOUTR"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT6L", "SPKDAT2L"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT6R", "SPKDAT2R"),
|
||||
|
||||
{ "MICSUPP", NULL, "SYSCLK" },
|
||||
|
||||
{ "DRC1 Signal Activity", NULL, "DRC1L" },
|
||||
|
@ -1996,12 +2131,65 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
|
|||
},
|
||||
.ops = &arizona_simple_dai_ops,
|
||||
},
|
||||
{
|
||||
.name = "wm5110-cpu-voicectrl",
|
||||
.capture = {
|
||||
.stream_name = "Voice Control CPU",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = WM5110_RATES,
|
||||
.formats = WM5110_FORMATS,
|
||||
},
|
||||
.compress_new = snd_soc_new_compress,
|
||||
},
|
||||
{
|
||||
.name = "wm5110-dsp-voicectrl",
|
||||
.capture = {
|
||||
.stream_name = "Voice Control DSP",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = WM5110_RATES,
|
||||
.formats = WM5110_FORMATS,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int wm5110_open(struct snd_compr_stream *stream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = stream->private_data;
|
||||
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
|
||||
struct arizona *arizona = priv->core.arizona;
|
||||
int n_adsp;
|
||||
|
||||
if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) {
|
||||
n_adsp = 2;
|
||||
} else {
|
||||
dev_err(arizona->dev,
|
||||
"No suitable compressed stream for DAI '%s'\n",
|
||||
rtd->codec_dai->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
|
||||
}
|
||||
|
||||
static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
|
||||
{
|
||||
struct wm5110_priv *florida = data;
|
||||
int ret;
|
||||
|
||||
ret = wm_adsp_compr_handle_irq(&florida->core.adsp[2]);
|
||||
if (ret == -ENODEV)
|
||||
return IRQ_NONE;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int wm5110_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||
struct arizona *arizona = priv->core.arizona;
|
||||
int i, ret;
|
||||
|
||||
priv->core.arizona->dapm = dapm;
|
||||
|
@ -2010,6 +2198,14 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
|
|||
arizona_init_gpio(codec);
|
||||
arizona_init_mono(codec);
|
||||
|
||||
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
|
||||
"ADSP2 Compressed IRQ", wm5110_adsp2_irq,
|
||||
priv);
|
||||
if (ret != 0) {
|
||||
dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
|
||||
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
|
||||
if (ret)
|
||||
|
@ -2030,12 +2226,15 @@ err_adsp2_codec_probe:
|
|||
for (--i; i >= 0; --i)
|
||||
wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
|
||||
|
||||
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm5110_codec_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||
struct arizona *arizona = priv->core.arizona;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WM5110_NUM_ADSP; ++i)
|
||||
|
@ -2043,6 +2242,8 @@ static int wm5110_codec_remove(struct snd_soc_codec *codec)
|
|||
|
||||
priv->core.arizona->dapm = NULL;
|
||||
|
||||
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2088,6 +2289,20 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
|
|||
.num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes),
|
||||
};
|
||||
|
||||
static struct snd_compr_ops wm5110_compr_ops = {
|
||||
.open = wm5110_open,
|
||||
.free = wm_adsp_compr_free,
|
||||
.set_params = wm_adsp_compr_set_params,
|
||||
.get_caps = wm_adsp_compr_get_caps,
|
||||
.trigger = wm_adsp_compr_trigger,
|
||||
.pointer = wm_adsp_compr_pointer,
|
||||
.copy = wm_adsp_compr_copy,
|
||||
};
|
||||
|
||||
static struct snd_soc_platform_driver wm5110_compr_platform = {
|
||||
.compr_ops = &wm5110_compr_ops,
|
||||
};
|
||||
|
||||
static int wm5110_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
||||
|
@ -2148,8 +2363,21 @@ static int wm5110_probe(struct platform_device *pdev)
|
|||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_idle(&pdev->dev);
|
||||
|
||||
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
|
||||
ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
|
||||
wm5110_dai, ARRAY_SIZE(wm5110_dai));
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
}
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm5110_remove(struct platform_device *pdev)
|
||||
|
|
|
@ -1804,7 +1804,7 @@ static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|||
|
||||
regmap_read(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, ®);
|
||||
|
||||
return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT;
|
||||
return !!((reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT);
|
||||
}
|
||||
|
||||
static int wm8903_gpio_direction_out(struct gpio_chip *chip,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue