[Intel-gfx] [PATCH 3/7] ALSA: add shell for Intel HDMI LPE audio driver

Takashi Iwai tiwai at suse.de
Wed Dec 14 12:45:31 UTC 2016


On Mon, 12 Dec 2016 19:10:39 +0100,
Jerome Anand wrote:
> 
> On Baytrail and Cherrytrail, HDaudio may be fused out or disabled
> by the BIOS. This driver enables an alternate path to the i915
> display registers and DMA.
> 
> Although there is no hardware path between i915 display and LPE/SST
> audio clusters, this HDMI capability is referred to in the documentation
> as "HDMI LPE Audio" so we keep the name for consistency. There is no
> hardware path or control dependencies with the LPE/SST DSP functionality.
> 
> The hdmi-lpe-audio driver will be probed when the i915 driver creates
> a child platform device.
> 
> Since this driver is neither SoC nor PCI, a new x86 folder is added
> 
> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>
> Signed-off-by: Jerome Anand <jerome.anand at intel.com>

Why do we need a "shell" and indirect calls?
This is a small driver set, so it's not utterly unacceptable, but it
still makes things a bit more complicated than necessary, so I'd like
to understand the idea behind it.


> ---
>  sound/Kconfig                    |   2 +
>  sound/Makefile                   |   2 +-
>  sound/x86/Kconfig                |  16 +
>  sound/x86/Makefile               |   8 +
>  sound/x86/intel_hdmi_lpe_audio.c | 622 +++++++++++++++++++++++++++++++++++
>  sound/x86/intel_hdmi_lpe_audio.h | 692 +++++++++++++++++++++++++++++++++++++++
>  6 files changed, 1341 insertions(+), 1 deletion(-)
>  create mode 100644 sound/x86/Kconfig
>  create mode 100644 sound/x86/Makefile
>  create mode 100644 sound/x86/intel_hdmi_lpe_audio.c
>  create mode 100644 sound/x86/intel_hdmi_lpe_audio.h
> 
> diff --git a/sound/Kconfig b/sound/Kconfig
> index 5a240e0..ee2e69a 100644
> --- a/sound/Kconfig
> +++ b/sound/Kconfig
> @@ -108,6 +108,8 @@ source "sound/parisc/Kconfig"
>  
>  source "sound/soc/Kconfig"
>  
> +source "sound/x86/Kconfig"
> +
>  endif # SND
>  
>  menuconfig SOUND_PRIME
> diff --git a/sound/Makefile b/sound/Makefile
> index c41bdf5..6de45d2 100644
> --- a/sound/Makefile
> +++ b/sound/Makefile
> @@ -5,7 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o
>  obj-$(CONFIG_SOUND_PRIME) += oss/
>  obj-$(CONFIG_DMASOUND) += oss/
>  obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
> -	firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/
> +	firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/
>  obj-$(CONFIG_SND_AOA) += aoa/
>  
>  # This one must be compilable even if sound is configured out
> diff --git a/sound/x86/Kconfig b/sound/x86/Kconfig
> new file mode 100644
> index 0000000..182adf3
> --- /dev/null
> +++ b/sound/x86/Kconfig
> @@ -0,0 +1,16 @@
> +menuconfig SND_X86
> +	tristate "X86 sound devices"
> +	---help---
> +
> +	  X86 sound devices that don't fall under SoC or PCI categories
> +
> +if SND_X86
> +
> +config HDMI_LPE_AUDIO
> +	tristate "HDMI audio without HDaudio on Intel Atom platforms"
> +	depends on DRM_I915
> +        default n
> +        help
> +          Choose this option to support HDMI LPE Audio mode

Please fix the indentation.  Also a bit more description would be more
user-friendly.


> +
> +endif	# SND_X86
> diff --git a/sound/x86/Makefile b/sound/x86/Makefile
> new file mode 100644
> index 0000000..78b2ae1
> --- /dev/null
> +++ b/sound/x86/Makefile
> @@ -0,0 +1,8 @@
> +DRIVER_NAME := hdmi_lpe_audio
> +
> +ccflags-y += -Idrivers/gpu/drm/i915

Is it just for intel_lpe_audio.h?  Then rather put intel_lpe_audio.h
to include/drm.


> +$(DRIVER_NAME)-objs += \
> +	intel_hdmi_lpe_audio.o
> +
> +obj-$(CONFIG_HDMI_LPE_AUDIO) += $(DRIVER_NAME).o

Don't use substitution, it's rather confusing.
Also, we give "snd-" prefix for the sound driver in general.
If no big reason against it, keep this rule please.


> --- /dev/null
> +++ b/sound/x86/intel_hdmi_lpe_audio.c
(snip)
> @@ -0,0 +1,622 @@
> +/*
> + *  intel_hdmi_lpe_audio.c - Intel HDMI LPE audio driver for Atom platforms
> + *
> + *  Copyright (C) 2016 Intel Corp
> + *  Authors:
> + *		Jerome Anand <jerome.anand at intel.com>
> + *		Aravind Siddappaji <aravindx.siddappaji at 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.
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + */
> +
> +#define pr_fmt(fmt)	"hdmi_lpe_audio: " fmt

Better to use dev_*() variant.


> +#include <linux/platform_device.h>
> +#include <linux/irqreturn.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <sound/pcm.h>
> +#include <sound/core.h>
> +#include <sound/pcm_params.h>
> +#include <sound/initval.h>
> +#include <sound/control.h>
> +#include <sound/initval.h>
> +#include <drm/intel_lpe_audio.h>
> +#include "intel_hdmi_lpe_audio.h"
> +
> +/* globals*/
> +struct platform_device *gpdev;
> +int _hdmi_state;
> +union otm_hdmi_eld_t hdmi_eld;

For the global variables, give some prefix, as we have no proper
namespace in C.

And, what's the reason to keep copies of the state here?  Can't we
peek the pdata at each time?


> +
> +struct hdmi_lpe_audio_ctx {
> +	int irq;
> +	void __iomem *mmio_start;
> +	had_event_call_back had_event_callbacks;
> +	struct snd_intel_had_interface *had_interface;
> +	void *had_pvt_data;
> +	int tmds_clock_speed;
> +	unsigned int had_config_offset;
> +	int hdmi_audio_interrupt_mask;
> +	struct work_struct hdmi_audio_wq;
> +};
> +
> +static inline void hdmi_set_eld(void *eld)
> +{
> +	int size = (sizeof(hdmi_eld)) > HDMI_MAX_ELD_BYTES ?
> +				HDMI_MAX_ELD_BYTES :
> +				(sizeof(hdmi_eld));

How can the size differ...?  If any, it should be checked by
BUILD_BUG_ON() or such.

> +
> +	memcpy((void *)&hdmi_eld, eld, size);

Also, when copying from the graphics side, isn't it safer to pass the
given ELD size as well?  Then we can zero-clear the rest bytes if it's
short.

> +}
> +
> +static inline int hdmi_get_eld(void *eld)
> +{
> +	memcpy(eld, (void *)&hdmi_eld, sizeof(hdmi_eld));
> +
> +	{
> +		int i;
> +		uint8_t *eld_data = (uint8_t *)&hdmi_eld;
> +
> +		pr_debug("hdmi_get_eld:\n{{");
> +
> +		for (i = 0; i < sizeof(hdmi_eld); i++)
> +			pr_debug("0x%x, ", eld_data[i]);
> +
> +		pr_debug("}}\n");
> +	}

There is a hexdump debug print helper, too.


> +	return HAD_SUCCESS;

Let's use the normal return code, i.e. 0 or a negative error.


> +}
> +
> +
> +static inline struct hdmi_lpe_audio_ctx *get_hdmi_context(void)
> +{
> +	struct hdmi_lpe_audio_ctx *ctx;
> +
> +	ctx = platform_get_drvdata(gpdev);
> +	return ctx;
> +}

Hrm...  Why not storing the audio pdev in i915 pdata?
Then the notify callback can extract it easily.

(snip)
> +static int hdmi_lpe_audio_probe(struct platform_device *pdev)
> +{
> +	struct hdmi_lpe_audio_ctx *ctx;
> +	struct intel_hdmi_lpe_audio_pdata *pdata;
> +	int irq;
> +	struct resource *res_mmio;
> +	void __iomem *mmio_start;
> +	int ret = 0;
> +	unsigned long flag_irq;
> +	const struct pci_device_id cherryview_ids[] = {

Missing static.

> +		{PCI_DEVICE(0x8086, 0x22b0)},
> +		{PCI_DEVICE(0x8086, 0x22b1)},
> +		{PCI_DEVICE(0x8086, 0x22b2)},
> +		{PCI_DEVICE(0x8086, 0x22b3)},
> +		{}
> +	};
> +
> +	pr_debug("Enter %s\n", __func__);
> +
> +	/*TBD:remove globals*/
> +	gpdev = pdev;
> +	_hdmi_state = hdmi_connector_status_disconnected;
> +
> +	/* get resources */
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		pr_err("Could not get irq resource\n");
> +		return -ENODEV;
> +	}
> +
> +	res_mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res_mmio) {
> +		pr_err("Could not get IO_MEM resources\n");
> +		return -ENXIO;
> +	}
> +
> +	pr_debug("%s: mmio_start = 0x%x, mmio_end = 0x%x\n", __func__,
> +		(unsigned int)res_mmio->start, (unsigned int)res_mmio->end);
> +
> +	mmio_start = ioremap_nocache(res_mmio->start,
> +				(size_t)((res_mmio->end - res_mmio->start)
> +						+ 1));
> +	if (!mmio_start) {
> +		pr_err("Could not get ioremap\n");
> +		return -EACCES;
> +	}
> +
> +	/* setup interrupt handler */
> +	ret = request_irq(irq, display_pipe_interrupt_handler,
> +			0, /* FIXME: is IRQF_SHARED needed ? */

It's a irqchip, so no sharing is considered, right?
Then IRQF_SHARED is rather wrong.

> --- /dev/null
> +++ b/sound/x86/intel_hdmi_lpe_audio.h
(snip)
> +#define HMDI_LPE_AUDIO_DRIVER_NAME		"intel-hdmi-lpe-audio"
> +#define HAD_DRIVER_VERSION	"0.01.003"

The driver version string doesn't make sense in most cases for
in-kernel codes.

> +#define HAD_MAX_DEVICES		1
> +#define HAD_MIN_CHANNEL		2
> +#define HAD_MAX_CHANNEL		8
> +#define HAD_NUM_OF_RING_BUFS	4
> +
> +/* HDMI Audio LPE Error Codes */
> +#define HAD_SUCCESS		0
> +#define HAD_FAIL		1

Avoid the own definition.


> +/* Assume 192KHz, 8channel, 25msec period */
> +#define HAD_MAX_BUFFER		(600*1024)
> +#define HAD_MIN_BUFFER		(32*1024)
> +#define HAD_MAX_PERIODS		4
> +#define HAD_MIN_PERIODS		4
> +#define HAD_MAX_PERIOD_BYTES	(HAD_MAX_BUFFER/HAD_MIN_PERIODS)
> +#define HAD_MIN_PERIOD_BYTES	256
> +#define HAD_FIFO_SIZE		0 /* fifo not being used */
> +#define MAX_SPEAKERS		8
> +/* TODO: Add own tlv when channel map is ported for user space */
> +#define USE_ALSA_DEFAULT_TLV
> +
> +#define AUD_SAMPLE_RATE_32	32000
> +#define AUD_SAMPLE_RATE_44_1	44100
> +#define AUD_SAMPLE_RATE_48	48000
> +#define AUD_SAMPLE_RATE_88_2	88200
> +#define AUD_SAMPLE_RATE_96	96000
> +#define AUD_SAMPLE_RATE_176_4	176400
> +#define AUD_SAMPLE_RATE_192	192000
> +
> +#define HAD_MIN_RATE		AUD_SAMPLE_RATE_32
> +#define HAD_MAX_RATE		AUD_SAMPLE_RATE_192
> +
> +#define DIS_SAMPLE_RATE_25_2	25200
> +#define DIS_SAMPLE_RATE_27	27000
> +#define DIS_SAMPLE_RATE_54	54000
> +#define DIS_SAMPLE_RATE_74_25	74250
> +#define DIS_SAMPLE_RATE_148_5	148500
> +#define HAD_REG_WIDTH		0x08
> +#define HAD_MAX_HW_BUFS		0x04
> +#define HAD_MAX_DIP_WORDS		16
> +#define INTEL_HAD		"IntelHdmiLpeAudio"
> +
> +/* _AUD_CONFIG register MASK */
> +#define AUD_CONFIG_MASK_UNDERRUN	0xC0000000
> +#define AUD_CONFIG_MASK_SRDBG		0x00000002
> +#define AUD_CONFIG_MASK_FUNCRST		0x00000001
> +
> +#define MAX_CNT			0xFF
> +#define HAD_SUSPEND_DELAY	1000
> +
> +#define OTM_HDMI_ELD_SIZE 128
> +
> +union otm_hdmi_eld_t {
> +	uint8_t eld_data[OTM_HDMI_ELD_SIZE];
> +	#pragma pack(1)

Use __packed notion for the packed structs.


> +	struct {
> +		/* Byte[0] = ELD Version Number */
> +		union {
> +			uint8_t   byte0;
> +			struct {
> +				uint8_t reserved:3; /* Reserf */
> +				uint8_t eld_ver:5; /* ELD Version Number */
> +				/* 00000b - reserved
> +				 * 00001b - first rev, obsoleted
> +				 * 00010b - version 2, supporting CEA version
> +				 *			861D or below
> +				 * 00011b:11111b - reserved
> +				 * for future
> +				 */
> +			};
> +		};
> +
> +		/* Byte[1] = Vendor Version Field */
> +		union {
> +			uint8_t vendor_version;
> +			struct {
> +				uint8_t reserved1:3;
> +				uint8_t veld_ver:5; /* Version number of the ELD
> +						     * extension. This value is
> +						     * provisioned and unique to
> +						     * each vendor.
> +						     */
> +			};
> +		};
> +
> +		/* Byte[2] = Baseline Length field */
> +		uint8_t baseline_eld_length; /* Length of the Baseline structure
> +					      *	divided by Four.
> +					      */
> +
> +		/* Byte [3] = Reserved for future use */
> +		uint8_t byte3;
> +
> +		/* Starting of the BaseLine EELD structure
> +		 * Byte[4] = Monitor Name Length
> +		 */
> +		union {
> +			uint8_t byte4;
> +			struct {
> +				uint8_t mnl:5;
> +				uint8_t cea_edid_rev_id:3;
> +			};
> +		};
> +
> +		/* Byte[5] = Capabilities */
> +		union {
> +			uint8_t capabilities;
> +			struct {
> +				uint8_t hdcp:1; /* HDCP support */
> +				uint8_t ai_support:1;   /* AI support */
> +				uint8_t connection_type:2; /* Connection type
> +							    * 00 - HDMI
> +							    * 01 - DP
> +							    * 10 -11  Reserved
> +							    * for future
> +							    * connection types
> +							    */
> +				uint8_t sadc:4; /* Indicates number of 3 bytes
> +						 * Short Audio Descriptors.
> +						 */
> +			};
> +		};
> +
> +		/* Byte[6] = Audio Synch Delay */
> +		uint8_t audio_synch_delay; /* Amount of time reported by the
> +					    * sink that the video trails audio
> +					    * in milliseconds.
> +					    */
> +
> +		/* Byte[7] = Speaker Allocation Block */
> +		union {
> +			uint8_t speaker_allocation_block;
> +			struct {
> +				uint8_t flr:1; /*Front Left and Right channels*/
> +				uint8_t lfe:1; /*Low Frequency Effect channel*/
> +				uint8_t fc:1;  /*Center transmission channel*/
> +				uint8_t rlr:1; /*Rear Left and Right channels*/
> +				uint8_t rc:1; /*Rear Center channel*/
> +				uint8_t flrc:1; /*Front left and Right of Center
> +						 *transmission channels
> +						 */
> +				uint8_t rlrc:1; /*Rear left and Right of Center
> +						 *transmission channels
> +						 */
> +				uint8_t reserved3:1; /* Reserved */
> +			};
> +		};
> +
> +		/* Byte[8 - 15] - 8 Byte port identification value */
> +		uint8_t port_id_value[8];
> +
> +		/* Byte[16 - 17] - 2 Byte Manufacturer ID */
> +		uint8_t manufacturer_id[2];
> +
> +		/* Byte[18 - 19] - 2 Byte Product ID */
> +		uint8_t product_id[2];
> +
> +		/* Byte [20-83] - 64 Bytes of BaseLine Data */
> +		uint8_t mn_sand_sads[64]; /* This will include
> +					   * - ASCII string of Monitor name
> +					   * - List of 3 byte SADs
> +					   * - Zero padding
> +					   */
> +
> +		/* Vendor ELD Block should continue here!
> +		 * No Vendor ELD block defined as of now.
> +		 */
> +	};
> +	#pragma pack()
> +};

Well, we should have a single definition of ELD somewhere.
We can keep this locally until we have some unified header, though.

In anyway, better to avoid uint8_t.


thanks,

Takashi


More information about the Intel-gfx mailing list