From patchwork Thu Jul 9 11:20:27 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Brown X-Patchwork-Id: 6755101 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 370B8C05AC for ; Thu, 9 Jul 2015 11:22:27 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 5257A20703 for ; Thu, 9 Jul 2015 11:22:25 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 1F04C206FC for ; Thu, 9 Jul 2015 11:22:23 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 9DEBD266192; Thu, 9 Jul 2015 13:22:20 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 5A494266198; Thu, 9 Jul 2015 13:20:44 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 0CCE3266184; Thu, 9 Jul 2015 13:20:43 +0200 (CEST) Received: from mezzanine.sirena.org.uk (mezzanine.sirena.org.uk [106.187.55.193]) by alsa0.perex.cz (Postfix) with ESMTP id AE762266042 for ; Thu, 9 Jul 2015 13:20:38 +0200 (CEST) Received: from cpc11-sgyl31-2-0-cust672.sgyl.cable.virginm.net ([94.175.94.161] helo=debutante) by mezzanine.sirena.org.uk with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA1:128) (Exim 4.80) (envelope-from ) id 1ZD9sU-00044D-IS; Thu, 09 Jul 2015 11:20:36 +0000 Received: from broonie by debutante with local (Exim 4.86_RC4) (envelope-from ) id 1ZD9sR-0005q2-M9; Thu, 09 Jul 2015 12:20:27 +0100 From: Mark Brown To: Jeeja KP , Subhransu S. Prusty , Vinod Koul , Mark Brown In-Reply-To: <1436435411-28028-2-git-send-email-vinod.koul@intel.com> Message-Id: Date: Thu, 09 Jul 2015 12:20:27 +0100 X-SA-Exim-Connect-IP: 94.175.94.161 X-SA-Exim-Mail-From: broonie@sirena.org.uk X-SA-Exim-Version: 4.2.1 (built Mon, 26 Dec 2011 16:24:06 +0000) X-SA-Exim-Scanned: Yes (on mezzanine.sirena.org.uk) Cc: alsa-devel@alsa-project.org Subject: [alsa-devel] Applied "ASoC: Intel: Add Skylake HDA platform driver" to the asoc tree X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP The patch ASoC: Intel: Add Skylake HDA platform driver has been applied to the asoc tree at git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark From a40e693c7f5ee3a1a7d1f6aef63c5dadbec64e10 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 9 Jul 2015 15:20:08 +0530 Subject: [PATCH] ASoC: Intel: Add Skylake HDA platform driver This patch starts to add the Skylake HDA platform driver by defining SoC CPU dais, DMA driver ops and implements ALSA operations Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 586 ++++++++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.h | 71 +++++ 2 files changed, 657 insertions(+) create mode 100644 sound/soc/intel/skylake/skl-pcm.c create mode 100644 sound/soc/intel/skylake/skl.h diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c new file mode 100644 index 000000000000..c171def2fcc9 --- /dev/null +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -0,0 +1,586 @@ +/* + * skl-pcm.c -ASoC HDA Platform driver file implementing PCM functionality + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Jeeja KP + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 +#include +#include +#include +#include "skl.h" + +#define HDA_MONO 1 +#define HDA_STEREO 2 + +static struct snd_pcm_hardware azx_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ + SNDRV_PCM_INFO_HAS_LINK_ATIME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = AZX_MAX_BUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = AZX_MAX_BUF_SIZE / 2, + .periods_min = 2, + .periods_max = AZX_MAX_FRAG, + .fifo_size = 0, +}; + +static inline +struct hdac_ext_stream *get_hdac_ext_stream(struct snd_pcm_substream *substream) +{ + return substream->runtime->private_data; +} + +static struct hdac_ext_bus *get_bus_ctx(struct snd_pcm_substream *substream) +{ + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct hdac_stream *hstream = hdac_stream(stream); + struct hdac_bus *bus = hstream->bus; + + return hbus_to_ebus(bus); +} + +static int skl_substream_alloc_pages(struct hdac_ext_bus *ebus, + struct snd_pcm_substream *substream, + size_t size) +{ + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + + hdac_stream(stream)->bufsize = 0; + hdac_stream(stream)->period_bytes = 0; + hdac_stream(stream)->format_val = 0; + + return snd_pcm_lib_malloc_pages(substream, size); +} + +static int skl_substream_free_pages(struct hdac_bus *bus, + struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus, + struct snd_pcm_runtime *runtime) +{ + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + /* avoid wrap-around with wall-clock */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, + 20, 178000000); +} + +static int skl_pcm_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *stream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct skl_dma_params *dma_params; + int ret; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + ret = pm_runtime_get_sync(dai->dev); + if (ret) + return ret; + + stream = snd_hdac_ext_stream_assign(ebus, substream, + HDAC_EXT_STREAM_TYPE_COUPLED); + if (stream == NULL) + return -EBUSY; + + skl_set_pcm_constrains(ebus, runtime); + + /* + * disable WALLCLOCK timestamps for capture streams + * until we figure out how to handle digital inputs + */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */ + runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME; + } + + runtime->private_data = stream; + + dma_params = kzalloc(sizeof(*dma_params), GFP_KERNEL); + if (!dma_params) + return -ENOMEM; + + dma_params->stream_tag = hdac_stream(stream)->stream_tag; + snd_soc_dai_set_dma_data(dai, substream, dma_params); + + dev_dbg(dai->dev, "stream tag set in dma params=%d\n", + dma_params->stream_tag); + snd_pcm_set_sync(substream); + + return 0; +} + +static int skl_get_format(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct skl_dma_params *dma_params; + int format_val = 0; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + dma_params = snd_soc_dai_get_dma_data(codec_dai, substream); + if (dma_params) + format_val = dma_params->format; + + return format_val; +} + +static int skl_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + unsigned int format_val; + int err; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + if (hdac_stream(stream)->prepared) { + dev_dbg(dai->dev, "already stream is prepared - returning\n"); + return 0; + } + + format_val = skl_get_format(substream, dai); + dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n", + hdac_stream(stream)->stream_tag, format_val); + snd_hdac_stream_reset(hdac_stream(stream)); + + err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); + if (err < 0) + return err; + + err = snd_hdac_stream_setup(hdac_stream(stream)); + if (err < 0) + return err; + + hdac_stream(stream)->prepared = 1; + + return err; +} + +static int skl_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + ret = skl_substream_alloc_pages(ebus, substream, + params_buffer_bytes(params)); + if (ret < 0) + return ret; + + dev_dbg(dai->dev, "format_val, rate=%d, ch=%d, format=%d\n", + runtime->rate, runtime->channels, runtime->format); + + return 0; +} + +static void skl_pcm_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct skl_dma_params *dma_params = NULL; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + snd_hdac_ext_stream_release(stream, HDAC_EXT_STREAM_TYPE_COUPLED); + + dma_params = snd_soc_dai_get_dma_data(dai, substream); + /* + * now we should set this to NULL as we are freeing by the + * dma_params + */ + snd_soc_dai_set_dma_data(dai, substream, NULL); + + pm_runtime_mark_last_busy(dai->dev); + pm_runtime_put_autosuspend(dai->dev); + kfree(dma_params); +} + +static int skl_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + snd_hdac_stream_cleanup(hdac_stream(stream)); + hdac_stream(stream)->prepared = 0; + + return skl_substream_free_pages(ebus_to_hbus(ebus), substream); +} + +static struct snd_soc_dai_ops skl_pcm_dai_ops = { + .startup = skl_pcm_open, + .shutdown = skl_pcm_close, + .prepare = skl_pcm_prepare, + .hw_params = skl_pcm_hw_params, + .hw_free = skl_pcm_hw_free, +}; + +static struct snd_soc_dai_driver skl_platform_dai[] = { +{ + .name = "System Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "System Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "System Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Deepbuffer Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "Deepbuffer Playback", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "LowLatency Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "Low Latency Playback", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +}; + +static int skl_platform_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dev_dbg(rtd->cpu_dai->dev, "In %s:%s\n", __func__, + dai_link->cpu_dai_name); + + runtime = substream->runtime; + snd_soc_set_runtime_hwparams(substream, &azx_pcm_hw); + + return 0; +} + +static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_ext_stream *stream; + struct snd_pcm_substream *s; + bool start; + int sbits = 0; + unsigned long cookie; + struct hdac_stream *hstr; + + stream = get_hdac_ext_stream(substream); + hstr = hdac_stream(stream); + + dev_dbg(bus->dev, "In %s cmd=%d\n", __func__, cmd); + + if (!hstr->prepared) + return -EPIPE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + start = true; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + start = false; + break; + + default: + return -EINVAL; + } + + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + stream = get_hdac_ext_stream(s); + sbits |= 1 << hdac_stream(stream)->index; + snd_pcm_trigger_done(s, substream); + } + + spin_lock_irqsave(&bus->reg_lock, cookie); + + /* first, set SYNC bits of corresponding streams */ + snd_hdac_stream_sync_trigger(hstr, true, sbits, AZX_REG_SSYNC); + + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + stream = get_hdac_ext_stream(s); + if (start) + snd_hdac_stream_start(hdac_stream(stream), true); + else + snd_hdac_stream_stop(hdac_stream(stream)); + } + spin_unlock_irqrestore(&bus->reg_lock, cookie); + + snd_hdac_stream_sync(hstr, start, sbits); + + spin_lock_irqsave(&bus->reg_lock, cookie); + + /* reset SYNC bits */ + snd_hdac_stream_sync_trigger(hstr, false, sbits, AZX_REG_SSYNC); + if (start) + snd_hdac_stream_timecounter_init(hstr, sbits); + spin_unlock_irqrestore(&bus->reg_lock, cookie); + + return 0; +} + +/* calculate runtime delay from LPIB */ +static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus, + struct hdac_ext_stream *sstream, + unsigned int pos) +{ + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_stream *hstream = hdac_stream(sstream); + struct snd_pcm_substream *substream = hstream->substream; + int stream = substream->stream; + unsigned int lpib_pos = snd_hdac_stream_get_pos_lpib(hstream); + int delay; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + delay = pos - lpib_pos; + else + delay = lpib_pos - pos; + + if (delay < 0) { + if (delay >= hstream->delay_negative_threshold) + delay = 0; + else + delay += hstream->bufsize; + } + + if (delay >= hstream->period_bytes) { + dev_info(bus->dev, + "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", + delay, hstream->period_bytes); + delay = 0; + } + + return bytes_to_frames(substream->runtime, delay); +} + +static unsigned int skl_get_position(struct hdac_ext_stream *hstream, + int codec_delay) +{ + struct hdac_stream *hstr = hdac_stream(hstream); + struct snd_pcm_substream *substream = hstr->substream; + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + unsigned int pos; + int delay; + + /* use the position buffer as default */ + pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream)); + + if (pos >= hdac_stream(hstream)->bufsize) + pos = 0; + + if (substream->runtime) { + delay = skl_get_delay_from_lpib(ebus, hstream, pos) + + codec_delay; + substream->runtime->delay += delay; + } + + return pos; +} + +static snd_pcm_uframes_t skl_platform_pcm_pointer + (struct snd_pcm_substream *substream) +{ + struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream); + + return bytes_to_frames(substream->runtime, + skl_get_position(hstream, 0)); +} + +static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream, + u64 nsec) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_dai *codec_dai = rtd->codec_dai; + u64 codec_frames, codec_nsecs; + + if (!codec_dai->driver->ops->delay) + return nsec; + + codec_frames = codec_dai->driver->ops->delay(substream, codec_dai); + codec_nsecs = div_u64(codec_frames * 1000000000LL, + substream->runtime->rate); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + return nsec + codec_nsecs; + + return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0; +} + +static int skl_get_time_info(struct snd_pcm_substream *substream, + struct timespec *system_ts, struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report) +{ + struct hdac_ext_stream *sstream = get_hdac_ext_stream(substream); + struct hdac_stream *hstr = hdac_stream(sstream); + u64 nsec; + + if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && + (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) { + + snd_pcm_gettime(substream->runtime, system_ts); + + nsec = timecounter_read(&hstr->tc); + nsec = div_u64(nsec, 3); /* can be optimized */ + if (audio_tstamp_config->report_delay) + nsec = skl_adjust_codec_delay(substream, nsec); + + *audio_ts = ns_to_timespec(nsec); + + audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; + audio_tstamp_report->accuracy_report = 1; /* rest of struct is valid */ + audio_tstamp_report->accuracy = 42; /* 24MHzWallClk == 42ns resolution */ + + } else { + audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; + } + + return 0; +} + +static struct snd_pcm_ops skl_platform_ops = { + .open = skl_platform_open, + .ioctl = snd_pcm_lib_ioctl, + .trigger = skl_platform_pcm_trigger, + .pointer = skl_platform_pcm_pointer, + .get_time_info = skl_get_time_info, + .mmap = snd_pcm_lib_default_mmap, + .page = snd_pcm_sgbuf_ops_page, +}; + +static void skl_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +#define MAX_PREALLOC_SIZE (32 * 1024 * 1024) + +static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->cpu_dai; + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct snd_pcm *pcm = rtd->pcm; + unsigned int size; + int retval = 0; + struct skl *skl = ebus_to_skl(ebus); + + if (dai->driver->playback.channels_min || + dai->driver->capture.channels_min) { + /* buffer pre-allocation */ + size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024; + if (size > MAX_PREALLOC_SIZE) + size = MAX_PREALLOC_SIZE; + retval = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(skl->pci), + size, MAX_PREALLOC_SIZE); + if (retval) { + dev_err(dai->dev, "dma buffer allocationf fail\n"); + return retval; + } + } + + return retval; +} + +static struct snd_soc_platform_driver skl_platform_drv = { + .ops = &skl_platform_ops, + .pcm_new = skl_pcm_new, + .pcm_free = skl_pcm_free, +}; + +static const struct snd_soc_component_driver skl_component = { + .name = "pcm", +}; + +int skl_platform_register(struct device *dev) +{ + int ret; + + ret = snd_soc_register_platform(dev, &skl_platform_drv); + if (ret) { + dev_err(dev, "soc platform registration failed %d\n", ret); + return ret; + } + ret = snd_soc_register_component(dev, &skl_component, + skl_platform_dai, + ARRAY_SIZE(skl_platform_dai)); + if (ret) { + dev_err(dev, "soc component registration failed %d\n", ret); + snd_soc_unregister_platform(dev); + } + + return ret; + +} + +int skl_platform_unregister(struct device *dev) +{ + snd_soc_unregister_component(dev); + snd_soc_unregister_platform(dev); + return 0; +} diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h new file mode 100644 index 000000000000..cc0f3e263495 --- /dev/null +++ b/sound/soc/intel/skylake/skl.h @@ -0,0 +1,71 @@ +/* + * skl.h - HD Audio skylake defintions. + * + * Copyright (C) 2015 Intel Corp + * Author: Jeeja KP + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef __SOUND_SOC_SKL_H +#define __SOUND_SOC_SKL_H + +#include +#include + +#define SKL_SUSPEND_DELAY 2000 + +/* Vendor Specific Registers */ +#define AZX_REG_VS_EM1 0x1000 +#define AZX_REG_VS_INRC 0x1004 +#define AZX_REG_VS_OUTRC 0x1008 +#define AZX_REG_VS_FIFOTRK 0x100C +#define AZX_REG_VS_FIFOTRK2 0x1010 +#define AZX_REG_VS_EM2 0x1030 +#define AZX_REG_VS_EM3L 0x1038 +#define AZX_REG_VS_EM3U 0x103C +#define AZX_REG_VS_EM4L 0x1040 +#define AZX_REG_VS_EM4U 0x1044 +#define AZX_REG_VS_LTRC 0x1048 +#define AZX_REG_VS_D0I3C 0x104A +#define AZX_REG_VS_PCE 0x104B +#define AZX_REG_VS_L2MAGC 0x1050 +#define AZX_REG_VS_L2LAHPT 0x1054 +#define AZX_REG_VS_SDXDPIB_XBASE 0x1084 +#define AZX_REG_VS_SDXDPIB_XINTERVAL 0x20 +#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094 +#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20 + +struct skl { + struct hdac_ext_bus ebus; + struct pci_dev *pci; + + unsigned int init_failed:1; /* delayed init failed */ + struct platform_device *dmic_dev; +}; + +#define skl_to_ebus(s) (&(s)->ebus) +#define ebus_to_skl(sbus) \ + container_of(sbus, struct skl, sbus) + +/* to pass dai dma data */ +struct skl_dma_params { + u32 format; + u8 stream_tag; +}; + +int skl_platform_unregister(struct device *dev); +int skl_platform_register(struct device *dev); + +#endif /* __SOUND_SOC_SKL_H */