summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/pcm179x.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-01-17 20:05:31 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2016-01-17 20:05:31 (GMT)
commita016af2e70bfca23f2f5de7d8708157b86ea374d (patch)
treebfe3c0c6ea9d52d4ec6ea021b0626a53c83e7d9f /sound/soc/codecs/pcm179x.c
parente535d74bc50df2357d3253f8f3ca48c66d0d892a (diff)
parentc3b1681375dc6e71d89a3ae00cc3ce9e775a8917 (diff)
downloadlinux-a016af2e70bfca23f2f5de7d8708157b86ea374d.tar.xz
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 ...
Diffstat (limited to 'sound/soc/codecs/pcm179x.c')
-rw-r--r--sound/soc/codecs/pcm179x.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
new file mode 100644
index 0000000..a56c7b7
--- /dev/null
+++ b/sound/soc/codecs/pcm179x.c
@@ -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");