diff --git a/packages/linux/package.mk b/packages/linux/package.mk index 15a826a7a5..eabfcac106 100644 --- a/packages/linux/package.mk +++ b/packages/linux/package.mk @@ -55,7 +55,7 @@ case "$LINUX" in PKG_DEPENDS_TARGET="$PKG_DEPENDS_TARGET imx6-status-led imx6-soc-fan irqbalanced" ;; *) - PKG_VERSION="4.6.3" + PKG_VERSION="4.7" PKG_URL="http://www.kernel.org/pub/linux/kernel/v4.x/$PKG_NAME-$PKG_VERSION.tar.xz" ;; esac diff --git a/packages/linux/patches/4.6.3/linux-056-add_Adaptec_eHome_Infrared_Receiver.patch b/packages/linux/patches/4.6.3/linux-056-add_Adaptec_eHome_Infrared_Receiver.patch deleted file mode 100644 index 4116b9ff86..0000000000 --- a/packages/linux/patches/4.6.3/linux-056-add_Adaptec_eHome_Infrared_Receiver.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -Naur linux-3.17.1/drivers/media/rc/mceusb.c linux-3.17.1.patch/drivers/media/rc/mceusb.c ---- linux-3.17.1/drivers/media/rc/mceusb.c 2014-10-15 12:29:30.000000000 +0200 -+++ linux-3.17.1.patch/drivers/media/rc/mceusb.c 2014-10-16 11:45:57.480176874 +0200 -@@ -188,6 +188,8 @@ - #define VENDOR_TWISTEDMELON 0x2596 - #define VENDOR_HAUPPAUGE 0x2040 - #define VENDOR_PCTV 0x2013 -+#define VENDOR_ADAPTEC 0x03f3 -+ - - enum mceusb_model_type { - MCE_GEN2 = 0, /* Most boards */ -@@ -401,6 +403,8 @@ - .driver_info = HAUPPAUGE_CX_HYBRID_TV }, - { USB_DEVICE(VENDOR_PCTV, 0x025e), - .driver_info = HAUPPAUGE_CX_HYBRID_TV }, -+ /* Adaptec / HP eHome Receiver */ -+ { USB_DEVICE(VENDOR_ADAPTEC, 0x0094) }, - - /* Terminating entry */ - { } diff --git a/packages/linux/patches/4.6.3/linux-063-xpad-add-3rdparty-controllers.patch b/packages/linux/patches/4.6.3/linux-063-xpad-add-3rdparty-controllers.patch deleted file mode 100644 index e05f92fa9a..0000000000 --- a/packages/linux/patches/4.6.3/linux-063-xpad-add-3rdparty-controllers.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 627e828fcf1bb9fc71dbbeca29acd33162efb448 Mon Sep 17 00:00:00 2001 -From: Matt DeVillier -Date: Wed, 2 Mar 2016 23:11:33 -0600 -Subject: [PATCH 1/1] linux/drivers/input/joystick/xpad: add support for 3rd party XBONE controllers - -linux/drivers/input/joystick/xpad: add support for 3rd party XBONE controllers - -Signed-off-by: Matt DeVillier ---- - drivers/input/joystick/xpad.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c -index e8a84d1..d1ab48a 100644 ---- a/drivers/input/joystick/xpad.c -+++ b/drivers/input/joystick/xpad.c -@@ -129,6 +129,7 @@ static const struct xpad_device { - { 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 }, - { 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE }, - { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE }, -+ { 0x0e6f, 0x0139, "Afterglow Prismatic Wired Controller", 0, XTYPE_XBOXONE }, - { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, - { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, - { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, -@@ -301,6 +302,7 @@ static struct usb_device_id xpad_table[] = { - XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */ - XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */ - XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */ -+ XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */ - XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */ - XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ - { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */ --- -2.5.0 diff --git a/packages/linux/patches/4.6.3/linux-223-Fix-video-artifacts-with-tt-3600-s2-usb.patch b/packages/linux/patches/4.6.3/linux-223-Fix-video-artifacts-with-tt-3600-s2-usb.patch deleted file mode 100644 index 7aaabc48c0..0000000000 --- a/packages/linux/patches/4.6.3/linux-223-Fix-video-artifacts-with-tt-3600-s2-usb.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff -Naur linux-3.7.9/drivers/media/usb/dvb-usb/pctv452e.c linux-3.7.9.patch/drivers/media/usb/dvb-usb/pctv452e.c ---- linux-3.7.9/drivers/media/usb/dvb-usb/pctv452e.c 2013-01-11 18:19:28.000000000 +0100 -+++ linux-3.7.9.patch/drivers/media/usb/dvb-usb/pctv452e.c 2013-01-16 10:35:01.131342123 +0100 -@@ -995,11 +995,11 @@ - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_ISOC, -- .count = 7, -+ .count = 4, - .endpoint = 0x02, - .u = { - .isoc = { -- .framesperurb = 4, -+ .framesperurb = 64, - .framesize = 940, - .interval = 1 - } diff --git a/packages/linux/patches/4.6.3/linux-230-elgato-eyetv-sat-v3.patch b/packages/linux/patches/4.6.3/linux-230-elgato-eyetv-sat-v3.patch deleted file mode 100644 index 2056a250f5..0000000000 --- a/packages/linux/patches/4.6.3/linux-230-elgato-eyetv-sat-v3.patch +++ /dev/null @@ -1,50 +0,0 @@ -diff --git a/drivers/media/usb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c -index 92e47d6..2e71136 100644 ---- a/drivers/media/usb/dvb-usb/az6027.c -+++ b/drivers/media/usb/dvb-usb/az6027.c -@@ -1090,6 +1090,7 @@ static struct usb_device_id az6027_usb_table[] = { - { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V2) }, - { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT) }, - { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT_V2) }, -+ { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT_V3) }, - { }, - }; - -@@ -1138,7 +1139,7 @@ static struct dvb_usb_device_properties az6027_properties = { - - .i2c_algo = &az6027_i2c_algo, - -- .num_device_descs = 7, -+ .num_device_descs = 8, - .devices = { - { - .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)", -@@ -1168,6 +1169,10 @@ static struct dvb_usb_device_properties az6027_properties = { - .name = "Elgato EyeTV Sat", - .cold_ids = { &az6027_usb_table[6], NULL }, - .warm_ids = { NULL }, -+ }, { -+ .name = "Elgato EyeTV Sat", -+ .cold_ids = { &az6027_usb_table[7], NULL }, -+ .warm_ids = { NULL }, - }, - { NULL }, - } - -warning: LF will be replaced by CRLF in az6027.c. -The file will have its original line endings in your working directory. -diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h -index c117fb3..7552f38 100644 ---- a/drivers/media/dvb-core/dvb-usb-ids.h -+++ b/drivers/media/dvb-core/dvb-usb-ids.h -@@ -365,6 +365,7 @@ - #define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 - #define USB_PID_ELGATO_EYETV_SAT 0x002a - #define USB_PID_ELGATO_EYETV_SAT_V2 0x0025 -+#define USB_PID_ELGATO_EYETV_SAT_V3 0x0036 - #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 - #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001 - #define USB_PID_FRIIO_WHITE 0x0001 - -warning: LF will be replaced by CRLF in dvb-usb-ids.h. -The file will have its original line endings in your working directory. diff --git a/packages/linux/patches/4.6.3/linux-003-no_dev_console.patch b/packages/linux/patches/4.7/linux-003-no_dev_console.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-003-no_dev_console.patch rename to packages/linux/patches/4.7/linux-003-no_dev_console.patch diff --git a/packages/linux/patches/4.6.3/linux-004-fix-build-with-gcc-5.patch b/packages/linux/patches/4.7/linux-004-fix-build-with-gcc-5.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-004-fix-build-with-gcc-5.patch rename to packages/linux/patches/4.7/linux-004-fix-build-with-gcc-5.patch diff --git a/packages/linux/patches/4.6.3/linux-051-ouya_controller_support.patch b/packages/linux/patches/4.7/linux-051-ouya_controller_support.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-051-ouya_controller_support.patch rename to packages/linux/patches/4.7/linux-051-ouya_controller_support.patch diff --git a/packages/linux/patches/4.6.3/linux-052-XBOX_remote_support.patch b/packages/linux/patches/4.7/linux-052-XBOX_remote_support.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-052-XBOX_remote_support.patch rename to packages/linux/patches/4.7/linux-052-XBOX_remote_support.patch diff --git a/packages/linux/patches/4.6.3/linux-053-spinelplus-remote-0.2.patch b/packages/linux/patches/4.7/linux-053-spinelplus-remote-0.2.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-053-spinelplus-remote-0.2.patch rename to packages/linux/patches/4.7/linux-053-spinelplus-remote-0.2.patch diff --git a/packages/linux/patches/4.6.3/linux-054-nuvoton_revert_d7b290a1056c5564eec8a1b169c6e84ff3.6.114c13.patch b/packages/linux/patches/4.7/linux-054-nuvoton_revert_d7b290a1056c5564eec8a1b169c6e84ff3.6.114c13.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-054-nuvoton_revert_d7b290a1056c5564eec8a1b169c6e84ff3.6.114c13.patch rename to packages/linux/patches/4.7/linux-054-nuvoton_revert_d7b290a1056c5564eec8a1b169c6e84ff3.6.114c13.patch diff --git a/packages/linux/patches/4.6.3/linux-057-Removed-MCE-customer-code-restriction-in-rc6-decode.patch b/packages/linux/patches/4.7/linux-057-Removed-MCE-customer-code-restriction-in-rc6-decode.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-057-Removed-MCE-customer-code-restriction-in-rc6-decode.patch rename to packages/linux/patches/4.7/linux-057-Removed-MCE-customer-code-restriction-in-rc6-decode.patch diff --git a/packages/linux/patches/4.6.3/linux-057-add_SMK_Manufacturing_Inc_Infrared_Receiver.patch b/packages/linux/patches/4.7/linux-057-add_SMK_Manufacturing_Inc_Infrared_Receiver.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-057-add_SMK_Manufacturing_Inc_Infrared_Receiver.patch rename to packages/linux/patches/4.7/linux-057-add_SMK_Manufacturing_Inc_Infrared_Receiver.patch diff --git a/packages/linux/patches/4.6.3/linux-058.05-hid_sony-add_autorepeat_for_PS3_remotes.patch b/packages/linux/patches/4.7/linux-058.05-hid_sony-add_autorepeat_for_PS3_remotes.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-058.05-hid_sony-add_autorepeat_for_PS3_remotes.patch rename to packages/linux/patches/4.7/linux-058.05-hid_sony-add_autorepeat_for_PS3_remotes.patch diff --git a/packages/linux/patches/4.6.3/linux-062-imon_pad_ignore_diagonal.patch b/packages/linux/patches/4.7/linux-062-imon_pad_ignore_diagonal.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-062-imon_pad_ignore_diagonal.patch rename to packages/linux/patches/4.7/linux-062-imon_pad_ignore_diagonal.patch diff --git a/packages/linux/patches/4.6.3/linux-203-stb0899_enable_low_symbol_rate.patch b/packages/linux/patches/4.7/linux-203-stb0899_enable_low_symbol_rate.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-203-stb0899_enable_low_symbol_rate.patch rename to packages/linux/patches/4.7/linux-203-stb0899_enable_low_symbol_rate.patch diff --git a/packages/linux/patches/4.6.3/linux-212-mantis_stb0899_faster_lock.patch b/packages/linux/patches/4.7/linux-212-mantis_stb0899_faster_lock.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-212-mantis_stb0899_faster_lock.patch rename to packages/linux/patches/4.7/linux-212-mantis_stb0899_faster_lock.patch diff --git a/packages/linux/patches/4.6.3/linux-221-ngene-octopus.todo b/packages/linux/patches/4.7/linux-221-ngene-octopus.todo similarity index 100% rename from packages/linux/patches/4.6.3/linux-221-ngene-octopus.todo rename to packages/linux/patches/4.7/linux-221-ngene-octopus.todo diff --git a/packages/linux/patches/4.6.3/linux-222-stb0899_signal_quality.patch b/packages/linux/patches/4.7/linux-222-stb0899_signal_quality.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-222-stb0899_signal_quality.patch rename to packages/linux/patches/4.7/linux-222-stb0899_signal_quality.patch diff --git a/packages/linux/patches/4.6.3/linux-227-ds3000-invalid-symbol-rate.patch b/packages/linux/patches/4.7/linux-227-ds3000-invalid-symbol-rate.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-227-ds3000-invalid-symbol-rate.patch rename to packages/linux/patches/4.7/linux-227-ds3000-invalid-symbol-rate.patch diff --git a/packages/linux/patches/4.6.3/linux-231-Terratec-Cinergy-S2-Rev.3.patch b/packages/linux/patches/4.7/linux-231-Terratec-Cinergy-S2-Rev.3.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-231-Terratec-Cinergy-S2-Rev.3.patch rename to packages/linux/patches/4.7/linux-231-Terratec-Cinergy-S2-Rev.3.patch diff --git a/packages/linux/patches/4.6.3/linux-950-saa716x_PCIe_interface_chipset.patch.todo b/packages/linux/patches/4.7/linux-950-saa716x_PCIe_interface_chipset.patch.todo similarity index 100% rename from packages/linux/patches/4.6.3/linux-950-saa716x_PCIe_interface_chipset.patch.todo rename to packages/linux/patches/4.7/linux-950-saa716x_PCIe_interface_chipset.patch.todo diff --git a/packages/linux/patches/4.6.3/linux-990.06-hda-Avoid-outputting-HDMI-audio-before-prepare-.patch b/packages/linux/patches/4.7/linux-990.06-hda-Avoid-outputting-HDMI-audio-before-prepare-.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-990.06-hda-Avoid-outputting-HDMI-audio-before-prepare-.patch rename to packages/linux/patches/4.7/linux-990.06-hda-Avoid-outputting-HDMI-audio-before-prepare-.patch diff --git a/packages/linux/patches/4.6.3/linux-999-i915-implement-passthrough-colors.patch b/packages/linux/patches/4.7/linux-999-i915-implement-passthrough-colors.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-999-i915-implement-passthrough-colors.patch rename to packages/linux/patches/4.7/linux-999-i915-implement-passthrough-colors.patch diff --git a/packages/linux/patches/4.6.3/linux-999-i915-use-legacy-turbo.patch b/packages/linux/patches/4.7/linux-999-i915-use-legacy-turbo.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-999-i915-use-legacy-turbo.patch rename to packages/linux/patches/4.7/linux-999-i915-use-legacy-turbo.patch diff --git a/packages/linux/patches/4.6.3/linux-999.02-0001-pm-disable-async-suspend-resume-by-default.patch b/packages/linux/patches/4.7/linux-999.02-0001-pm-disable-async-suspend-resume-by-default.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-999.02-0001-pm-disable-async-suspend-resume-by-default.patch rename to packages/linux/patches/4.7/linux-999.02-0001-pm-disable-async-suspend-resume-by-default.patch diff --git a/packages/linux/patches/4.6.3/linux-999.05-eMMC-Don-t-initialize-partitions-on-RPMB-flagged-are.patch b/packages/linux/patches/4.7/linux-999.05-eMMC-Don-t-initialize-partitions-on-RPMB-flagged-are.patch similarity index 100% rename from packages/linux/patches/4.6.3/linux-999.05-eMMC-Don-t-initialize-partitions-on-RPMB-flagged-are.patch rename to packages/linux/patches/4.7/linux-999.05-eMMC-Don-t-initialize-partitions-on-RPMB-flagged-are.patch diff --git a/projects/Generic/patches/linux/linux-030-BYT-CHT-SOC-audio-support.patch b/projects/Generic/patches/linux/linux-030-BYT-CHT-SOC-audio-support.patch deleted file mode 100644 index de6f16ffe9..0000000000 --- a/projects/Generic/patches/linux/linux-030-BYT-CHT-SOC-audio-support.patch +++ /dev/null @@ -1,5679 +0,0 @@ -From 09e9dd64f231da00bc01a7188ddf258cb6189958 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Wed, 2 Mar 2016 14:30:44 -0600 -Subject: [PATCH 02/14] drm: i915: remove intel_hdmi variable declaration - -'intel_hdmi' variable is redeclared, use same variable declared in -function scope. - -Signed-off-by: Pierre-Louis Bossart ---- - drivers/gpu/drm/i915/intel_hdmi.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c -index 1ab6f68..5196843c 100644 ---- a/drivers/gpu/drm/i915/intel_hdmi.c -+++ b/drivers/gpu/drm/i915/intel_hdmi.c -@@ -1429,7 +1429,7 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) - intel_hdmi_unset_edid(connector); - - if (intel_hdmi_set_edid(connector, live_status)) { -- struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); -+ intel_hdmi = intel_attached_hdmi(connector); - - hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; - status = connector_status_connected; - -From 9ad93c689dec85eb1bed23f635aead002594db81 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Tue, 1 Mar 2016 16:25:04 -0600 -Subject: [PATCH 03/14] drm/i915: Add headers for non-HDAudio HDMI interface - -Add header files for interface available on Baytrail and CherryTrail - -Initial code was downloaded from https://github.com/01org/baytrailaudio/ -...and had the changes to .config stripped and the revert on sound/init.c -done by David Henningson - -Clean-up, port to 4.5 and intel-drm by Pierre Bossart -CherryTrail support by Jerome Anand - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart ---- - drivers/gpu/drm/i915/hdmi_audio_if.h | 125 +++++++++++++++++++++++++++++++++++ - drivers/gpu/drm/i915/i915_drv.h | 31 +++++++++ - drivers/gpu/drm/i915/i915_reg.h | 22 ++++++ - drivers/gpu/drm/i915/intel_drv.h | 13 ++++ - 4 files changed, 191 insertions(+) - create mode 100644 drivers/gpu/drm/i915/hdmi_audio_if.h - -diff --git a/drivers/gpu/drm/i915/hdmi_audio_if.h b/drivers/gpu/drm/i915/hdmi_audio_if.h -new file mode 100644 -index 0000000..165bba5 ---- /dev/null -+++ b/drivers/gpu/drm/i915/hdmi_audio_if.h -@@ -0,0 +1,125 @@ -+/* -+ * Copyright (c) 2010, Intel Corporation. -+ * -+ * 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. -+ * -+ * This program is distributed in the hope 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. -+ * -+ * Authors: -+ * jim liu -+ * Uma Shankar -+ */ -+ -+ -+#ifndef __HDMI_AUDIO_IF_H -+#define __HDMI_AUDIO_IF_H -+ -+#include -+#include -+ -+/* HDMI AUDIO INTERRUPT TYPE */ -+#define HDMI_AUDIO_UNDERRUN (1UL<<0) -+#define HDMI_AUDIO_BUFFER_DONE (1UL<<1) -+ -+/* the monitor type HDMI or DVI */ -+#define MONITOR_TYPE_HDMI 1 -+#define MONITOR_TYPE_DVI 2 -+ -+extern int i915_hdmi_state; -+ -+enum had_caps_list { -+ HAD_GET_ELD = 1, -+ HAD_GET_SAMPLING_FREQ, -+ HAD_GET_DISPLAY_RATE, -+ HAD_GET_HDCP_STATUS, -+ HAD_GET_AUDIO_STATUS, -+ HAD_SET_ENABLE_AUDIO, -+ HAD_SET_DISABLE_AUDIO, -+ HAD_SET_ENABLE_AUDIO_INT, -+ HAD_SET_DISABLE_AUDIO_INT, -+ OTHERS_TBD, -+}; -+ -+enum had_event_type { -+ HAD_EVENT_HOT_PLUG = 1, -+ HAD_EVENT_HOT_UNPLUG, -+ HAD_EVENT_MODE_CHANGING, -+ HAD_EVENT_PM_CHANGING, -+ HAD_EVENT_AUDIO_BUFFER_DONE, -+ HAD_EVENT_AUDIO_BUFFER_UNDERRUN, -+ HAD_EVENT_QUERY_IS_AUDIO_BUSY, -+ HAD_EVENT_QUERY_IS_AUDIO_SUSPENDED, -+}; -+ -+/* -+ * HDMI Display Controller Audio Interface -+ * -+ */ -+typedef int (*had_event_call_back) (enum had_event_type event_type, -+ void *ctxt_info); -+ -+struct hdmi_audio_registers_ops { -+ int (*hdmi_audio_get_register_base)(uint32_t *reg_base); -+ int (*hdmi_audio_read_register)(uint32_t reg_addr, uint32_t *data); -+ int (*hdmi_audio_write_register)(uint32_t reg_addr, uint32_t data); -+ int (*hdmi_audio_read_modify)(uint32_t reg_addr, uint32_t data, -+ uint32_t mask); -+}; -+ -+struct hdmi_audio_query_set_ops { -+ int (*hdmi_audio_get_caps)(enum had_caps_list query_element, -+ void *capabilties); -+ int (*hdmi_audio_set_caps)(enum had_caps_list set_element, -+ void *capabilties); -+}; -+ -+typedef struct hdmi_audio_event { -+ int type; -+} hdmi_audio_event_t; -+ -+struct snd_intel_had_interface { -+ const char *name; -+ int (*query)(void *had_data, hdmi_audio_event_t event); -+ int (*suspend)(void *had_data, hdmi_audio_event_t event); -+ int (*resume)(void *had_data); -+}; -+ -+struct hdmi_audio_priv { -+ struct drm_device *dev; -+ u32 hdmi_reg; -+ u32 hdmi_lpe_audio_reg; -+ bool is_hdcp_supported; -+ bool hdmi_hpd_connected; -+ int monitor_type; -+ void *context; -+ int pipe; -+}; -+ -+extern void i915_hdmi_audio_init(struct hdmi_audio_priv *p_hdmi_priv); -+ -+extern bool mid_hdmi_audio_is_busy(struct drm_device *dev); -+extern bool mid_hdmi_audio_suspend(struct drm_device *dev); -+extern void mid_hdmi_audio_resume(struct drm_device *dev); -+extern void mid_hdmi_audio_signal_event(struct drm_device *dev, -+ enum had_event_type event); -+ -+/* Added for HDMI Audio */ -+extern void hdmi_get_eld(uint8_t *eld); -+extern struct hdmi_audio_priv *get_hdmi_priv(void); -+extern void hdmi_get_eld(uint8_t *eld); -+extern int i915_enable_hdmi_audio_int(struct drm_device *dev); -+extern int i915_disable_hdmi_audio_int(struct drm_device *dev); -+extern int mid_hdmi_audio_setup( -+ had_event_call_back audio_callbacks, -+ struct hdmi_audio_registers_ops *reg_ops, -+ struct hdmi_audio_query_set_ops *query_ops); -+extern int mid_hdmi_audio_register( -+ struct snd_intel_had_interface *driver, -+ void *had_data); -+ -+#endif /* __HDMI_AUDIO_IF_H */ -diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h -index daba7eb..b7df2d36 100644 ---- a/drivers/gpu/drm/i915/i915_drv.h -+++ b/drivers/gpu/drm/i915/i915_drv.h -@@ -53,6 +53,7 @@ - #include - #include - #include "intel_guc.h" -+#include "hdmi_audio_if.h" - - /* General customization: - */ -@@ -1188,6 +1189,18 @@ struct intel_gen6_power_mgmt { - struct mutex hw_lock; - }; - -+/* Runtime power management related */ -+struct intel_gen7_rpm { -+ /* To track (num of get calls - num of put calls) -+ * made by procfs -+ */ -+ atomic_t procfs_count; -+ /* To make sure ring get/put are in pair */ -+ bool ring_active; -+ struct proc_dir_entry *i915_proc_dir; -+ struct proc_dir_entry *i915_proc_file; -+}; -+ - /* defined intel_pm.c */ - extern spinlock_t mchdev_lock; - -@@ -2003,6 +2016,19 @@ struct drm_i915_private { - - struct intel_encoder *dig_port_map[I915_MAX_PORTS]; - -+ /* Added for HDMI Audio */ -+ had_event_call_back had_event_callbacks; -+ struct snd_intel_had_interface *had_interface; -+ void *had_pvt_data; -+ int tmds_clock_speed; -+ int hdmi_audio_interrupt_mask; -+ struct work_struct hdmi_audio_wq; -+ -+ u32 hotplug_status; -+ -+ /* Runtime power management related */ -+ struct intel_gen7_rpm rpm; -+ - /* - * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch - * will be rejected. Instead look for a better place. -@@ -3522,6 +3548,11 @@ int intel_freq_opcode(struct drm_i915_private *dev_priv, int val); - } while (upper != old_upper && loop++ < 2); \ - (u64)upper << 32 | lower; }) - -+int i915_rpm_get_disp(struct drm_device *dev); -+int i915_rpm_put_disp(struct drm_device *dev); -+ -+bool i915_is_device_active(struct drm_device *dev); -+ - #define POSTING_READ(reg) (void)I915_READ_NOTRACE(reg) - #define POSTING_READ16(reg) (void)I915_READ16_NOTRACE(reg) - -diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h -index 363bd79d..ee12840 100644 ---- a/drivers/gpu/drm/i915/i915_reg.h -+++ b/drivers/gpu/drm/i915/i915_reg.h -@@ -2062,7 +2062,25 @@ enum skl_disp_power_wells { - #define I915_WINVALID_INTERRUPT (1<<1) - #define I915_USER_INTERRUPT (1<<1) - #define I915_ASLE_INTERRUPT (1<<0) -+#define I915_LPE_AUDIO_HDMI_STATUS_A _MMIO(dev_priv->info.display_mmio_offset + 0x65064) -+#define I915_LPE_AUDIO_HDMI_STATUS_B _MMIO(dev_priv->info.display_mmio_offset + 0x65864) -+#define I915_LPE_AUDIO_HDMI_STATUS_C _MMIO(dev_priv->info.display_mmio_offset + 0x65964) -+#define I915_HDMI_AUDIO_UNDERRUN (1UL<<31) -+#define I915_HDMI_AUDIO_BUFFER_DONE (1UL<<29) - #define I915_BSD_USER_INTERRUPT (1<<25) -+#define I915_HDMI_AUDIO_UNDERRUN_ENABLE (1UL<<15) -+ -+#define I915_HDMI_AUDIO_LPE_C_CONFIG 0x65900 -+#define I915_HDMI_AUDIO_LPE_B_CONFIG 0x65800 -+#define I915_HDMI_AUDIO_LPE_A_CONFIG 0x65000 -+ -+#define HDMI_LPE_AUDIO_PIPE_OFFSET 0x100 -+#define HDMI_LPE_AUDIO_PIPE_BC_OFFSET(pipe) \ -+ (I915_LPE_AUDIO_HDMI_STATUS_B + \ -+ (pipe - 1) * HDMI_LPE_AUDIO_PIPE_OFFSET) -+#define I915_LPE_AUDIO_HDMI_STATUS(pipe) \ -+ (pipe ? (HDMI_LPE_AUDIO_PIPE_BC_OFFSET(pipe)) : \ -+ I915_LPE_AUDIO_HDMI_STATUS_A) - - #define GEN6_BSD_RNCID _MMIO(0x12198) - -@@ -3371,6 +3389,9 @@ enum skl_disp_power_wells { - #define _GEN3_SDVOC 0x61160 - #define GEN3_SDVOB _MMIO(_GEN3_SDVOB) - #define GEN3_SDVOC _MMIO(_GEN3_SDVOC) -+#define HDMIB (dev_priv->info.display_mmio_offset + 0x61140) -+#define HDMIC (dev_priv->info.display_mmio_offset + 0x61160) -+#define HDMID (dev_priv->info.display_mmio_offset + 0x6116C) - #define GEN4_HDMIB GEN3_SDVOB - #define GEN4_HDMIC GEN3_SDVOC - #define VLV_HDMIB _MMIO(VLV_DISPLAY_BASE + 0x61140) -@@ -3380,6 +3401,7 @@ enum skl_disp_power_wells { - #define PCH_HDMIB PCH_SDVOB - #define PCH_HDMIC _MMIO(0xe1150) - #define PCH_HDMID _MMIO(0xe1160) -+#define PORT_ENABLE (1 << 31) - - #define PORT_DFT_I9XX _MMIO(0x61150) - #define DC_BALANCE_RESET (1 << 25) -diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h -index 9d0770c..bb991acd 100644 ---- a/drivers/gpu/drm/i915/intel_drv.h -+++ b/drivers/gpu/drm/i915/intel_drv.h -@@ -700,6 +700,14 @@ struct cxsr_latency { - #define to_intel_plane_state(x) container_of(x, struct intel_plane_state, base) - #define intel_fb_obj(x) (x ? to_intel_framebuffer(x)->obj : NULL) - -+/* HDMI bits are shared with the DP bits */ -+#define HDMIB_HOTPLUG_LIVE_STATUS (1 << 29) -+#define HDMIC_HOTPLUG_LIVE_STATUS (1 << 28) -+#define HDMID_HOTPLUG_LIVE_STATUS (1 << 27) -+#define HDMI_LIVE_STATUS_BASE 30 -+#define HDMI_LIVE_STATUS_DELAY_STEP 10 -+#define HDMI_EDID_RETRY_COUNT 3 -+ - struct intel_hdmi { - i915_reg_t hdmi_reg; - int ddc_bus; -@@ -711,6 +719,9 @@ struct intel_hdmi { - bool rgb_quant_range_selectable; - enum hdmi_picture_aspect aspect_ratio; - struct intel_connector *attached_connector; -+ struct edid *edid; -+ uint32_t edid_mode_count; -+ - void (*write_infoframe)(struct drm_encoder *encoder, - enum hdmi_infoframe_type type, - const void *frame, ssize_t len); -@@ -1141,6 +1152,8 @@ intel_rotation_90_or_270(unsigned int rotation) - - void intel_create_rotation_property(struct drm_device *dev, - struct intel_plane *plane); -+void chv_set_lpe_audio_reg_pipe(struct drm_device *dev, -+ int encoder_type, enum port port); - - /* shared dpll functions */ - struct intel_shared_dpll *intel_crtc_to_shared_dpll(struct intel_crtc *crtc); - -From 897ed8d50c83d0186a1e639b415301c979b696f5 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Tue, 1 Mar 2016 16:25:04 -0600 -Subject: [PATCH 04/14] drm/i915: changes for non-HDAudio HDMI interface - -Changes to existing code for interface available on Baytrail and -CherryTrail - -This driver was downloaded from https://github.com/01org/baytrailaudio/ - -...and had the changes to .config stripped and the revert on sound/init.c - -Also squashed change from Toyo Abe to fix typos and underrun issues -To enable interrupt, IER, IIR, and IMR must be configured appropriately. -IER setting for hdmi_audio was missing. -This fixes the following warning in dmesg. -[ 302.369965] had: Driver detected 2 missed buffer done interrupt(s)!!!! - -Cleanup, correction for PIPE_A/PIPE_B inversions, -port to 4.5 and intel-drm by Pierre Bossart -CherryTrail support by Jerome Anand - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart -Signed-off-by: Toyo Abe ---- - drivers/gpu/drm/i915/i915_irq.c | 151 +++++++++++++++++++++++++ - drivers/gpu/drm/i915/intel_display.c | 86 ++++++++++++++ - drivers/gpu/drm/i915/intel_hdmi.c | 211 ++++++++++++++++++++++++++++++++++- - 3 files changed, 446 insertions(+), 2 deletions(-) - -diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c -index d1a46ef..1cd07e3 100644 ---- a/drivers/gpu/drm/i915/i915_irq.c -+++ b/drivers/gpu/drm/i915/i915_irq.c -@@ -603,6 +603,42 @@ i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe, - __i915_disable_pipestat(dev_priv, pipe, enable_mask, status_mask); - } - -+/* Added for HDMI AUDIO */ -+void -+i915_enable_lpe_pipestat(struct drm_i915_private *dev_priv, int pipe) -+{ -+ u32 mask; -+ -+ mask = dev_priv->hdmi_audio_interrupt_mask; -+ mask |= I915_HDMI_AUDIO_UNDERRUN | I915_HDMI_AUDIO_BUFFER_DONE; -+ /* Enable the interrupt, clear any pending status */ -+ if (IS_CHERRYVIEW(dev_priv->dev)) { -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_C, mask); -+ POSTING_READ(I915_LPE_AUDIO_HDMI_STATUS_C); -+ } else { -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_A, mask); -+ POSTING_READ(I915_LPE_AUDIO_HDMI_STATUS_A); -+ } -+} -+ -+void -+i915_disable_lpe_pipestat(struct drm_i915_private *dev_priv, int pipe) -+{ -+ u32 mask; -+ -+ mask = dev_priv->hdmi_audio_interrupt_mask; -+ mask |= I915_HDMI_AUDIO_UNDERRUN | I915_HDMI_AUDIO_BUFFER_DONE; -+ /* Disable the interrupt, clear any pending status */ -+ if (IS_CHERRYVIEW(dev_priv->dev)) { -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_C, mask); -+ POSTING_READ(I915_LPE_AUDIO_HDMI_STATUS_C); -+ -+ } else { -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_A, mask); -+ POSTING_READ(I915_LPE_AUDIO_HDMI_STATUS_A); -+ } -+} -+ - /** - * i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion - * @dev: drm device -@@ -1644,6 +1680,24 @@ static bool intel_pipe_handle_vblank(struct drm_device *dev, enum pipe pipe) - return true; - } - -+static inline -+void i915_notify_audio_buffer_status(struct drm_device *dev, const i915_reg_t reg) -+{ -+ u32 lpe_stream = 0; -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ lpe_stream = I915_READ(reg); -+ if (lpe_stream & I915_HDMI_AUDIO_UNDERRUN) { -+ I915_WRITE(reg, I915_HDMI_AUDIO_UNDERRUN); -+ mid_hdmi_audio_signal_event(dev, -+ HAD_EVENT_AUDIO_BUFFER_UNDERRUN); -+ } -+ if (lpe_stream & I915_HDMI_AUDIO_BUFFER_DONE) { -+ I915_WRITE(reg, I915_HDMI_AUDIO_BUFFER_DONE); -+ mid_hdmi_audio_signal_event(dev, -+ HAD_EVENT_AUDIO_BUFFER_DONE); -+ } -+} -+ - static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir) - { - struct drm_i915_private *dev_priv = dev->dev_private; -@@ -1719,6 +1773,23 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir) - intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); - } - -+ if (IS_CHERRYVIEW(dev)) { -+ // FIXME: plb: why are pipes and status mapped this way? -+ if (iir & I915_LPE_PIPE_C_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_C); -+ if (iir & I915_LPE_PIPE_B_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_B); -+ if (iir & I915_LPE_PIPE_A_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_A); -+ } else { -+ if (iir & I915_LPE_PIPE_B_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_A); -+ } -+ - if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) - gmbus_irq_handler(dev); - } -@@ -2804,6 +2875,72 @@ static void gen8_disable_vblank(struct drm_device *dev, unsigned int pipe) - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - } - -+/* Added for HDMI Audio */ -+int i915_enable_hdmi_audio_int(struct drm_device *dev) -+{ -+ struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; -+ unsigned long irqflags; -+ u32 imr, int_bit; -+ int pipe = 1; -+ -+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); -+ i915_enable_lpe_pipestat(dev_priv, pipe); -+ -+ imr = I915_READ(VLV_IMR); -+ -+ if (IS_CHERRYVIEW(dev_priv->dev)) { -+ // FIXME: plb: looks wrong -+ -+ //imr &= ~I915_LPE_PIPE_C_INTERRUPT; -+ int_bit = (pipe ? (I915_LPE_PIPE_B_INTERRUPT >> -+ ((pipe - 1) * 9)) : -+ I915_LPE_PIPE_A_INTERRUPT); -+ imr &= ~int_bit; -+ } else { -+ /* Audio is on Stream A but uses HDMI PIPE B */ -+ imr &= ~I915_LPE_PIPE_B_INTERRUPT; -+ } -+ -+ I915_WRITE(VLV_IMR, imr); -+ I915_WRITE(VLV_IER, ~imr); -+ POSTING_READ(VLV_IER); -+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); -+ -+ return 0; -+} -+ -+/* Added for HDMI Audio */ -+int i915_disable_hdmi_audio_int(struct drm_device *dev) -+{ -+ struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; -+ unsigned long irqflags; -+ u32 imr, int_bit; -+ int pipe = 1; -+ -+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); -+ imr = I915_READ(VLV_IMR); -+ -+ if (IS_CHERRYVIEW(dev_priv->dev)) { -+ // FIXME: plb: looks wrong, should have other interrupts as well? -+ //imr |= I915_LPE_PIPE_C_INTERRUPT; -+ int_bit = (pipe ? (I915_LPE_PIPE_B_INTERRUPT >> -+ ((pipe - 1) * 9)) : -+ I915_LPE_PIPE_A_INTERRUPT); -+ imr |= int_bit; -+ } -+ else -+ imr |= I915_LPE_PIPE_B_INTERRUPT; -+ -+ I915_WRITE(VLV_IER, ~imr); -+ I915_WRITE(VLV_IMR, imr); -+ POSTING_READ(VLV_IMR); -+ -+ i915_disable_lpe_pipestat(dev_priv, pipe); -+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); -+ -+ return 0; -+} -+ - static bool - ring_idle(struct intel_engine_cs *ring, u32 seqno) - { -@@ -3618,6 +3755,7 @@ static void valleyview_display_irqs_install(struct drm_i915_private *dev_priv) - u32 pipestat_mask; - u32 iir_mask; - enum pipe pipe; -+ u32 lpe_status_clear; - - pipestat_mask = PIPESTAT_INT_STATUS_MASK | - PIPE_FIFO_UNDERRUN_STATUS; -@@ -3638,12 +3776,25 @@ static void valleyview_display_irqs_install(struct drm_i915_private *dev_priv) - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; - if (IS_CHERRYVIEW(dev_priv)) - iir_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT; -+ -+ if (IS_CHERRYVIEW(dev_priv->dev)) -+ // FIXME: plb: looks wrong: what about other interrupts -+ iir_mask |= I915_LPE_PIPE_C_INTERRUPT; -+ - dev_priv->irq_mask &= ~iir_mask; - - I915_WRITE(VLV_IIR, iir_mask); - I915_WRITE(VLV_IIR, iir_mask); - I915_WRITE(VLV_IER, ~dev_priv->irq_mask); - I915_WRITE(VLV_IMR, dev_priv->irq_mask); -+ -+ lpe_status_clear = I915_HDMI_AUDIO_UNDERRUN | -+ I915_HDMI_AUDIO_BUFFER_DONE; -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_A, lpe_status_clear); -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_B, lpe_status_clear); -+ if (IS_CHERRYVIEW(dev_priv->dev)) -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_C, lpe_status_clear); -+ - POSTING_READ(VLV_IMR); - } - -diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c -index 0104a06..82584aa 100644 ---- a/drivers/gpu/drm/i915/intel_display.c -+++ b/drivers/gpu/drm/i915/intel_display.c -@@ -7960,6 +7960,16 @@ static int i9xx_crtc_compute_clock(struct intel_crtc *crtc, - num_connectors); - } - -+ /* Added for HDMI Audio */ -+ if ((IS_CHERRYVIEW(dev)) || (IS_VALLEYVIEW(dev))) { -+ if(intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) { -+ dev_priv->tmds_clock_speed = crtc_state->port_clock; -+ -+ mid_hdmi_audio_signal_event(dev_priv->dev, -+ HAD_EVENT_MODE_CHANGING); -+ } -+ } -+ - return 0; - } - -@@ -14616,6 +14626,82 @@ static void intel_setup_outputs(struct drm_device *dev) - drm_helper_move_panel_connectors_to_head(dev); - } - -+void chv_set_lpe_audio_reg_pipe(struct drm_device *dev, -+ int encoder_type, enum port port) -+{ -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ struct intel_encoder *intel_encoder; -+ struct hdmi_audio_priv *hdmi_priv = get_hdmi_priv(); -+ -+ if(!hdmi_priv) { -+ DRM_DEBUG_KMS("hdmi_priv was never allocated\n"); -+ return; -+ } -+ -+ /* -+ * Due to hardware limitaion, Port D will always -+ * be driven by Pipe C. So Port B and Port C will -+ * be driven by either Pipe A or PipeB, depending -+ * on whether the LFP is MIPI or EDP. -+ */ -+ -+ if (port == PORT_D) { -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_C_CONFIG; -+ hdmi_priv->pipe = PIPE_C; -+ if (encoder_type == INTEL_OUTPUT_HDMI) -+ hdmi_priv->hdmi_reg = HDMID; -+ //else -+ // hdmi_priv->hdmi_reg = CHV_DP_D; -+ } else { -+ list_for_each_entry(intel_encoder, &dev-> -+ mode_config.encoder_list, base.head) { -+ -+ /* -+ * MIPI always comes on Pipe A or Pipe B -+ * depending on Port A or Port C and EDP -+ * comes on Pipe B. So the other pipe -+ * will only be able to drive the DP. -+ * MIPI on Port A is driven by Pipe A -+ * and MIPI on Port C is driven by -+ * Pipe B. So the other pipe will -+ * drive DP. -+ */ -+ -+ if (intel_encoder->type == INTEL_OUTPUT_EDP) { -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_A_CONFIG; -+ hdmi_priv->pipe = PIPE_A; -+ break; -+ } else if (intel_encoder->type == INTEL_OUTPUT_DSI && -+ dev_priv->vbt.dsi.port == DVO_PORT_MIPIA) { -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_B_CONFIG; -+ hdmi_priv->pipe = PIPE_B; -+ break; -+ } else if (intel_encoder->type == INTEL_OUTPUT_DSI && -+ dev_priv->vbt.dsi.port == DVO_PORT_MIPIC) { -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_A_CONFIG; -+ hdmi_priv->pipe = PIPE_A; -+ break; -+ } -+ } -+ -+ if (port == PORT_B) { -+ if (encoder_type == INTEL_OUTPUT_HDMI) -+ hdmi_priv->hdmi_reg = HDMIB; -+ //else -+ // hdmi_priv->hdmi_reg = VLV_DP_B; -+ } else { -+ if (encoder_type == INTEL_OUTPUT_HDMI) -+ hdmi_priv->hdmi_reg = HDMIC; -+ //else -+ // hdmi_priv->hdmi_reg = VLV_DP_C; -+ } -+ } -+} -+ - static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) - { - struct drm_device *dev = fb->dev; -diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c -index 5196843c..cc41d81 100644 ---- a/drivers/gpu/drm/i915/intel_hdmi.c -+++ b/drivers/gpu/drm/i915/intel_hdmi.c -@@ -38,6 +38,8 @@ - #include - #include "i915_drv.h" - -+static int i915_notify_had; -+ - static struct drm_device *intel_hdmi_to_dev(struct intel_hdmi *intel_hdmi) - { - return hdmi_to_dig_port(intel_hdmi)->base.base.dev; -@@ -1394,6 +1396,124 @@ intel_hdmi_set_edid(struct drm_connector *connector, bool force) - return connected; - } - -+static bool vlv_hdmi_live_status(struct drm_device *dev, -+ struct intel_hdmi *intel_hdmi) -+{ -+ uint32_t bit; -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ struct intel_digital_port *intel_dig_port = -+ hdmi_to_dig_port(intel_hdmi); -+ -+ DRM_DEBUG_KMS("Reading Live status"); -+ switch (intel_dig_port->port) { -+ case PORT_B: -+ bit = HDMIB_HOTPLUG_LIVE_STATUS; -+ break; -+ case PORT_C: -+ bit = HDMIC_HOTPLUG_LIVE_STATUS; -+ break; -+ case PORT_D: -+ bit = HDMID_HOTPLUG_LIVE_STATUS; -+ break; -+ default: -+ bit = 0; -+ } -+ -+ /* Return results in trems of connector */ -+ return I915_READ(PORT_HOTPLUG_STAT) & bit; -+} -+ -+ -+/* -+ * intel_hdmi_live_status: detect live status of HDMI -+ * if device is gen 6 and above, read the live status reg -+ * else, do not block the detection, return true -+ */ -+static bool intel_hdmi_live_status(struct drm_connector *connector) -+{ -+ struct drm_device *dev = connector->dev; -+ struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); -+ -+ if (INTEL_INFO(dev)->gen > 6) { -+ /* Todo: Implement for other Gen 6+ archs*/ -+ if (IS_VALLEYVIEW(dev)) -+ return vlv_hdmi_live_status(dev, intel_hdmi); -+ } -+ -+ return true; -+} -+ -+/* Read DDC and get EDID */ -+struct edid *intel_hdmi_get_edid(struct drm_connector *connector, bool force) -+{ -+ bool current_state = false; -+ bool saved_state = false; -+ -+ struct edid *new_edid = NULL; -+ struct i2c_adapter *adapter = NULL; -+ struct drm_device *dev = connector->dev; -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); -+ u32 hotplug_status = dev_priv->hotplug_status; -+ enum port hdmi_port = hdmi_to_dig_port(intel_hdmi)->port; -+ unsigned char retry = HDMI_EDID_RETRY_COUNT; -+ -+ if (!intel_hdmi) { -+ DRM_ERROR("Invalid input to get hdmi\n"); -+ return NULL; -+ } -+ -+ /* Get the saved status from top half */ -+ saved_state = hotplug_status & (1 << (HDMI_LIVE_STATUS_BASE - hdmi_port)); -+ -+ /* -+ * Few monitors are slow to respond on EDID and live status, -+ * so read live status multiple times within a max delay of 30ms -+ */ -+ do { -+ mdelay(HDMI_LIVE_STATUS_DELAY_STEP); -+ current_state = intel_hdmi_live_status(connector); -+ if (current_state) -+ break; -+ } while (retry--); -+ -+ /* Compare current status, and saved status in top half */ -+ if (current_state != saved_state) -+ DRM_DEBUG_DRIVER("Warning: Saved HDMI status != current status"); -+ -+ /* Read EDID if live status or saved status is up, or we are forced */ -+ if (current_state || saved_state || force) { -+ -+ adapter = intel_gmbus_get_adapter(dev_priv, -+ intel_hdmi->ddc_bus); -+ if (!adapter) { -+ DRM_ERROR("Get_hdmi cant get adapter\n"); -+ return NULL; -+ } -+ -+ /* -+ * Few monitors issue EDID after some delay, so give them -+ * some chances, but within 30ms -+ */ -+ retry = 3; -+READ_EDID: -+ new_edid = drm_get_edid(connector, adapter); -+ if (!new_edid) { -+ if (retry--) { -+ mdelay(HDMI_LIVE_STATUS_DELAY_STEP); -+ goto READ_EDID; -+ } -+ -+ DRM_ERROR("Get_hdmi cant read edid\n"); -+ return NULL; -+ } -+ -+ DRM_DEBUG_KMS("Live status up, got EDID"); -+ } -+ -+ return new_edid; -+} -+ - static enum drm_connector_status - intel_hdmi_detect(struct drm_connector *connector, bool force) - { -@@ -1402,6 +1522,8 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) - struct drm_i915_private *dev_priv = to_i915(connector->dev); - bool live_status = false; - unsigned int try; -+ bool inform_audio = false; -+ struct drm_device *dev = connector->dev; - - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, connector->name); -@@ -1438,6 +1560,31 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) - - intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS); - -+ /* Need to inform audio about the event */ -+ intel_hdmi = intel_attached_hdmi(connector); -+ if (intel_hdmi->has_audio) -+ inform_audio = true; -+ -+ if (status == connector_status_connected) { -+ if (intel_hdmi->has_audio) -+ i915_notify_had = 1; -+ } else { -+ struct intel_digital_port *intel_dig_port = -+ hdmi_to_dig_port(intel_hdmi); -+ -+ chv_set_lpe_audio_reg_pipe(dev, INTEL_OUTPUT_HDMI, -+ intel_dig_port->port); -+ /* Send a disconnect event to audio */ -+ if (inform_audio) { -+ DRM_DEBUG_DRIVER("Sending event to audio"); -+ mid_hdmi_audio_signal_event(dev_priv->dev, -+ HAD_EVENT_HOT_UNPLUG); -+ } -+ } -+ -+ if (IS_VALLEYVIEW(dev)) -+ i915_hdmi_state = status; -+ - return status; - } - -@@ -1461,12 +1608,29 @@ intel_hdmi_force(struct drm_connector *connector) - static int intel_hdmi_get_modes(struct drm_connector *connector) - { - struct edid *edid; -+ struct intel_encoder *intel_encoder = intel_attached_encoder(connector); -+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); -+ struct intel_digital_port *intel_dig_port = -+ hdmi_to_dig_port(intel_hdmi); -+ struct drm_device *dev = connector->dev; -+ int ret; -+ struct drm_i915_private *dev_priv = connector->dev->dev_private; - - edid = to_intel_connector(connector)->detect_edid; - if (edid == NULL) - return 0; - -- return intel_connector_update_modes(connector, edid); -+ ret = intel_connector_update_modes(connector, edid); -+ -+ if (i915_notify_had) { -+ chv_set_lpe_audio_reg_pipe(dev, INTEL_OUTPUT_HDMI, -+ intel_dig_port->port); -+ mid_hdmi_audio_signal_event(dev_priv->dev, -+ HAD_EVENT_HOT_PLUG); -+ i915_notify_had = 0; -+ } -+ -+ return ret; - } - - static bool -@@ -2170,6 +2334,20 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, - u32 temp = I915_READ(PEG_BAND_GAP_DATA); - I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); - } -+ -+ i915_notify_had = 1; -+} -+ -+/* Added for HDMI Audio */ -+void i915_had_wq(struct work_struct *work) -+{ -+ struct drm_i915_private *dev_priv = container_of(work, -+ struct drm_i915_private, hdmi_audio_wq); -+ -+ if (i915_hdmi_state == connector_status_connected) { -+ mid_hdmi_audio_signal_event(dev_priv->dev, -+ HAD_EVENT_HOT_PLUG); -+ } - } - - void intel_hdmi_init(struct drm_device *dev, -@@ -2178,7 +2356,10 @@ void intel_hdmi_init(struct drm_device *dev, - struct intel_digital_port *intel_dig_port; - struct intel_encoder *intel_encoder; - struct intel_connector *intel_connector; -- -+ /* Added for HDMI Audio */ -+ struct hdmi_audio_priv *hdmi_priv; -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ - intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL); - if (!intel_dig_port) - return; -@@ -2189,6 +2370,7 @@ void intel_hdmi_init(struct drm_device *dev, - return; - } - -+ - intel_encoder = &intel_dig_port->base; - - drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs, -@@ -2248,4 +2430,29 @@ void intel_hdmi_init(struct drm_device *dev, - intel_dig_port->max_lanes = 4; - - intel_hdmi_init_connector(intel_dig_port, intel_connector); -+ -+ /* Added for HDMI Audio */ -+ /* HDMI private data */ -+ INIT_WORK(&dev_priv->hdmi_audio_wq, i915_had_wq); -+ hdmi_priv = kzalloc(sizeof(struct hdmi_audio_priv), GFP_KERNEL); -+ if (!hdmi_priv) { -+ pr_err("failed to allocate memory"); -+ } else { -+ hdmi_priv->dev = dev; -+ if (IS_CHERRYVIEW(dev)) { -+ // FIXME: plb: looks wrong -+ // mapping between stream and Hdmi port ? -+ hdmi_priv->hdmi_reg = HDMIC; -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_C_CONFIG; -+ } else { -+ hdmi_priv->hdmi_reg = HDMIB; -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_A_CONFIG; -+ } -+ hdmi_priv->monitor_type = MONITOR_TYPE_HDMI; -+ hdmi_priv->is_hdcp_supported = true; -+ i915_hdmi_audio_init(hdmi_priv); -+ } -+ - } - -From 330ab9336a78e14cca184e4b30b78e0786102dbe Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Tue, 1 Mar 2016 16:25:04 -0600 -Subject: [PATCH 05/14] drm/i915: power-related changes non-HDAudio HDMI - interface - -PM and RPM changes for interface available on Baytrail and CherryTrail - -This driver was downloaded from https://github.com/01org/baytrailaudio/ - -...and had the changes to .config stripped and the revert on sound/init.c - -Clean-up, port to 4.4 and intel-drm by Pierre Bossart - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart ---- - drivers/gpu/drm/i915/i915_rpm.c | 476 ++++++++++++++++++++++++++++++++++++++++ - drivers/gpu/drm/i915/intel_pm.c | 53 +++++ - 2 files changed, 529 insertions(+) - create mode 100644 drivers/gpu/drm/i915/i915_rpm.c - -diff --git a/drivers/gpu/drm/i915/i915_rpm.c b/drivers/gpu/drm/i915/i915_rpm.c -new file mode 100644 -index 0000000..511311c ---- /dev/null -+++ b/drivers/gpu/drm/i915/i915_rpm.c -@@ -0,0 +1,476 @@ -+/* -+ * Copyright 2013 Intel Corporation -+ * -+ * 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 (including the next -+ * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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. -+ * -+ * Author: -+ * Naresh Kumar Kachhi -+ */ -+ -+#include "i915_drv.h" -+#include "i915_reg.h" -+#include "intel_drv.h" -+#include -+#ifdef CONFIG_PM_RUNTIME -+#include -+#endif -+#include /* Needed for procfs access */ -+#include /* For the basic file system */ -+#include -+ -+#define RPM_AUTOSUSPEND_DELAY 500 -+ -+#ifdef CONFIG_PM_RUNTIME -+ -+/** -+ * - Where should we use get/put? -+ * Get/put should be used very carefully as we might end up in weird states -+ * if not used properly (see the Note below). We want to cover all the -+ * acesses that might result in accessing rings/display/registers/gtt etc -+ * Mostly covering ioctls and drm callbacks should be enough. You can -+ * avoid those which does not access any HW. -+ * -+ * - When should we avoid get/put? -+ * WQ and interrupts should be taken care in suspend path. We should -+ * disable all the interrupts and cancel any pending WQs. Never try to -+ * cover interrupt/WQ with get/put unless you are sure about it. -+ * -+ * Note:Following scenarios should be strictly avoided while using get_sync -+ * 1. Calling get_sync with struct_mutex or mode_config.mutex locked -+ * - we acquire these locks in runtime_resume, so any call to get_sync -+ * with these mutex locked might end up in a dead lock. -+ * check_mutex_current function can be used to debug this scenario. -+ * - Or let's say thread1 has done get_sync and is currently executing -+ * runtime_resume function. Before thread1 is able to acquire these -+ * mutex, thread2 acquires the mutex and does a get_sync. Now thread1 -+ * is waiting for mutex and thread2 is waiting for dev->power.lock -+ * resulting in a deadlock. Use check_mutex to debug this. -+ * 2. Calling get_sync from runtime_resume path -+ * runtime_resume is called with dev->power.lock held. doing get_sync -+ * in same path will end up in deadlock -+ */ -+ -+#define RPM_PROC_ENTRY_FILENAME "i915_rpm_op" -+#define RPM_PROC_ENTRY_DIRECTORY "driver/i915rpm" -+ -+int i915_rpm_get_procfs(struct inode *inode, struct file *file); -+int i915_rpm_put_procfs(struct inode *inode, struct file *file); -+/* proc file operations supported */ -+static const struct file_operations rpm_file_ops = { -+ .owner = THIS_MODULE, -+ .open = i915_rpm_get_procfs, -+ .release = i915_rpm_put_procfs, -+}; -+ -+bool i915_pm_runtime_enabled(struct device *dev) -+{ -+ return pm_runtime_enabled(dev); -+} -+ -+void i915_rpm_enable(struct device *dev) -+{ -+ int cur_status = pm_runtime_enabled(dev); -+ -+ if (!cur_status) { -+ pm_runtime_enable(dev); -+ pm_runtime_allow(dev); -+ } -+} -+ -+void i915_rpm_disable(struct drm_device *drm_dev) -+{ -+ struct device *dev = drm_dev->dev; -+ int cur_status = pm_runtime_enabled(dev); -+ -+ if (cur_status) { -+ pm_runtime_forbid(dev); -+ pm_runtime_disable(dev); -+ } -+} -+ -+static int i915_rpm_procfs_init(struct drm_device *drm_dev) -+{ -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ -+ dev_priv->rpm.i915_proc_dir = NULL; -+ dev_priv->rpm.i915_proc_file = NULL; -+ -+ /** -+ * Create directory for rpm file(s) -+ */ -+ dev_priv->rpm.i915_proc_dir = proc_mkdir(RPM_PROC_ENTRY_DIRECTORY, -+ NULL); -+ if (dev_priv->rpm.i915_proc_dir == NULL) { -+ DRM_ERROR("Could not initialize %s\n", -+ RPM_PROC_ENTRY_DIRECTORY); -+ return -ENOMEM; -+ } -+ /** -+ * Create the /proc file -+ */ -+ dev_priv->rpm.i915_proc_file = proc_create_data( -+ RPM_PROC_ENTRY_FILENAME, -+ S_IRUGO | S_IWUSR, -+ dev_priv->rpm.i915_proc_dir, -+ &rpm_file_ops, -+ drm_dev); -+ /* check if file is created successfuly */ -+ if (dev_priv->rpm.i915_proc_file == NULL) { -+ DRM_ERROR("Could not initialize %s/%s\n", -+ RPM_PROC_ENTRY_DIRECTORY, RPM_PROC_ENTRY_FILENAME); -+ return -ENOMEM; -+ } -+ return 0; -+} -+ -+static int i915_rpm_procfs_deinit(struct drm_device *drm_dev) -+{ -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ /* Clean up proc file */ -+ if (dev_priv->rpm.i915_proc_file) { -+ remove_proc_entry(RPM_PROC_ENTRY_FILENAME, -+ dev_priv->rpm.i915_proc_dir); -+ dev_priv->rpm.i915_proc_file = NULL; -+ } -+ if (dev_priv->rpm.i915_proc_dir) { -+ remove_proc_entry(RPM_PROC_ENTRY_DIRECTORY, NULL); -+ dev_priv->rpm.i915_proc_dir = NULL; -+ } -+ return 0; -+} -+ -+/* RPM init */ -+int i915_rpm_init(struct drm_device *drm_dev) -+{ -+ int ret = 0; -+ struct device *dev = drm_dev->dev; -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ -+ ret = i915_rpm_procfs_init(drm_dev); -+ if (ret) -+ DRM_ERROR("unable to initialize procfs entry"); -+ ret = pm_runtime_set_active(dev); -+ dev_priv->rpm.ring_active = false; -+ atomic_set(&dev_priv->rpm.procfs_count, 0); -+ pm_runtime_allow(dev); -+ /* enable Auto Suspend */ -+ pm_runtime_set_autosuspend_delay(dev, RPM_AUTOSUSPEND_DELAY); -+ pm_runtime_use_autosuspend(dev); -+ if (dev->power.runtime_error) -+ DRM_ERROR("rpm init: error = %d\n", dev->power.runtime_error); -+ -+ return ret; -+} -+ -+int i915_rpm_deinit(struct drm_device *drm_dev) -+{ -+ struct device *dev = drm_dev->dev; -+ -+ pm_runtime_forbid(dev); -+ pm_runtime_set_suspended(dev); -+ pm_runtime_get_noresume(dev); -+ if (dev->power.runtime_error) -+ DRM_ERROR("rpm init: error = %d\n", dev->power.runtime_error); -+ -+ i915_rpm_procfs_deinit(drm_dev); -+ return 0; -+} -+ -+/** -+ * We have different flavour of get/put based on access type (ring/disp/ -+ * vxd etc). this is done based on different requirements and to make -+ * debugging a little easier. Debugfs introduces separate counter for -+ * each type. -+ */ -+ -+/** -+ * Once we have scheduled commands on GPU, it might take a while GPU -+ * to execute them. Following is done to make sure Gfx is in D0i0 while -+ * GPU is executing the commands. -+ * 1. For IOCTLS make sure we are in D0i0 by calling "get_ioctl". -+ * 2. if IOCTL scheudles GPU commands using rings do the following -+ * a. For all ring accesses make sure we add a request in the request -+ * list and schedule a work item to track the "seq no". This -+ * is done by using "i915_add_request" or "i915_add_request_no_flush" -+ * functions. -+ * b. If request list was empty, we do a "get_ring". This will increment -+ * ref count to make sure GPU will be in D0 state. -+ * c. Once the list becomes empty call put_ring -+ * -+ * Note: All the ring accesses are covered with struct_mutex. So we -+ * don't need any synchronization to protect ring_active. -+ */ -+int i915_rpm_get_ring(struct drm_device *drm_dev) -+{ -+ struct intel_engine_cs *ring; -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ int i; -+ bool idle = true; -+ -+ for_each_ring(ring, dev_priv, i) -+ idle &= list_empty(&ring->request_list); -+ -+ if (idle) { -+ if (!dev_priv->rpm.ring_active) { -+ dev_priv->rpm.ring_active = true; -+ pm_runtime_get_noresume(drm_dev->dev); -+ } -+ } -+ -+ return 0; -+} -+ -+int i915_rpm_put_ring(struct drm_device *drm_dev) -+{ -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ -+ if (dev_priv->rpm.ring_active) { -+ /* Mark last time it was busy and schedule a autosuspend */ -+ pm_runtime_mark_last_busy(drm_dev->dev); -+ pm_runtime_put_autosuspend(drm_dev->dev); -+ dev_priv->rpm.ring_active = false; -+ } -+ return 0; -+} -+ -+/** -+ * To cover the function pointers that are assigned to drm structures -+ * and can be called from drm -+ */ -+int i915_rpm_get_callback(struct drm_device *drm_dev) -+{ -+ return pm_runtime_get_sync(drm_dev->dev); -+} -+ -+int i915_rpm_put_callback(struct drm_device *drm_dev) -+{ -+ pm_runtime_mark_last_busy(drm_dev->dev); -+ return pm_runtime_put_autosuspend(drm_dev->dev); -+} -+ -+/** -+ * early_suspend/DSR should call this function to notify PM Core about -+ * display idleness -+ */ -+int i915_rpm_get_disp(struct drm_device *drm_dev) -+{ -+ return pm_runtime_get_sync(drm_dev->dev); -+} -+ -+int i915_rpm_put_disp(struct drm_device *drm_dev) -+{ -+ pm_runtime_mark_last_busy(drm_dev->dev); -+ return pm_runtime_put_autosuspend(drm_dev->dev); -+} -+ -+/** to cover the ioctls with get/put*/ -+int i915_rpm_get_ioctl(struct drm_device *drm_dev) -+{ -+ /* Don't do anything if device is not ready */ -+ if (drm_device_is_unplugged(drm_dev)) -+ return 0; -+ -+ return pm_runtime_get_sync(drm_dev->dev); -+} -+ -+int i915_rpm_put_ioctl(struct drm_device *drm_dev) -+{ -+ /* Don't do anything if device is not ready */ -+ if (drm_device_is_unplugged(drm_dev)) -+ return 0; -+ -+ pm_runtime_mark_last_busy(drm_dev->dev); -+ return pm_runtime_put_autosuspend(drm_dev->dev); -+} -+ -+/* these operations are caled from user mode (CoreU) to make sure -+ * Gfx is up before register accesses from user mode -+ */ -+int i915_rpm_get_procfs(struct inode *inode, struct file *file) -+{ -+ struct drm_device *dev = PDE_DATA(inode); -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ -+ atomic_inc(&dev_priv->rpm.procfs_count); -+ pm_runtime_get_sync(dev->dev); -+ return 0; -+} -+ -+int i915_rpm_put_procfs(struct inode *inode, struct file *file) -+{ -+ struct drm_device *dev = PDE_DATA(inode); -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ -+ pm_runtime_mark_last_busy(dev->dev); -+ pm_runtime_put_autosuspend(dev->dev); -+ atomic_dec(&dev_priv->rpm.procfs_count); -+ return 0; -+} -+ -+/** -+ * VXD driver need to call this to make sure Gfx is in D0i0 -+ * while VXD is on -+ */ -+#ifdef CONFIG_DRM_VXD_BYT -+int i915_rpm_get_vxd(struct drm_device *drm_dev) -+{ -+ return pm_runtime_get_sync(drm_dev->dev); -+} -+EXPORT_SYMBOL(i915_rpm_get_vxd); -+ -+/** -+ * VXD driver need to call this to notify Gfx that it is -+ * done with HW accesses -+ */ -+int i915_rpm_put_vxd(struct drm_device *drm_dev) -+{ -+ pm_runtime_mark_last_busy(drm_dev->dev); -+ return pm_runtime_put_autosuspend(drm_dev->dev); -+} -+EXPORT_SYMBOL(i915_rpm_put_vxd); -+#endif -+ -+/* mainly for debug purpose, check if the access is valid */ -+bool i915_rpm_access_check(struct drm_device *dev) -+{ -+ if (dev->dev->power.runtime_status == RPM_SUSPENDED) { -+ DRM_ERROR("invalid access, will cause Hard Hang\n"); -+ dump_stack(); -+ return false; -+ } -+ return true; -+} -+ -+/* mainly for debug purpose, check if mutex is locked by -+ * current thread -+ */ -+int check_mutex_current(struct drm_device *drm_dev) -+{ -+ int ret = 0; -+ -+ if ((mutex_is_locked(&drm_dev->mode_config.mutex)) && -+ (drm_dev->mode_config.mutex.owner == current)) { -+ DRM_ERROR("config mutex locked by current thread\n"); -+ dump_stack(); -+ ret = -1; -+ } -+ if ((mutex_is_locked(&drm_dev->struct_mutex)) && -+ (drm_dev->struct_mutex.owner == current)) { -+ DRM_ERROR("struct mutex locked by current thread\n"); -+ dump_stack(); -+ ret = -2; -+ } -+ return ret; -+} -+ -+int check_mutex(struct drm_device *drm_dev) -+{ -+ int ret = 0; -+ -+ if (mutex_is_locked(&drm_dev->mode_config.mutex)) { -+ DRM_ERROR("config mutex locked\n"); -+ dump_stack(); -+ ret = -1; -+ } -+ if (mutex_is_locked(&drm_dev->struct_mutex)) { -+ DRM_ERROR("struct mutex locked\n"); -+ dump_stack(); -+ ret = -2; -+ } -+ return ret; -+} -+ -+/* Check for current runtime state */ -+bool i915_is_device_active(struct drm_device *dev) -+{ -+ return (dev->dev->power.runtime_status == RPM_ACTIVE); -+} -+ -+bool i915_is_device_resuming(struct drm_device *dev) -+{ -+ return (dev->dev->power.runtime_status == RPM_RESUMING); -+} -+ -+bool i915_is_device_suspended(struct drm_device *dev) -+{ -+ return (dev->dev->power.runtime_status == RPM_SUSPENDED); -+} -+ -+bool i915_is_device_suspending(struct drm_device *dev) -+{ -+ return (dev->dev->power.runtime_status == RPM_SUSPENDING); -+} -+ -+#else /*CONFIG_PM_RUNTIME*/ -+int i915_rpm_init(struct drm_device *dev) {return 0; } -+int i915_rpm_deinit(struct drm_device *dev) {return 0; } -+int i915_rpm_get(struct drm_device *dev, u32 flags) {return 0; } -+int i915_rpm_put(struct drm_device *dev, u32 flags) {return 0; } -+int i915_rpm_get_ring(struct drm_device *dev) {return 0; } -+int i915_rpm_put_ring(struct drm_device *dev) {return 0; } -+int i915_rpm_get_callback(struct drm_device *dev) {return 0; } -+int i915_rpm_put_callback(struct drm_device *dev) {return 0; } -+int i915_rpm_get_ioctl(struct drm_device *dev) {return 0; } -+int i915_rpm_put_ioctl(struct drm_device *dev) {return 0; } -+int i915_rpm_get_disp(struct drm_device *dev) {return 0; } -+int i915_rpm_put_disp(struct drm_device *dev) {return 0; } -+int i915_rpm_get_procfs(struct inode *inode, -+ struct file *file) {return 0; } -+int i915_rpm_put_procfs(struct inode *inode, -+ struct file *file) {return 0; } -+#ifdef CONFIG_DRM_VXD_BYT -+int i915_rpm_get_vxd(struct drm_device *dev) {return 0; } -+int i915_rpm_put_vxd(struct drm_device *dev) {return 0; } -+#endif -+ -+bool i915_is_device_active(struct drm_device *dev) -+{ -+ return true; -+} -+ -+bool i915_is_device_resuming(struct drm_device *dev) -+{ -+ return false; -+} -+ -+bool i915_is_device_suspended(struct drm_device *dev) -+{ -+ return false; -+} -+ -+bool i915_is_device_suspending(struct drm_device *dev) -+{ -+ return false; -+} -+ -+bool i915_rpm_access_check(struct drm_device *dev) -+{ -+ return true; -+} -+bool i915_pm_runtime_enabled(struct device *dev) -+{ -+ return false; -+} -+ -+void i915_rpm_enable(struct device *dev) {} -+ -+void i915_rpm_disable(struct drm_device *drm_dev) {} -+ -+#endif /*CONFIG_PM_RUNTIME*/ -diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c -index 3425d8e..aa80a85 100644 ---- a/drivers/gpu/drm/i915/intel_pm.c -+++ b/drivers/gpu/drm/i915/intel_pm.c -@@ -31,6 +31,17 @@ - #include "../../../platform/x86/intel_ips.h" - #include - -+typedef enum _UHBUsage { -+ OSPM_UHB_ONLY_IF_ON = 0, -+ OSPM_UHB_FORCE_POWER_ON, -+} UHBUsage; -+ -+static struct drm_device *gdev; -+ -+#ifdef CONFIG_HAS_EARLYSUSPEND -+ #include -+#endif -+ - /** - * DOC: RC6 - * -@@ -7072,6 +7083,7 @@ void intel_suspend_hw(struct drm_device *dev) - void intel_init_pm(struct drm_device *dev) - { - struct drm_i915_private *dev_priv = dev->dev_private; -+ gdev = dev; - - intel_fbc_init(dev_priv); - -@@ -7368,3 +7380,44 @@ void intel_pm_setup(struct drm_device *dev) - atomic_set(&dev_priv->pm.wakeref_count, 0); - atomic_set(&dev_priv->pm.atomic_seq, 0); - } -+ -+bool ospm_power_is_hw_on(int hw_islands) -+{ -+#if 0 -+ struct drm_device *drm_dev = gdev; -+ unsigned long flags; -+ bool ret = false; -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ u32 data = vlv_punit_read(dev_priv, VLV_IOSFSB_PWRGT_STATUS); -+ -+ if ((VLV_POWER_GATE_DISPLAY_MASK & data) -+ == VLV_POWER_GATE_DISPLAY_MASK) { -+ DRM_ERROR("Display Island not ON\n"); -+ return false; -+ } else { -+ return true; -+ } -+#endif -+ return true; -+} -+EXPORT_SYMBOL(ospm_power_is_hw_on); -+ -+/* Dummy Function for HDMI Audio Power management. -+ * Will be updated once S0iX code is integrated -+ */ -+bool ospm_power_using_hw_begin(int hw_island, UHBUsage usage) -+{ -+ struct drm_device *drm_dev = gdev; -+ -+ i915_rpm_get_disp(drm_dev); -+ return i915_is_device_active(drm_dev); -+} -+EXPORT_SYMBOL(ospm_power_using_hw_begin); -+ -+void ospm_power_using_hw_end(int hw_island) -+{ -+ struct drm_device *drm_dev = gdev; -+ -+ i915_rpm_put_disp(drm_dev); -+} -+EXPORT_SYMBOL(ospm_power_using_hw_end); - -From 50e2be60f474f7574df40035d22ed0e2ace646c9 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Thu, 3 Mar 2016 11:08:10 -0600 -Subject: [PATCH 06/14] drm/i915: Add API code for non-HDAudio HDMI interface - -Add API code for interface available on Baytrail and CherryTrail - -Initial code was downloaded from https://github.com/01org/baytrailaudio/ -...and had the changes to .config stripped and the revert on sound/init.c -done by David Henningson - -Clean-up, port to 4.5 and intel-drm by Pierre Bossart -CherryTrail support by Jerome Anand - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart ---- - drivers/gpu/drm/i915/hdmi_audio_if.c | 425 +++++++++++++++++++++++++++++++++++ - 1 file changed, 425 insertions(+) - create mode 100644 drivers/gpu/drm/i915/hdmi_audio_if.c - -diff --git a/drivers/gpu/drm/i915/hdmi_audio_if.c b/drivers/gpu/drm/i915/hdmi_audio_if.c -new file mode 100644 -index 0000000..c7c5f8f ---- /dev/null -+++ b/drivers/gpu/drm/i915/hdmi_audio_if.c -@@ -0,0 +1,425 @@ -+/* -+ * Copyright (c) 2010, Intel Corporation. -+ * -+ * 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. -+ * -+ * This program is distributed in the hope 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. -+ * -+ * Authors: -+ * jim liu -+ * Uma Shankar -+ */ -+ -+#include -+#include "hdmi_audio_if.h" -+#include "i915_drv.h" -+#include "i915_reg.h" -+ -+#define CONFIG_SUPPORT_HDMI_AUDIO -+#ifdef CONFIG_SUPPORT_HDMI_AUDIO -+ -+int i915_hdmi_state; -+ -+/* -+ * Audio register range 0x65000 to 0x65FFF -+ */ -+ -+#define IS_HDMI_AUDIO_I915(reg) ((reg >= 0x65000) && (reg < 0x65FFF)) -+ -+/* Added for HDMI Audio */ -+#define HAD_MAX_ELD_BYTES 84 -+uint8_t hdmi_eld[HAD_MAX_ELD_BYTES]; -+ -+static struct hdmi_audio_priv *hdmi_priv; -+ -+void i915_hdmi_audio_init(struct hdmi_audio_priv *p_hdmi_priv) -+{ -+ hdmi_priv = p_hdmi_priv; -+} -+ -+/* Added for HDMI Audio */ -+void hdmi_get_eld(uint8_t *eld) -+{ -+ struct drm_device *dev = hdmi_priv->dev; -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ memcpy(hdmi_eld, eld, HAD_MAX_ELD_BYTES); -+ mid_hdmi_audio_signal_event(dev_priv->dev, HAD_EVENT_HOT_PLUG); -+} -+ -+static inline int android_hdmi_get_eld(struct drm_device *dev, void *eld) -+{ -+ memcpy(eld, hdmi_eld, HAD_MAX_ELD_BYTES); -+ return 0; -+} -+ -+struct hdmi_audio_priv *get_hdmi_priv() -+{ -+ return hdmi_priv; -+} -+ -+/* -+ * return whether HDMI audio device is busy. -+ */ -+bool mid_hdmi_audio_is_busy(struct drm_device *dev) -+{ -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ int hdmi_audio_busy = 0; -+ hdmi_audio_event_t hdmi_audio_event; -+ -+ if (i915_hdmi_state == connector_status_disconnected) { -+ /* HDMI is not connected, assuming audio device is idle. */ -+ return false; -+ } -+ -+ if (dev_priv->had_interface) { -+ hdmi_audio_event.type = HAD_EVENT_QUERY_IS_AUDIO_BUSY; -+ hdmi_audio_busy = dev_priv->had_interface->query( -+ dev_priv->had_pvt_data, -+ hdmi_audio_event); -+ return hdmi_audio_busy != 0; -+ } -+ return false; -+} -+ -+/* -+ * return whether HDMI audio device is suspended. -+ */ -+bool mid_hdmi_audio_suspend(struct drm_device *dev) -+{ -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ hdmi_audio_event_t hdmi_audio_event; -+ int ret = 0; -+ -+ if (i915_hdmi_state == connector_status_disconnected) { -+ /* HDMI is not connected, assuming audio device -+ * is suspended already. -+ */ -+ return true; -+ } -+ DRM_DEBUG_DRIVER("%s: i915_hdmi_state %d", __func__, -+ i915_hdmi_state); -+ -+ if (dev_priv->had_interface) { -+ hdmi_audio_event.type = 0; -+ ret = dev_priv->had_interface->suspend(dev_priv->had_pvt_data, -+ hdmi_audio_event); -+ return (ret == 0) ? true : false; -+ } -+ return true; -+} -+ -+void mid_hdmi_audio_resume(struct drm_device *dev) -+{ -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ -+ if (i915_hdmi_state == connector_status_disconnected) { -+ /* HDMI is not connected, there is no need -+ * to resume audio device. -+ */ -+ return; -+ } -+ DRM_DEBUG_DRIVER("%s: i915_hdmi_state %d", __func__, -+ i915_hdmi_state); -+ -+ if (dev_priv->had_interface) -+ dev_priv->had_interface->resume(dev_priv->had_pvt_data); -+} -+ -+void mid_hdmi_audio_signal_event(struct drm_device *dev, -+ enum had_event_type event) -+{ -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ -+ if (dev_priv->had_event_callbacks) -+ (*dev_priv->had_event_callbacks)(event, -+ dev_priv->had_pvt_data); -+} -+ -+/** -+ * hdmi_audio_write: -+ * used to write into display controller HDMI audio registers. -+ * -+ */ -+static int hdmi_audio_write(uint32_t reg, uint32_t val) -+{ -+ struct drm_device *dev = hdmi_priv->dev; -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ int ret = 0; -+ -+ if (hdmi_priv->monitor_type == MONITOR_TYPE_DVI) -+ return 0; -+ -+ if (IS_HDMI_AUDIO_I915(reg)) -+ I915_WRITE(_MMIO(VLV_DISPLAY_BASE + reg), val); -+ else -+ ret = -EINVAL; -+ -+ return ret; -+} -+ -+/** -+ * hdmi_audio_read: -+ * used to get the register value read from -+ * display controller HDMI audio registers. -+ */ -+static int hdmi_audio_read(uint32_t reg, uint32_t *val) -+{ -+ struct drm_device *dev = hdmi_priv->dev; -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ int ret = 0; -+ -+ if (hdmi_priv->monitor_type == MONITOR_TYPE_DVI) -+ return 0; -+ -+ if (IS_HDMI_AUDIO_I915(reg)) -+ *val = I915_READ(_MMIO(VLV_DISPLAY_BASE + reg)); -+ else -+ ret = -EINVAL; -+ -+ return ret; -+} -+ -+/** -+ * hdmi_audio_rmw: -+ * used to update the masked bits in display controller HDMI audio registers . -+ * -+ */ -+static int hdmi_audio_rmw(uint32_t reg, uint32_t val, uint32_t mask) -+{ -+ struct drm_device *dev = hdmi_priv->dev; -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ int ret = 0; -+ uint32_t val_tmp = 0; -+ -+ if (IS_HDMI_AUDIO_I915(reg)) { -+ val_tmp = (val & mask) | -+ (I915_READ(_MMIO(VLV_DISPLAY_BASE + reg)) & ~mask); -+ I915_WRITE(_MMIO(VLV_DISPLAY_BASE + reg), val_tmp); -+ } else { -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+/** -+ * hdmi_audio_get_caps: -+ * used to return the HDMI audio capabilities. -+ * e.g. resolution, frame rate. -+ */ -+static int hdmi_audio_get_caps(enum had_caps_list get_element, -+ void *capabilities) -+{ -+ struct drm_device *dev = hdmi_priv->dev; -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ int ret = 0; -+ -+ DRM_DEBUG_DRIVER("\n"); -+ -+ switch (get_element) { -+ case HAD_GET_ELD: -+ ret = android_hdmi_get_eld(dev, capabilities); -+ break; -+ case HAD_GET_SAMPLING_FREQ: -+ /* ToDo: Verify if sampling freq logic is correct */ -+ memcpy(capabilities, &(dev_priv->tmds_clock_speed), -+ sizeof(uint32_t)); -+ break; -+ default: -+ break; -+ } -+ -+ return ret; -+} -+ -+/** -+ * hdmi_audio_get_register_base -+ * used to get the current hdmi base address -+ */ -+int hdmi_audio_get_register_base(uint32_t *reg_base) -+{ -+ *reg_base = hdmi_priv->hdmi_lpe_audio_reg; -+ return 0; -+} -+ -+/** -+ * hdmi_audio_set_caps: -+ * used to set the HDMI audio capabilities. -+ * e.g. Audio INT. -+ */ -+static int hdmi_audio_set_caps(enum had_caps_list set_element, -+ void *capabilties) -+{ -+ struct drm_device *dev = hdmi_priv->dev; -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ int ret = 0; -+ u32 hdmi_reg; -+ u32 int_masks = 0; -+ -+ DRM_DEBUG_DRIVER("\n"); -+ -+ switch (set_element) { -+ case HAD_SET_ENABLE_AUDIO: -+ hdmi_reg = I915_READ(_MMIO(hdmi_priv->hdmi_reg)); -+ if (hdmi_reg & PORT_ENABLE) -+ hdmi_reg |= SDVO_AUDIO_ENABLE; -+ -+ I915_WRITE(_MMIO(hdmi_priv->hdmi_reg), hdmi_reg); -+ I915_READ(_MMIO(hdmi_priv->hdmi_reg)); -+ break; -+ case HAD_SET_DISABLE_AUDIO: -+ hdmi_reg = I915_READ(_MMIO(hdmi_priv->hdmi_reg)) & -+ ~SDVO_AUDIO_ENABLE; -+ I915_WRITE(_MMIO(hdmi_priv->hdmi_reg), hdmi_reg); -+ I915_READ(_MMIO(hdmi_priv->hdmi_reg)); -+ break; -+ -+ case HAD_SET_ENABLE_AUDIO_INT: -+ if (*((u32 *)capabilties) & HDMI_AUDIO_UNDERRUN) -+ int_masks |= I915_HDMI_AUDIO_UNDERRUN_ENABLE; -+ dev_priv->hdmi_audio_interrupt_mask |= int_masks; -+ i915_enable_hdmi_audio_int(dev); -+ break; -+ case HAD_SET_DISABLE_AUDIO_INT: -+ if (*((u32 *)capabilties) & HDMI_AUDIO_UNDERRUN) -+ int_masks |= I915_HDMI_AUDIO_UNDERRUN_ENABLE; -+ dev_priv->hdmi_audio_interrupt_mask &= ~int_masks; -+ -+ i915_disable_hdmi_audio_int(dev); -+ break; -+ default: -+ break; -+ } -+ -+ return ret; -+} -+ -+static struct hdmi_audio_registers_ops hdmi_audio_reg_ops = { -+ .hdmi_audio_get_register_base = hdmi_audio_get_register_base, -+ .hdmi_audio_read_register = hdmi_audio_read, -+ .hdmi_audio_write_register = hdmi_audio_write, -+ .hdmi_audio_read_modify = hdmi_audio_rmw, -+}; -+ -+static struct hdmi_audio_query_set_ops hdmi_audio_get_set_ops = { -+ .hdmi_audio_get_caps = hdmi_audio_get_caps, -+ .hdmi_audio_set_caps = hdmi_audio_set_caps, -+}; -+ -+int mid_hdmi_audio_setup( -+ had_event_call_back audio_callbacks, -+ struct hdmi_audio_registers_ops *reg_ops, -+ struct hdmi_audio_query_set_ops *query_ops) -+{ -+ struct drm_device *dev = hdmi_priv->dev; -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ int ret = 0; -+ -+ DRM_DEBUG_DRIVER("%s: called\n", __func__); -+ -+ reg_ops->hdmi_audio_get_register_base = -+ (hdmi_audio_reg_ops.hdmi_audio_get_register_base); -+ reg_ops->hdmi_audio_read_register = -+ (hdmi_audio_reg_ops.hdmi_audio_read_register); -+ reg_ops->hdmi_audio_write_register = -+ (hdmi_audio_reg_ops.hdmi_audio_write_register); -+ reg_ops->hdmi_audio_read_modify = -+ (hdmi_audio_reg_ops.hdmi_audio_read_modify); -+ query_ops->hdmi_audio_get_caps = -+ hdmi_audio_get_set_ops.hdmi_audio_get_caps; -+ query_ops->hdmi_audio_set_caps = -+ hdmi_audio_get_set_ops.hdmi_audio_set_caps; -+ -+ dev_priv->had_event_callbacks = audio_callbacks; -+ -+ return ret; -+} -+EXPORT_SYMBOL(mid_hdmi_audio_setup); -+ -+int mid_hdmi_audio_register(struct snd_intel_had_interface *driver, -+ void *had_data) -+{ -+ struct drm_device *dev; -+ struct drm_i915_private *dev_priv; -+ -+ DRM_DEBUG_DRIVER("%s: called\n", __func__); -+ if (!hdmi_priv) -+ return -ENODEV; -+ -+ dev = hdmi_priv->dev; -+ dev_priv = (struct drm_i915_private *) dev->dev_private; -+ dev_priv->had_pvt_data = had_data; -+ dev_priv->had_interface = driver; -+ -+ if (hdmi_priv->monitor_type == MONITOR_TYPE_DVI) -+ return 0; -+ -+ /* The Audio driver is loading now and we need to notify -+ * it if there is an HDMI device attached -+ */ -+ DRM_INFO("%s: Scheduling HDMI audio work queue\n", __func__); -+ schedule_work(&dev_priv->hdmi_audio_wq); -+ -+ return 0; -+} -+EXPORT_SYMBOL(mid_hdmi_audio_register); -+#else -+bool hdmi_audio_is_busy(struct drm_device *dev) -+{ -+ /* always in idle state */ -+ return false; -+} -+ -+bool hdmi_audio_suspend(struct drm_device *dev) -+{ -+ /* always in suspend state */ -+ return true; -+} -+ -+void hdmi_audio_resume(struct drm_device *dev) -+{ -+} -+ -+void hdmi_audio_signal_event(struct drm_device *dev, enum had_event_type event) -+{ -+} -+ -+void i915_hdmi_audio_init(struct hdmi_audio_priv *hdmi_priv) -+{ -+ DRM_INFO("%s: HDMI is not supported.\n", __func__); -+} -+ -+int mid_hdmi_audio_setup( -+ had_event_call_back audio_callbacks, -+ struct hdmi_audio_registers_ops *reg_ops, -+ struct hdmi_audio_query_set_ops *query_ops) -+{ -+ DRM_ERROR("%s: HDMI is not supported.\n", __func__); -+ return -ENODEV; -+} -+EXPORT_SYMBOL(mid_hdmi_audio_setup); -+ -+int mid_hdmi_audio_register(struct snd_intel_had_interface *driver, -+ void *had_data) -+{ -+ DRM_ERROR("%s: HDMI is not supported.\n", __func__); -+ return -ENODEV; -+} -+EXPORT_SYMBOL(mid_hdmi_audio_register); -+#endif - -From a4f8a94f2ece0144a98cf52bf5ed2d4d7723960e Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Tue, 1 Mar 2016 16:25:04 -0600 -Subject: [PATCH 07/14] drm/i915: enable non-HDAudio HDMI interface Makefile - -Makefile for all previous patches - -This driver was downloaded from https://github.com/01org/baytrailaudio/ - -...and had the changes to .config stripped and the revert on sound/init.c - -clean-up, port to 4.5 and intel-drm by Pierre Bossart - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart ---- - drivers/gpu/drm/i915/Makefile | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile -index 0851de07..2432085 100644 ---- a/drivers/gpu/drm/i915/Makefile -+++ b/drivers/gpu/drm/i915/Makefile -@@ -12,7 +12,8 @@ i915-y := i915_drv.o \ - i915_sysfs.o \ - intel_csr.o \ - intel_pm.o \ -- intel_runtime_pm.o -+ intel_runtime_pm.o \ -+ hdmi_audio_if.o - - i915-$(CONFIG_COMPAT) += i915_ioc32.o - i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o -@@ -37,6 +38,7 @@ i915-y += i915_cmd_parser.o \ - i915_trace_points.o \ - intel_lrc.o \ - intel_mocs.o \ -+ i915_rpm.o \ - intel_ringbuffer.o \ - intel_uncore.o - - -From 8886c51953a0bc5c536f7b387d15e8e510395e4b Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Thu, 3 Mar 2016 11:09:26 -0600 -Subject: [PATCH 08/14] ALSA: Intel: Atom: add Atom non-HDAudio HDMI interface - -Add support interface available on Baytrail and CherryTrail - -Initial code was downloaded from https://github.com/01org/baytrailaudio/ -...and had the changes to .config stripped and the revert on sound/init.c -and printk->pr_debug done by David Henningson - -Clean-up, port to 4.5 and intel-drm by Pierre Bossart -CherryTrail support by Jerome Anand - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart ---- - sound/Kconfig | 8 + - sound/Makefile | 1 + - sound/hdmi_audio/Makefile | 9 + - sound/hdmi_audio/intel_mid_hdmi_audio.c | 2027 ++++++++++++++++++++++++++++ - sound/hdmi_audio/intel_mid_hdmi_audio.h | 740 ++++++++++ - sound/hdmi_audio/intel_mid_hdmi_audio_if.c | 533 ++++++++ - 6 files changed, 3318 insertions(+) - create mode 100644 sound/hdmi_audio/Makefile - create mode 100644 sound/hdmi_audio/intel_mid_hdmi_audio.c - create mode 100644 sound/hdmi_audio/intel_mid_hdmi_audio.h - create mode 100644 sound/hdmi_audio/intel_mid_hdmi_audio_if.c - -diff --git a/sound/Kconfig b/sound/Kconfig -index 5a240e0..75c679e 100644 ---- a/sound/Kconfig -+++ b/sound/Kconfig -@@ -134,3 +134,11 @@ config AC97_BUS - sound subsystem and other function drivers completely unrelated to - sound although they're sharing the AC97 bus. Concerned drivers - should "select" this. -+ -+config SUPPORT_HDMI -+ bool "SUPPORT_HDMI" -+ depends on DRM_I915 -+ default n -+ help -+ Choose this option to support HDMI. -+ -diff --git a/sound/Makefile b/sound/Makefile -index 7732070..f2c5e82 100644 ---- a/sound/Makefile -+++ b/sound/Makefile -@@ -8,6 +8,7 @@ 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/ - obj-$(CONFIG_SND_AOA) += aoa/ -+obj-$(CONFIG_SUPPORT_HDMI) += hdmi_audio/ - - # This one must be compilable even if sound is configured out - obj-$(CONFIG_AC97_BUS) += ac97_bus.o -diff --git a/sound/hdmi_audio/Makefile b/sound/hdmi_audio/Makefile -new file mode 100644 -index 0000000..b28eb3b ---- /dev/null -+++ b/sound/hdmi_audio/Makefile -@@ -0,0 +1,9 @@ -+DRIVER_NAME := hdmi_audio -+ -+ccflags-y += -Idrivers/gpu/drm/i915 -+ -+$(DRIVER_NAME)-objs += \ -+ intel_mid_hdmi_audio.o \ -+ intel_mid_hdmi_audio_if.o -+ -+obj-m += $(DRIVER_NAME).o -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -new file mode 100644 -index 0000000..d8c5574 ---- /dev/null -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -0,0 +1,2027 @@ -+/* -+ * intel_mid_hdmi_audio.c - Intel HDMI audio driver for MID -+ * -+ * Copyright (C) 2010 Intel Corp -+ * Authors: Sailaja Bandarupalli -+ * Ramesh Babu K V -+ * Vaibhav Agarwal -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * 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. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * ALSA driver for Intel MID HDMI audio controller -+ */ -+ -+#define pr_fmt(fmt) "had: " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "intel_mid_hdmi_audio.h" -+ -+#define PCM_INDEX 0 -+#define MAX_PB_STREAMS 1 -+#define MAX_CAP_STREAMS 0 -+#define HDMI_AUDIO_DRIVER "hdmi-audio" -+static DEFINE_MUTEX(had_mutex); -+ -+/*standard module options for ALSA. This module supports only one card*/ -+static int hdmi_card_index = SNDRV_DEFAULT_IDX1; -+static char *hdmi_card_id = SNDRV_DEFAULT_STR1; -+static struct snd_intelhad *had_data; -+ -+module_param(hdmi_card_index, int, 0444); -+MODULE_PARM_DESC(hdmi_card_index, -+ "Index value for INTEL Intel HDMI Audio controller."); -+module_param(hdmi_card_id, charp, 0444); -+MODULE_PARM_DESC(hdmi_card_id, -+ "ID string for INTEL Intel HDMI Audio controller."); -+MODULE_AUTHOR("Sailaja Bandarupalli "); -+MODULE_AUTHOR("Ramesh Babu K V "); -+MODULE_AUTHOR("Vaibhav Agarwal "); -+MODULE_DESCRIPTION("Intel HDMI Audio driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_SUPPORTED_DEVICE("{Intel,Intel_HAD}"); -+MODULE_VERSION(HAD_DRIVER_VERSION); -+ -+#define INFO_FRAME_WORD1 0x000a0184 -+#define FIFO_THRESHOLD 0xFE -+#define DMA_FIFO_THRESHOLD 0x7 -+#define BYTES_PER_WORD 0x4 -+ -+/* Sampling rate as per IEC60958 Ver 3 */ -+#define CH_STATUS_MAP_32KHZ 0x3 -+#define CH_STATUS_MAP_44KHZ 0x0 -+#define CH_STATUS_MAP_48KHZ 0x2 -+#define CH_STATUS_MAP_88KHZ 0x8 -+#define CH_STATUS_MAP_96KHZ 0xA -+#define CH_STATUS_MAP_176KHZ 0xC -+#define CH_STATUS_MAP_192KHZ 0xE -+ -+#define MAX_SMPL_WIDTH_20 0x0 -+#define MAX_SMPL_WIDTH_24 0x1 -+#define SMPL_WIDTH_16BITS 0x1 -+#define SMPL_WIDTH_24BITS 0x5 -+#define CHANNEL_ALLOCATION 0x1F -+#define MASK_BYTE0 0x000000FF -+#define VALID_DIP_WORDS 3 -+#define LAYOUT0 0 -+#define LAYOUT1 1 -+#define SWAP_LFE_CENTER 0x00fac4c8 -+#define AUD_CONFIG_CH_MASK_V2 0x70 -+ -+/* -+ * ELD SA bits in the CEA Speaker Allocation data block -+*/ -+static int eld_speaker_allocation_bits[] = { -+ [0] = FL | FR, -+ [1] = LFE, -+ [2] = FC, -+ [3] = RL | RR, -+ [4] = RC, -+ [5] = FLC | FRC, -+ [6] = RLC | RRC, -+ /* the following are not defined in ELD yet */ -+ [7] = 0, -+}; -+ -+/* -+ * This is an ordered list! -+ * -+ * The preceding ones have better chances to be selected by -+ * hdmi_channel_allocation(). -+ */ -+static struct cea_channel_speaker_allocation channel_allocations[] = { -+/* channel: 7 6 5 4 3 2 1 0 */ -+{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, -+ /* 2.1 */ -+{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, -+ /* Dolby Surround */ -+{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, -+ /* surround40 */ -+{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, -+ /* surround41 */ -+{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, -+ /* surround50 */ -+{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, -+ /* surround51 */ -+{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, -+ /* 6.1 */ -+{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, -+ /* surround71 */ -+{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, -+ -+{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, -+{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, -+{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, -+{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, -+{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -+{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, -+{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, -+{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, -+{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, -+{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, -+{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, -+{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, -+{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, -+{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, -+}; -+ -+static struct channel_map_table map_tables[] = { -+ { SNDRV_CHMAP_FL, 0x00, FL }, -+ { SNDRV_CHMAP_FR, 0x01, FR }, -+ { SNDRV_CHMAP_RL, 0x04, RL }, -+ { SNDRV_CHMAP_RR, 0x05, RR }, -+ { SNDRV_CHMAP_LFE, 0x02, LFE }, -+ { SNDRV_CHMAP_FC, 0x03, FC }, -+ { SNDRV_CHMAP_RLC, 0x06, RLC }, -+ { SNDRV_CHMAP_RRC, 0x07, RRC }, -+ {} /* terminator */ -+}; -+ -+/* hardware capability structure */ -+static const struct snd_pcm_hardware snd_intel_hadstream = { -+ .info = (SNDRV_PCM_INFO_INTERLEAVED | -+ SNDRV_PCM_INFO_DOUBLE | -+ SNDRV_PCM_INFO_MMAP| -+ SNDRV_PCM_INFO_MMAP_VALID | -+ SNDRV_PCM_INFO_BATCH), -+ .formats = (SNDRV_PCM_FMTBIT_S24 | -+ SNDRV_PCM_FMTBIT_U24), -+ .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, -+ .rate_min = HAD_MIN_RATE, -+ .rate_max = HAD_MAX_RATE, -+ .channels_min = HAD_MIN_CHANNEL, -+ .channels_max = HAD_MAX_CHANNEL, -+ .buffer_bytes_max = HAD_MAX_BUFFER, -+ .period_bytes_min = HAD_MIN_PERIOD_BYTES, -+ .period_bytes_max = HAD_MAX_PERIOD_BYTES, -+ .periods_min = HAD_MIN_PERIODS, -+ .periods_max = HAD_MAX_PERIODS, -+ .fifo_size = HAD_FIFO_SIZE, -+}; -+ -+/* Register access functions */ -+ -+inline int had_get_hwstate(struct snd_intelhad *intelhaddata) -+{ -+ /* Check for device presence -SW state */ -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { -+ pr_debug("%s:Device not connected:%d\n", __func__, -+ intelhaddata->drv_status); -+ return -ENODEV; -+ } -+ -+ /* Check for device presence -HW state */ -+ if (!ospm_power_is_hw_on(OSPM_DISPLAY_ISLAND)) { -+ pr_err("%s:Device not connected\n", __func__); -+ /* HOT_UNPLUG event can be sent to -+ * maintain correct state within HAD -+ * had_event_handler(HAD_EVENT_HOT_UNPLUG, intelhaddata); -+ * Drop all acuired locks before executing this. -+ */ -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+inline int had_get_caps(enum had_caps_list query, void *caps) -+{ -+ int retval; -+ struct snd_intelhad *intelhaddata = had_data; -+ -+ retval = had_get_hwstate(intelhaddata); -+ if (!retval) -+ retval = intelhaddata->query_ops.hdmi_audio_get_caps(query, -+ caps); -+ -+ return retval; -+} -+ -+inline int had_set_caps(enum had_caps_list set_element, void *caps) -+{ -+ int retval; -+ struct snd_intelhad *intelhaddata = had_data; -+ -+ retval = had_get_hwstate(intelhaddata); -+ if (!retval) -+ retval = intelhaddata->query_ops.hdmi_audio_set_caps( -+ set_element, caps); -+ -+ return retval; -+} -+ -+inline int had_read_register(uint32_t offset, uint32_t *data) -+{ -+ int retval; -+ struct snd_intelhad *intelhaddata = had_data; -+ u32 base_addr = intelhaddata->audio_reg_base; -+ -+ retval = had_get_hwstate(intelhaddata); -+ if (!retval) -+ retval = intelhaddata->reg_ops.hdmi_audio_read_register( -+ base_addr + offset, data); -+ -+ return retval; -+} -+ -+inline int had_write_register(uint32_t offset, uint32_t data) -+{ -+ int retval; -+ struct snd_intelhad *intelhaddata = had_data; -+ u32 base_addr = intelhaddata->audio_reg_base; -+ -+ retval = had_get_hwstate(intelhaddata); -+ if (!retval) -+ retval = intelhaddata->reg_ops.hdmi_audio_write_register( -+ base_addr + offset, data); -+ -+ return retval; -+} -+ -+inline int had_read_modify(uint32_t offset, uint32_t data, uint32_t mask) -+{ -+ int retval; -+ struct snd_intelhad *intelhaddata = had_data; -+ u32 base_addr = intelhaddata->audio_reg_base; -+ -+ retval = had_get_hwstate(intelhaddata); -+ if (!retval) -+ retval = intelhaddata->reg_ops.hdmi_audio_read_modify( -+ base_addr + offset, data, mask); -+ -+ return retval; -+} -+/** -+ * had_read_modify_aud_config_v2 - Specific function to read-modify AUD_CONFIG -+ * register on VLV2.The had_read_modify() function should not directly be used -+ * on VLV2 for updating AUD_CONFIG register. -+ * This is because: -+ * Bit6 of AUD_CONFIG register is writeonly due to a silicon bug on VLV2 HDMI IP. -+ * As a result a read-modify of AUD_CONFIG regiter will always clear bit6. -+ * AUD_CONFIG[6:4] represents the "channels" field of the register. -+ * This field should be 1xy binary for configuration with 6 or more channels. -+ * Read-modify of AUD_CONFIG (Eg. for enabling audio) causes the "channels" field -+ * to be updated as 0xy binary resulting in bad audio. -+ * The fix is to always write the AUD_CONFIG[6:4] with appropriate value when -+ * doing read-modify of AUD_CONFIG register. -+ * -+ * @substream: the current substream or NULL if no active substream -+ * @data : data to be written -+ * @mask : mask -+ * -+ */ -+inline int had_read_modify_aud_config_v2(struct snd_pcm_substream *substream, -+ uint32_t data, uint32_t mask) -+{ -+ union aud_cfg cfg_val = {.cfg_regval = 0}; -+ u8 channels; -+ -+ /* -+ * If substream is NULL, there is no active stream. -+ * In this case just set channels to 2 -+ */ -+ if (substream) -+ channels = substream->runtime->channels; -+ else -+ channels = 2; -+ cfg_val.cfg_regx_v2.num_ch = channels - 2; -+ -+ data = data | cfg_val.cfg_regval; -+ mask = mask | AUD_CONFIG_CH_MASK_V2; -+ -+ pr_debug("%s : data = %x, mask =%x\n", __func__, data, mask); -+ -+ return had_read_modify(AUD_CONFIG, data, mask); -+} -+ -+/** -+ * snd_intelhad_enable_audio_v1 - to enable audio -+ * -+ * @substream: Current substream or NULL if no active substream. -+ * @enable: 1 if audio is to be enabled; 0 if audio is to be disabled. -+ * -+ */ -+static void snd_intelhad_enable_audio_v1(struct snd_pcm_substream *substream, -+ u8 enable) -+{ -+ had_read_modify(AUD_CONFIG, enable, BIT(0)); -+} -+ -+/** -+ * snd_intelhad_enable_audio_v2 - to enable audio -+ * -+ * @substream: Current substream or NULL if no active substream. -+ * @enable: 1 if audio is to be enabled; 0 if audio is to be disabled. -+ */ -+static void snd_intelhad_enable_audio_v2(struct snd_pcm_substream *substream, -+ u8 enable) -+{ -+ had_read_modify_aud_config_v2(substream, enable, BIT(0)); -+} -+ -+/** -+ * snd_intelhad_reset_audio_v1 - to reset audio subsystem -+ * -+ * @reset: 1 to reset audio; 0 to bring audio out of reset. -+ * -+ */ -+static void snd_intelhad_reset_audio_v1(u8 reset) -+{ -+ had_write_register(AUD_HDMI_STATUS, reset); -+} -+ -+/** -+ * snd_intelhad_reset_audio_v2 - to reset audio subsystem -+ * -+ * @reset: 1 to reset audio; 0 to bring audio out of reset. -+ * -+ */ -+static void snd_intelhad_reset_audio_v2(u8 reset) -+{ -+ had_write_register(AUD_HDMI_STATUS_v2, reset); -+} -+ -+/** -+ * had_prog_status_reg - to initialize audio channel status registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback -+ */ -+static int had_prog_status_reg(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata) -+{ -+ union aud_ch_status_0 ch_stat0 = {.status_0_regval = 0}; -+ union aud_ch_status_1 ch_stat1 = {.status_1_regval = 0}; -+ int format; -+ -+ pr_debug("Entry %s\n", __func__); -+ -+ ch_stat0.status_0_regx.lpcm_id = (intelhaddata->aes_bits & -+ IEC958_AES0_NONAUDIO)>>1; -+ ch_stat0.status_0_regx.clk_acc = (intelhaddata->aes_bits & -+ IEC958_AES3_CON_CLOCK)>>4; -+ switch (substream->runtime->rate) { -+ case AUD_SAMPLE_RATE_32: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_32KHZ; -+ break; -+ -+ case AUD_SAMPLE_RATE_44_1: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_44KHZ; -+ break; -+ case AUD_SAMPLE_RATE_48: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_48KHZ; -+ break; -+ case AUD_SAMPLE_RATE_88_2: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_88KHZ; -+ break; -+ case AUD_SAMPLE_RATE_96: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_96KHZ; -+ break; -+ case AUD_SAMPLE_RATE_176_4: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_176KHZ; -+ break; -+ case AUD_SAMPLE_RATE_192: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_192KHZ; -+ break; -+ -+ default: -+ /* control should never come here */ -+ return -EINVAL; -+ break; -+ -+ } -+ had_write_register(AUD_CH_STATUS_0, ch_stat0.status_0_regval); -+ -+ format = substream->runtime->format; -+ -+ if (format == SNDRV_PCM_FORMAT_S16_LE) { -+ ch_stat1.status_1_regx.max_wrd_len = MAX_SMPL_WIDTH_20; -+ ch_stat1.status_1_regx.wrd_len = SMPL_WIDTH_16BITS; -+ } else if (format == SNDRV_PCM_FORMAT_S24_LE) { -+ ch_stat1.status_1_regx.max_wrd_len = MAX_SMPL_WIDTH_24; -+ ch_stat1.status_1_regx.wrd_len = SMPL_WIDTH_24BITS; -+ } else { -+ ch_stat1.status_1_regx.max_wrd_len = 0; -+ ch_stat1.status_1_regx.wrd_len = 0; -+ } -+ had_write_register(AUD_CH_STATUS_1, ch_stat1.status_1_regval); -+ return 0; -+} -+ -+/** -+ * snd_intelhad_prog_audio_ctrl_v2 - to initialize audio -+ * registers and buffer confgiuration registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback -+ */ -+int snd_intelhad_prog_audio_ctrl_v2(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata) -+{ -+ union aud_cfg cfg_val = {.cfg_regval = 0}; -+ union aud_buf_config buf_cfg = {.buf_cfgval = 0}; -+ u8 channels; -+ -+ had_prog_status_reg(substream, intelhaddata); -+ -+ buf_cfg.buf_cfg_regx_v2.audio_fifo_watermark = FIFO_THRESHOLD; -+ buf_cfg.buf_cfg_regx_v2.dma_fifo_watermark = DMA_FIFO_THRESHOLD; -+ buf_cfg.buf_cfg_regx_v2.aud_delay = 0; -+ had_write_register(AUD_BUF_CONFIG, buf_cfg.buf_cfgval); -+ -+ channels = substream->runtime->channels; -+ cfg_val.cfg_regx_v2.num_ch = channels - 2; -+ if (channels <= 2) -+ cfg_val.cfg_regx_v2.layout = LAYOUT0; -+ else -+ cfg_val.cfg_regx_v2.layout = LAYOUT1; -+ -+ cfg_val.cfg_regx_v2.val_bit = 1; -+ had_write_register(AUD_CONFIG, cfg_val.cfg_regval); -+ return 0; -+} -+ -+/** -+ * snd_intelhad_prog_audio_ctrl_v1 - to initialize audio -+ * registers and buffer confgiuration registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback -+ */ -+int snd_intelhad_prog_audio_ctrl_v1(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata) -+{ -+ union aud_cfg cfg_val = {.cfg_regval = 0}; -+ union aud_buf_config buf_cfg = {.buf_cfgval = 0}; -+ u8 channels; -+ -+ had_prog_status_reg(substream, intelhaddata); -+ -+ buf_cfg.buf_cfg_regx.fifo_width = FIFO_THRESHOLD; -+ buf_cfg.buf_cfg_regx.aud_delay = 0; -+ had_write_register(AUD_BUF_CONFIG, buf_cfg.buf_cfgval); -+ -+ channels = substream->runtime->channels; -+ -+ switch (channels) { -+ case 1: -+ case 2: -+ cfg_val.cfg_regx.num_ch = CH_STEREO; -+ cfg_val.cfg_regx.layout = LAYOUT0; -+ break; -+ -+ case 3: -+ case 4: -+ cfg_val.cfg_regx.num_ch = CH_THREE_FOUR; -+ cfg_val.cfg_regx.layout = LAYOUT1; -+ break; -+ -+ case 5: -+ case 6: -+ cfg_val.cfg_regx.num_ch = CH_FIVE_SIX; -+ cfg_val.cfg_regx.layout = LAYOUT1; -+ break; -+ -+ case 7: -+ case 8: -+ cfg_val.cfg_regx.num_ch = CH_SEVEN_EIGHT; -+ cfg_val.cfg_regx.layout = LAYOUT1; -+ break; -+ -+ } -+ -+ cfg_val.cfg_regx.val_bit = 1; -+ had_write_register(AUD_CONFIG, cfg_val.cfg_regval); -+ return 0; -+} -+/* -+ * Compute derived values in channel_allocations[]. -+ */ -+static void init_channel_allocations(void) -+{ -+ int i, j; -+ struct cea_channel_speaker_allocation *p; -+ -+ pr_debug("%s: Enter\n", __func__); -+ -+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { -+ p = channel_allocations + i; -+ p->channels = 0; -+ p->spk_mask = 0; -+ for (j = 0; j < ARRAY_SIZE(p->speakers); j++) -+ if (p->speakers[j]) { -+ p->channels++; -+ p->spk_mask |= p->speakers[j]; -+ } -+ } -+} -+ -+/* -+ * The transformation takes two steps: -+ * -+ * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask -+ * spk_mask => (channel_allocations[]) => ai->CA -+ * -+ * TODO: it could select the wrong CA from multiple candidates. -+*/ -+static int snd_intelhad_channel_allocation(struct snd_intelhad *intelhaddata, -+ int channels) -+{ -+ int i; -+ int ca = 0; -+ int spk_mask = 0; -+ -+ /* -+ * CA defaults to 0 for basic stereo audio -+ */ -+ if (channels <= 2) -+ return 0; -+ -+ /* -+ * expand ELD's speaker allocation mask -+ * -+ * ELD tells the speaker mask in a compact(paired) form, -+ * expand ELD's notions to match the ones used by Audio InfoFrame. -+ */ -+ -+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { -+ if (intelhaddata->eeld.speaker_allocation_block & (1 << i)) -+ spk_mask |= eld_speaker_allocation_bits[i]; -+ } -+ -+ /* search for the first working match in the CA table */ -+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { -+ if (channels == channel_allocations[i].channels && -+ (spk_mask & channel_allocations[i].spk_mask) == -+ channel_allocations[i].spk_mask) { -+ ca = channel_allocations[i].ca_index; -+ break; -+ } -+ } -+ -+ pr_debug("HDMI: select CA 0x%x for %d\n", ca, channels); -+ -+ return ca; -+} -+ -+/* from speaker bit mask to ALSA API channel position */ -+static int spk_to_chmap(int spk) -+{ -+ struct channel_map_table *t = map_tables; -+ -+ for (; t->map; t++) { -+ if (t->spk_mask == spk) -+ return t->map; -+ } -+ return 0; -+} -+ -+void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata) -+{ -+ int i = 0, c = 0; -+ int spk_mask = 0; -+ struct snd_pcm_chmap_elem *chmap; -+ uint8_t eld_high, eld_high_mask = 0xF0; -+ uint8_t high_msb; -+ -+ chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); -+ if (chmap == NULL) { -+ pr_err("kzalloc returned null in %s\n", __func__); -+ intelhaddata->chmap->chmap = NULL; -+ return; -+ } -+ -+ had_get_caps(HAD_GET_ELD, &intelhaddata->eeld); -+ -+ pr_debug("eeld.speaker_allocation_block = %x\n", -+ intelhaddata->eeld.speaker_allocation_block); -+ -+ /* WA: Fix the max channel supported to 8 */ -+ -+ /* -+ * Sink may support more than 8 channels, if eld_high has more than -+ * one bit set. SOC supports max 8 channels. -+ * Refer eld_speaker_allocation_bits, for sink speaker allocation -+ */ -+ -+ /* if 0x2F < eld < 0x4F fall back to 0x2f, else fall back to 0x4F */ -+ eld_high = intelhaddata->eeld.speaker_allocation_block & eld_high_mask; -+ if ((eld_high & (eld_high-1)) && (eld_high > 0x1F)) { -+ /* eld_high & (eld_high-1): if more than 1 bit set */ -+ /* 0x1F: 7 channels */ -+ for (i = 1; i < 4; i++) { -+ high_msb = eld_high & (0x80 >> i); -+ if (high_msb) { -+ intelhaddata->eeld.speaker_allocation_block &= -+ high_msb | 0xF; -+ break; -+ } -+ } -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { -+ if (intelhaddata->eeld.speaker_allocation_block & (1 << i)) -+ spk_mask |= eld_speaker_allocation_bits[i]; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { -+ if (spk_mask == channel_allocations[i].spk_mask) { -+ for (c = 0; c < channel_allocations[i].channels; c++) { -+ chmap->map[c] = spk_to_chmap( -+ channel_allocations[i].speakers[(MAX_SPEAKERS - 1)-c]); -+ } -+ chmap->channels = channel_allocations[i].channels; -+ intelhaddata->chmap->chmap = chmap; -+ break; -+ } -+ } -+ if (i >= ARRAY_SIZE(channel_allocations)) -+ kfree(chmap); -+} -+ -+/* -+ ** ALSA API channel-map control callbacks -+ **/ -+static int had_chmap_ctl_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); -+ struct snd_intelhad *intelhaddata = info->private_data; -+ -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) -+ return -ENODEV; -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = HAD_MAX_CHANNEL; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = SNDRV_CHMAP_LAST; -+ return 0; -+} -+ -+#ifndef USE_ALSA_DEFAULT_TLV -+static int had_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, -+ unsigned int size, unsigned int __user *tlv) -+{ -+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); -+ struct snd_intelhad *intelhaddata = info->private_data; -+ -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) -+ return -ENODEV; -+ -+ /* TODO: Fix for query channel map */ -+ return -EPERM; -+} -+#endif -+ -+static int had_chmap_ctl_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); -+ struct snd_intelhad *intelhaddata = info->private_data; -+ int i = 0; -+ const struct snd_pcm_chmap_elem *chmap; -+ -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) -+ return -ENODEV; -+ if (intelhaddata->chmap->chmap == NULL) -+ return -ENODATA; -+ chmap = intelhaddata->chmap->chmap; -+ for (i = 0; i < chmap->channels; i++) { -+ ucontrol->value.integer.value[i] = chmap->map[i]; -+ pr_debug("chmap->map[%d] = %d\n", i, chmap->map[i]); -+ } -+ -+ return 0; -+} -+ -+static int had_chmap_ctl_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ /* TODO: Get channel map and set swap register */ -+ return -EPERM; -+} -+ -+static int had_register_chmap_ctls(struct snd_intelhad *intelhaddata, -+ struct snd_pcm *pcm) -+{ -+ int err = 0; -+ -+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, -+ NULL, 0, (unsigned long)intelhaddata, -+ &intelhaddata->chmap); -+ if (err < 0) -+ return err; -+ -+ intelhaddata->chmap->private_data = intelhaddata; -+ intelhaddata->kctl = intelhaddata->chmap->kctl; -+ intelhaddata->kctl->info = had_chmap_ctl_info; -+ intelhaddata->kctl->get = had_chmap_ctl_get; -+ intelhaddata->kctl->put = had_chmap_ctl_put; -+#ifndef USE_ALSA_DEFAULT_TLV -+ intelhaddata->kctl->tlv.c = had_chmap_ctl_tlv; -+#endif -+ intelhaddata->chmap->chmap = NULL; -+ return 0; -+} -+ -+/** -+ * snd_intelhad_prog_dip_v1 - to initialize Data Island Packets registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback -+ */ -+static void snd_intelhad_prog_dip_v1(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata) -+{ -+ int i; -+ union aud_ctrl_st ctrl_state = {.ctrl_val = 0}; -+ union aud_info_frame2 frame2 = {.fr2_val = 0}; -+ union aud_info_frame3 frame3 = {.fr3_val = 0}; -+ u8 checksum = 0; -+ int channels; -+ -+ channels = substream->runtime->channels; -+ -+ had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); -+ -+ frame2.fr2_regx.chnl_cnt = substream->runtime->channels - 1; -+ -+ frame3.fr3_regx.chnl_alloc = snd_intelhad_channel_allocation( -+ intelhaddata, channels); -+ -+ /*Calculte the byte wide checksum for all valid DIP words*/ -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (INFO_FRAME_WORD1 >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (frame2.fr2_val >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (frame3.fr3_val >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ -+ frame2.fr2_regx.chksum = -(checksum); -+ -+ had_write_register(AUD_HDMIW_INFOFR, INFO_FRAME_WORD1); -+ had_write_register(AUD_HDMIW_INFOFR, frame2.fr2_val); -+ had_write_register(AUD_HDMIW_INFOFR, frame3.fr3_val); -+ -+ /* program remaining DIP words with zero */ -+ for (i = 0; i < HAD_MAX_DIP_WORDS-VALID_DIP_WORDS; i++) -+ had_write_register(AUD_HDMIW_INFOFR, 0x0); -+ -+ ctrl_state.ctrl_regx.dip_freq = 1; -+ ctrl_state.ctrl_regx.dip_en_sta = 1; -+ had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); -+} -+ -+/** -+ * snd_intelhad_prog_dip_v2 - to initialize Data Island Packets registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback -+ */ -+static void snd_intelhad_prog_dip_v2(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata) -+{ -+ int i; -+ union aud_ctrl_st ctrl_state = {.ctrl_val = 0}; -+ union aud_info_frame2 frame2 = {.fr2_val = 0}; -+ union aud_info_frame3 frame3 = {.fr3_val = 0}; -+ u8 checksum = 0; -+ int channels; -+ -+ channels = substream->runtime->channels; -+ -+ had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); -+ -+ frame2.fr2_regx.chnl_cnt = substream->runtime->channels - 1; -+ -+ frame3.fr3_regx.chnl_alloc = snd_intelhad_channel_allocation( -+ intelhaddata, channels); -+ -+ /*Calculte the byte wide checksum for all valid DIP words*/ -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (INFO_FRAME_WORD1 >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (frame2.fr2_val >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (frame3.fr3_val >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ -+ frame2.fr2_regx.chksum = -(checksum); -+ -+ had_write_register(AUD_HDMIW_INFOFR_v2, INFO_FRAME_WORD1); -+ had_write_register(AUD_HDMIW_INFOFR_v2, frame2.fr2_val); -+ had_write_register(AUD_HDMIW_INFOFR_v2, frame3.fr3_val); -+ -+ /* program remaining DIP words with zero */ -+ for (i = 0; i < HAD_MAX_DIP_WORDS-VALID_DIP_WORDS; i++) -+ had_write_register(AUD_HDMIW_INFOFR_v2, 0x0); -+ -+ ctrl_state.ctrl_regx.dip_freq = 1; -+ ctrl_state.ctrl_regx.dip_en_sta = 1; -+ had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); -+} -+ -+/** -+ * snd_intelhad_prog_buffer - programs buffer -+ * address and length registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function programs ring buffer address and length into registers. -+ */ -+int snd_intelhad_prog_buffer(struct snd_intelhad *intelhaddata, -+ int start, int end) -+{ -+ u32 ring_buf_addr, ring_buf_size, period_bytes; -+ u8 i, num_periods; -+ struct snd_pcm_substream *substream; -+ -+ substream = intelhaddata->stream_info.had_substream; -+ if (!substream) { -+ pr_err("substream is NULL\n"); -+ dump_stack(); -+ return 0; -+ } -+ -+ ring_buf_addr = substream->runtime->dma_addr; -+ ring_buf_size = snd_pcm_lib_buffer_bytes(substream); -+ intelhaddata->stream_info.ring_buf_size = ring_buf_size; -+ period_bytes = frames_to_bytes(substream->runtime, -+ substream->runtime->period_size); -+ num_periods = substream->runtime->periods; -+ -+ /* -+ * buffer addr should be 64 byte aligned, period bytes -+ * will be used to calculate addr offset -+ */ -+ period_bytes &= ~0x3F; -+ -+ /* Hardware supports MAX_PERIODS buffers */ -+ if (end >= HAD_MAX_PERIODS) -+ return -EINVAL; -+ -+ for (i = start; i <= end; i++) { -+ /* Program the buf registers with addr and len */ -+ intelhaddata->buf_info[i].buf_addr = ring_buf_addr + -+ (i * period_bytes); -+ if (i < num_periods-1) -+ intelhaddata->buf_info[i].buf_size = period_bytes; -+ else -+ intelhaddata->buf_info[i].buf_size = ring_buf_size - -+ (period_bytes*i); -+ -+ had_write_register(AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH), -+ intelhaddata->buf_info[i].buf_addr | -+ BIT(0) | BIT(1)); -+ had_write_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH), -+ period_bytes); -+ intelhaddata->buf_info[i].is_valid = true; -+ } -+ pr_debug("%s:buf[%d-%d] addr=%#x and size=%d\n", __func__, start, end, -+ intelhaddata->buf_info[start].buf_addr, -+ intelhaddata->buf_info[start].buf_size); -+ intelhaddata->valid_buf_cnt = num_periods; -+ return 0; -+} -+ -+inline int snd_intelhad_read_len(struct snd_intelhad *intelhaddata) -+{ -+ int i, retval = 0; -+ u32 len[4]; -+ -+ for (i = 0; i < 4 ; i++) { -+ had_read_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH), -+ &len[i]); -+ if (!len[i]) -+ retval++; -+ } -+ if (retval != 1) { -+ for (i = 0; i < 4 ; i++) -+ pr_debug("buf[%d] size=%d\n", i, len[i]); -+ } -+ -+ return retval; -+} -+ -+/** -+ * snd_intelhad_prog_cts_v1 - Program HDMI audio CTS value -+ * -+ * @aud_samp_freq: sampling frequency of audio data -+ * @tmds: sampling frequency of the display data -+ * @n_param: N value, depends on aud_samp_freq -+ * @intelhaddata:substream private data -+ * -+ * Program CTS register based on the audio and display sampling frequency -+ */ -+static void snd_intelhad_prog_cts_v1(u32 aud_samp_freq, u32 tmds, u32 n_param, -+ struct snd_intelhad *intelhaddata) -+{ -+ u32 cts_val; -+ u64 dividend, divisor; -+ -+ /* Calculate CTS according to HDMI 1.3a spec*/ -+ dividend = (u64)tmds * n_param*1000; -+ divisor = 128 * aud_samp_freq; -+ cts_val = div64_u64(dividend, divisor); -+ pr_debug("TMDS value=%d, N value=%d, CTS Value=%d\n", -+ tmds, n_param, cts_val); -+ had_write_register(AUD_HDMI_CTS, (BIT(20) | cts_val)); -+} -+ -+/** -+ * snd_intelhad_prog_cts_v2 - Program HDMI audio CTS value -+ * -+ * @aud_samp_freq: sampling frequency of audio data -+ * @tmds: sampling frequency of the display data -+ * @n_param: N value, depends on aud_samp_freq -+ * @intelhaddata:substream private data -+ * -+ * Program CTS register based on the audio and display sampling frequency -+ */ -+static void snd_intelhad_prog_cts_v2(u32 aud_samp_freq, u32 tmds, u32 n_param, -+ struct snd_intelhad *intelhaddata) -+{ -+ u32 cts_val; -+ u64 dividend, divisor; -+ -+ /* Calculate CTS according to HDMI 1.3a spec*/ -+ dividend = (u64)tmds * n_param*1000; -+ divisor = 128 * aud_samp_freq; -+ cts_val = div64_u64(dividend, divisor); -+ pr_debug("TMDS value=%d, N value=%d, CTS Value=%d\n", -+ tmds, n_param, cts_val); -+ had_write_register(AUD_HDMI_CTS, (BIT(24) | cts_val)); -+} -+ -+static int had_calculate_n_value(u32 aud_samp_freq) -+{ -+ s32 n_val; -+ -+ /* Select N according to HDMI 1.3a spec*/ -+ switch (aud_samp_freq) { -+ case AUD_SAMPLE_RATE_32: -+ n_val = 4096; -+ break; -+ -+ case AUD_SAMPLE_RATE_44_1: -+ n_val = 6272; -+ break; -+ -+ case AUD_SAMPLE_RATE_48: -+ n_val = 6144; -+ break; -+ -+ case AUD_SAMPLE_RATE_88_2: -+ n_val = 12544; -+ break; -+ -+ case AUD_SAMPLE_RATE_96: -+ n_val = 12288; -+ break; -+ -+ case AUD_SAMPLE_RATE_176_4: -+ n_val = 25088; -+ break; -+ -+ case HAD_MAX_RATE: -+ n_val = 24576; -+ break; -+ -+ default: -+ n_val = -EINVAL; -+ break; -+ } -+ return n_val; -+} -+ -+/** -+ * snd_intelhad_prog_n_v1 - Program HDMI audio N value -+ * -+ * @aud_samp_freq: sampling frequency of audio data -+ * @n_param: N value, depends on aud_samp_freq -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback. -+ * It programs based on the audio and display sampling frequency -+ */ -+static int snd_intelhad_prog_n_v1(u32 aud_samp_freq, u32 *n_param, -+ struct snd_intelhad *intelhaddata) -+{ -+ s32 n_val; -+ -+ n_val = had_calculate_n_value(aud_samp_freq); -+ -+ if (n_val < 0) -+ return n_val; -+ -+ had_write_register(AUD_N_ENABLE, (BIT(20) | n_val)); -+ *n_param = n_val; -+ return 0; -+} -+ -+/** -+ * snd_intelhad_prog_n_v2 - Program HDMI audio N value -+ * -+ * @aud_samp_freq: sampling frequency of audio data -+ * @n_param: N value, depends on aud_samp_freq -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback. -+ * It programs based on the audio and display sampling frequency -+ */ -+static int snd_intelhad_prog_n_v2(u32 aud_samp_freq, u32 *n_param, -+ struct snd_intelhad *intelhaddata) -+{ -+ s32 n_val; -+ -+ n_val = had_calculate_n_value(aud_samp_freq); -+ -+ if (n_val < 0) -+ return n_val; -+ -+ had_write_register(AUD_N_ENABLE, (BIT(24) | n_val)); -+ *n_param = n_val; -+ return 0; -+} -+ -+static void had_clear_underrun_intr_v1(struct snd_intelhad *intelhaddata) -+{ -+ u32 hdmi_status, i = 0; -+ -+ /* Handle Underrun interrupt within Audio Unit */ -+ had_write_register(AUD_CONFIG, 0); -+ /* Reset buffer pointers */ -+ had_write_register(AUD_HDMI_STATUS, 1); -+ had_write_register(AUD_HDMI_STATUS, 0); -+ /** -+ * The interrupt status 'sticky' bits might not be cleared by -+ * setting '1' to that bit once... -+ */ -+ do { /* clear bit30, 31 AUD_HDMI_STATUS */ -+ had_read_register(AUD_HDMI_STATUS, &hdmi_status); -+ pr_debug("HDMI status =0x%x\n", hdmi_status); -+ if (hdmi_status & AUD_CONFIG_MASK_UNDERRUN) { -+ i++; -+ hdmi_status &= (AUD_CONFIG_MASK_SRDBG | -+ AUD_CONFIG_MASK_FUNCRST); -+ hdmi_status |= ~AUD_CONFIG_MASK_UNDERRUN; -+ had_write_register(AUD_HDMI_STATUS, hdmi_status); -+ } else -+ break; -+ } while (i < MAX_CNT); -+ if (i >= MAX_CNT) -+ pr_err("Unable to clear UNDERRUN bits\n"); -+} -+ -+static void had_clear_underrun_intr_v2(struct snd_intelhad *intelhaddata) -+{ -+ u32 hdmi_status, i = 0; -+ -+ /* Handle Underrun interrupt within Audio Unit */ -+ had_write_register(AUD_CONFIG, 0); -+ /* Reset buffer pointers */ -+ had_write_register(AUD_HDMI_STATUS_v2, 1); -+ had_write_register(AUD_HDMI_STATUS_v2, 0); -+ /** -+ * The interrupt status 'sticky' bits might not be cleared by -+ * setting '1' to that bit once... -+ */ -+ do { /* clear bit30, 31 AUD_HDMI_STATUS */ -+ had_read_register(AUD_HDMI_STATUS_v2, &hdmi_status); -+ pr_debug("HDMI status =0x%x\n", hdmi_status); -+ if (hdmi_status & AUD_CONFIG_MASK_UNDERRUN) { -+ i++; -+ hdmi_status &= ~AUD_CONFIG_MASK_UNDERRUN; -+ had_write_register(AUD_HDMI_STATUS_v2, hdmi_status); -+ } else -+ break; -+ } while (i < MAX_CNT); -+ if (i >= MAX_CNT) -+ pr_err("Unable to clear UNDERRUN bits\n"); -+} -+ -+/** -+* snd_intelhad_open - stream initializations are done here -+* @substream:substream for which the stream function is called -+* -+* This function is called whenever a PCM stream is opened -+*/ -+static int snd_intelhad_open(struct snd_pcm_substream *substream) -+{ -+ struct snd_intelhad *intelhaddata; -+ struct snd_pcm_runtime *runtime; -+ struct had_stream_pvt *stream; -+ struct had_pvt_data *had_stream; -+ int retval; -+ -+ pr_debug("snd_intelhad_open called\n"); -+ intelhaddata = snd_pcm_substream_chip(substream); -+ had_stream = intelhaddata->private_data; -+ runtime = substream->runtime; -+ -+ pm_runtime_get(intelhaddata->dev); -+ -+ /* -+ * HDMI driver might suspend the device already, -+ * so we return it on -+ */ -+ if (!ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, -+ OSPM_UHB_FORCE_POWER_ON)) { -+ pr_err("HDMI device can't be turned on\n"); -+ retval = -ENODEV; -+ goto exit_put_handle; -+ } -+ -+ if (had_get_hwstate(intelhaddata)) { -+ pr_err("%s: HDMI cable plugged-out\n", __func__); -+ retval = -ENODEV; -+ goto exit_ospm_handle; -+ } -+ -+ /* Check, if device already in use */ -+ if (runtime->private_data) { -+ pr_err("Device already in use\n"); -+ retval = -EBUSY; -+ goto exit_ospm_handle; -+ } -+ -+ /* set the runtime hw parameter with local snd_pcm_hardware struct */ -+ runtime->hw = snd_intel_hadstream; -+ -+ stream = kzalloc(sizeof(*stream), GFP_KERNEL); -+ if (!stream) { -+ retval = -ENOMEM; -+ goto exit_ospm_handle; -+ } -+ stream->stream_status = STREAM_INIT; -+ runtime->private_data = stream; -+ -+ retval = snd_pcm_hw_constraint_integer(runtime, -+ SNDRV_PCM_HW_PARAM_PERIODS); -+ if (retval < 0) { -+ goto exit_err; -+ } -+ -+ /* Make sure, that the period size is always aligned -+ * 64byte boundary -+ */ -+ retval = snd_pcm_hw_constraint_step(substream->runtime, 0, -+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); -+ if (retval < 0) { -+ pr_err("%s:step_size=64 failed,err=%d\n", __func__, retval); -+ goto exit_err; -+ } -+ -+ return retval; -+exit_err: -+ kfree(stream); -+exit_ospm_handle: -+ ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); -+exit_put_handle: -+ pm_runtime_put(intelhaddata->dev); -+ runtime->private_data = NULL; -+ return retval; -+} -+ -+/** -+* had_period_elapsed - updates the hardware pointer status -+* @had_substream:substream for which the stream function is called -+* -+*/ -+static void had_period_elapsed(void *had_substream) -+{ -+ struct snd_pcm_substream *substream = had_substream; -+ struct had_stream_pvt *stream; -+ -+ /* pr_debug("had_period_elapsed called\n"); */ -+ -+ if (!substream || !substream->runtime) -+ return; -+ stream = substream->runtime->private_data; -+ if (!stream) -+ return; -+ -+ if (stream->stream_status != STREAM_RUNNING) -+ return; -+ snd_pcm_period_elapsed(substream); -+} -+ -+/** -+* snd_intelhad_init_stream - internal function to initialize stream info -+* @substream:substream for which the stream function is called -+* -+*/ -+static int snd_intelhad_init_stream(struct snd_pcm_substream *substream) -+{ -+ struct snd_intelhad *intelhaddata = snd_pcm_substream_chip(substream); -+ -+ pr_debug("snd_intelhad_init_stream called\n"); -+ -+ pr_debug("setting buffer ptr param\n"); -+ intelhaddata->stream_info.period_elapsed = had_period_elapsed; -+ intelhaddata->stream_info.had_substream = substream; -+ intelhaddata->stream_info.buffer_ptr = 0; -+ intelhaddata->stream_info.buffer_rendered = 0; -+ intelhaddata->stream_info.sfreq = substream->runtime->rate; -+ return 0; -+} -+ -+/** -+ * snd_intelhad_close- to free parameteres when stream is stopped -+ * -+ * @substream: substream for which the function is called -+ * -+ * This function is called by ALSA framework when stream is stopped -+ */ -+static int snd_intelhad_close(struct snd_pcm_substream *substream) -+{ -+ struct snd_intelhad *intelhaddata; -+ struct snd_pcm_runtime *runtime; -+ -+ pr_debug("snd_intelhad_close called\n"); -+ -+ intelhaddata = snd_pcm_substream_chip(substream); -+ runtime = substream->runtime; -+ -+ if (!runtime->private_data) { -+ pr_debug("close() might have called after failed open"); -+ return 0; -+ } -+ -+ intelhaddata->stream_info.buffer_rendered = 0; -+ intelhaddata->stream_info.buffer_ptr = 0; -+ intelhaddata->stream_info.str_id = 0; -+ intelhaddata->stream_info.had_substream = NULL; -+ -+ /* Check if following drv_status modification is required - VA */ -+ if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) -+ intelhaddata->drv_status = HAD_DRV_CONNECTED; -+ kfree(runtime->private_data); -+ runtime->private_data = NULL; -+ ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); -+ pm_runtime_put(intelhaddata->dev); -+ return 0; -+} -+ -+/** -+ * snd_intelhad_hw_params- to setup the hardware parameters -+ * like allocating the buffers -+ * -+ * @substream: substream for which the function is called -+ * @hw_params: hardware parameters -+ * -+ * This function is called by ALSA framework when hardware params are set -+ */ -+static int snd_intelhad_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *hw_params) -+{ -+ unsigned long addr; -+ int pages, buf_size, retval; -+ -+ pr_debug("snd_intelhad_hw_params called\n"); -+ -+ BUG_ON(!hw_params); -+ -+ buf_size = params_buffer_bytes(hw_params); -+ retval = snd_pcm_lib_malloc_pages(substream, buf_size); -+ if (retval < 0) -+ return retval; -+ pr_debug("%s:allocated memory = %d\n", __func__, buf_size); -+ /* mark the pages as uncached region */ -+ addr = (unsigned long) substream->runtime->dma_area; -+ pages = (substream->runtime->dma_bytes + PAGE_SIZE - 1) / PAGE_SIZE; -+ retval = set_memory_uc(addr, pages); -+ if (retval) { -+ pr_err("set_memory_uc failed.Error:%d\n", retval); -+ return retval; -+ } -+ memset(substream->runtime->dma_area, 0, buf_size); -+ -+ return retval; -+} -+ -+/** -+ * snd_intelhad_hw_free- to release the resources allocated during -+ * hardware params setup -+ * -+ * @substream: substream for which the function is called -+ * -+ * This function is called by ALSA framework before close callback. -+ * -+ */ -+static int snd_intelhad_hw_free(struct snd_pcm_substream *substream) -+{ -+ unsigned long addr; -+ u32 pages; -+ -+ pr_debug("snd_intelhad_hw_free called\n"); -+ -+ /* mark back the pages as cached/writeback region before the free */ -+ if (substream->runtime->dma_area != NULL) { -+ addr = (unsigned long) substream->runtime->dma_area; -+ pages = (substream->runtime->dma_bytes + PAGE_SIZE - 1) / -+ PAGE_SIZE; -+ set_memory_wb(addr, pages); -+ return snd_pcm_lib_free_pages(substream); -+ } -+ return 0; -+} -+ -+/** -+* snd_intelhad_pcm_trigger - stream activities are handled here -+* @substream:substream for which the stream function is called -+* @cmd:the stream commamd thats requested from upper layer -+* This function is called whenever an a stream activity is invoked -+*/ -+static int snd_intelhad_pcm_trigger(struct snd_pcm_substream *substream, -+ int cmd) -+{ -+ int caps, retval = 0; -+ unsigned long flag_irq; -+ struct snd_intelhad *intelhaddata; -+ struct had_stream_pvt *stream; -+ struct had_pvt_data *had_stream; -+ -+ pr_debug("snd_intelhad_pcm_trigger called\n"); -+ -+ intelhaddata = snd_pcm_substream_chip(substream); -+ stream = substream->runtime->private_data; -+ had_stream = intelhaddata->private_data; -+ -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ pr_debug("Trigger Start\n"); -+ -+ /* Disable local INTRs till register prgmng is done */ -+ if (had_get_hwstate(intelhaddata)) { -+ pr_err("_START: HDMI cable plugged-out\n"); -+ retval = -ENODEV; -+ break; -+ } -+ stream->stream_status = STREAM_RUNNING; -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irq); -+ had_stream->stream_type = HAD_RUNNING_STREAM; -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irq); -+ -+ /* Enable Audio */ -+ /* -+ * ToDo: Need to enable UNDERRUN interrupts as well -+ * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; -+ */ -+ caps = HDMI_AUDIO_BUFFER_DONE; -+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO_INT, &caps); -+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO, NULL); -+ intelhaddata->ops->enable_audio(substream, 1); -+ -+ pr_debug("Processed _Start\n"); -+ -+ break; -+ -+ case SNDRV_PCM_TRIGGER_STOP: -+ pr_debug("Trigger Stop\n"); -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irq); -+ intelhaddata->stream_info.str_id = 0; -+ intelhaddata->curr_buf = 0; -+ -+ /* Stop reporting BUFFER_DONE/UNDERRUN to above layers*/ -+ -+ had_stream->stream_type = HAD_INIT; -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irq); -+ /* Disable Audio */ -+ /* -+ * ToDo: Need to disable UNDERRUN interrupts as well -+ * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; -+ */ -+ caps = HDMI_AUDIO_BUFFER_DONE; -+ had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); -+ intelhaddata->ops->enable_audio(substream, 0); -+ /* Reset buffer pointers */ -+ intelhaddata->ops->reset_audio(1); -+ intelhaddata->ops->reset_audio(0); -+ stream->stream_status = STREAM_DROPPED; -+ had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); -+ break; -+ -+ default: -+ retval = -EINVAL; -+ } -+ return retval; -+} -+ -+/** -+* snd_intelhad_pcm_prepare- internal preparation before starting a stream -+* -+* @substream: substream for which the function is called -+* -+* This function is called when a stream is started for internal preparation. -+*/ -+static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream) -+{ -+ int retval; -+ u32 disp_samp_freq, n_param; -+ struct snd_intelhad *intelhaddata; -+ struct snd_pcm_runtime *runtime; -+ struct had_pvt_data *had_stream; -+ -+ pr_debug("snd_intelhad_pcm_prepare called\n"); -+ -+ intelhaddata = snd_pcm_substream_chip(substream); -+ runtime = substream->runtime; -+ had_stream = intelhaddata->private_data; -+ -+ if (had_get_hwstate(intelhaddata)) { -+ pr_err("%s: HDMI cable plugged-out\n", __func__); -+ snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); -+ retval = -ENODEV; -+ goto prep_end; -+ } -+ -+ pr_debug("period_size=%d\n", -+ frames_to_bytes(runtime, runtime->period_size)); -+ pr_debug("periods=%d\n", runtime->periods); -+ pr_debug("buffer_size=%d\n", snd_pcm_lib_buffer_bytes(substream)); -+ pr_debug("rate=%d\n", runtime->rate); -+ pr_debug("channels=%d\n", runtime->channels); -+ -+ if (intelhaddata->stream_info.str_id) { -+ pr_debug("_prepare is called for existing str_id#%d\n", -+ intelhaddata->stream_info.str_id); -+ retval = snd_intelhad_pcm_trigger(substream, -+ SNDRV_PCM_TRIGGER_STOP); -+ return retval; -+ } -+ -+ retval = snd_intelhad_init_stream(substream); -+ if (retval) -+ goto prep_end; -+ -+ -+ /* Get N value in KHz */ -+ retval = had_get_caps(HAD_GET_SAMPLING_FREQ, &disp_samp_freq); -+ if (retval) { -+ pr_err("querying display sampling freq failed %#x\n", retval); -+ goto prep_end; -+ } -+ -+ had_get_caps(HAD_GET_ELD, &intelhaddata->eeld); -+ -+ retval = intelhaddata->ops->prog_n(substream->runtime->rate, &n_param, -+ intelhaddata); -+ if (retval) { -+ pr_err("programming N value failed %#x\n", retval); -+ goto prep_end; -+ } -+ intelhaddata->ops->prog_cts(substream->runtime->rate, -+ disp_samp_freq, n_param, intelhaddata); -+ -+ intelhaddata->ops->prog_dip(substream, intelhaddata); -+ -+ retval = intelhaddata->ops->audio_ctrl(substream, intelhaddata); -+ -+ /* Prog buffer address */ -+ retval = snd_intelhad_prog_buffer(intelhaddata, -+ HAD_BUF_TYPE_A, HAD_BUF_TYPE_D); -+ -+ /* -+ * Program channel mapping in following order: -+ * FL, FR, C, LFE, RL, RR -+ */ -+ -+ had_write_register(AUD_BUF_CH_SWAP, SWAP_LFE_CENTER); -+ -+prep_end: -+ return retval; -+} -+ -+/** -+ * snd_intelhad_pcm_pointer- to send the current buffer pointerprocessed by hw -+ * -+ * @substream: substream for which the function is called -+ * -+ * This function is called by ALSA framework to get the current hw buffer ptr -+ * when a period is elapsed -+ */ -+static snd_pcm_uframes_t snd_intelhad_pcm_pointer( -+ struct snd_pcm_substream *substream) -+{ -+ struct snd_intelhad *intelhaddata; -+ u32 bytes_rendered = 0; -+ -+ /* pr_debug("snd_intelhad_pcm_pointer called\n"); */ -+ -+ intelhaddata = snd_pcm_substream_chip(substream); -+ -+ if (intelhaddata->flag_underrun) { -+ intelhaddata->flag_underrun = 0; -+ return SNDRV_PCM_POS_XRUN; -+ } -+ -+ if (intelhaddata->stream_info.buffer_rendered) -+ div_u64_rem(intelhaddata->stream_info.buffer_rendered, -+ intelhaddata->stream_info.ring_buf_size, -+ &(bytes_rendered)); -+ -+ intelhaddata->stream_info.buffer_ptr = bytes_to_frames( -+ substream->runtime, -+ bytes_rendered); -+ return intelhaddata->stream_info.buffer_ptr; -+} -+ -+/** -+* snd_intelhad_pcm_mmap- mmaps a kernel buffer to user space for copying data -+* -+* @substream: substream for which the function is called -+* @vma: struct instance of memory VMM memory area -+* -+* This function is called by OS when a user space component -+* tries to get mmap memory from driver -+*/ -+static int snd_intelhad_pcm_mmap(struct snd_pcm_substream *substream, -+ struct vm_area_struct *vma) -+{ -+ -+ pr_debug("snd_intelhad_pcm_mmap called\n"); -+ -+ pr_debug("entry with prot:%s\n", __func__); -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -+ return remap_pfn_range(vma, vma->vm_start, -+ substream->dma_buffer.addr >> PAGE_SHIFT, -+ vma->vm_end - vma->vm_start, vma->vm_page_prot); -+} -+ -+int hdmi_audio_mode_change(struct snd_pcm_substream *substream) -+{ -+ int retval = 0; -+ u32 disp_samp_freq, n_param; -+ struct snd_intelhad *intelhaddata; -+ -+ intelhaddata = snd_pcm_substream_chip(substream); -+ -+ /* Disable Audio */ -+ intelhaddata->ops->enable_audio(substream, 0); -+ -+ /* Update CTS value */ -+ retval = had_get_caps(HAD_GET_SAMPLING_FREQ, &disp_samp_freq); -+ if (retval) { -+ pr_err("querying display sampling freq failed %#x\n", retval); -+ goto out; -+ } -+ -+ retval = intelhaddata->ops->prog_n(substream->runtime->rate, &n_param, -+ intelhaddata); -+ if (retval) { -+ pr_err("programming N value failed %#x\n", retval); -+ goto out; -+ } -+ intelhaddata->ops->prog_cts(substream->runtime->rate, -+ disp_samp_freq, n_param, intelhaddata); -+ -+ /* Enable Audio */ -+ intelhaddata->ops->enable_audio(substream, 1); -+ -+out: -+ return retval; -+} -+ -+/*PCM operations structure and the calls back for the same */ -+struct snd_pcm_ops snd_intelhad_playback_ops = { -+ .open = snd_intelhad_open, -+ .close = snd_intelhad_close, -+ .ioctl = snd_pcm_lib_ioctl, -+ .hw_params = snd_intelhad_hw_params, -+ .hw_free = snd_intelhad_hw_free, -+ .prepare = snd_intelhad_pcm_prepare, -+ .trigger = snd_intelhad_pcm_trigger, -+ .pointer = snd_intelhad_pcm_pointer, -+ .mmap = snd_intelhad_pcm_mmap, -+}; -+ -+/** -+ * snd_intelhad_create - to crete alsa card instance -+ * -+ * @intelhaddata: pointer to internal context -+ * @card: pointer to card -+ * -+ * This function is called when the hdmi cable is plugged in -+ */ -+static int snd_intelhad_create( -+ struct snd_intelhad *intelhaddata, -+ struct snd_card *card) -+{ -+ int retval; -+ static struct snd_device_ops ops = { -+ }; -+ -+ BUG_ON(!intelhaddata); -+ /* ALSA api to register the device */ -+ retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, intelhaddata, &ops); -+ return retval; -+} -+/** -+ * snd_intelhad_pcm_free - to free the memory allocated -+ * -+ * @pcm: pointer to pcm instance -+ * This function is called when the device is removed -+ */ -+static void snd_intelhad_pcm_free(struct snd_pcm *pcm) -+{ -+ pr_debug("Freeing PCM preallocated pages\n"); -+ snd_pcm_lib_preallocate_free_for_all(pcm); -+} -+ -+static int had_iec958_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; -+ uinfo->count = 1; -+ return 0; -+} -+ -+static int had_iec958_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); -+ -+ ucontrol->value.iec958.status[0] = (intelhaddata->aes_bits >> 0) & 0xff; -+ ucontrol->value.iec958.status[1] = (intelhaddata->aes_bits >> 8) & 0xff; -+ ucontrol->value.iec958.status[2] = -+ (intelhaddata->aes_bits >> 16) & 0xff; -+ ucontrol->value.iec958.status[3] = -+ (intelhaddata->aes_bits >> 24) & 0xff; -+ return 0; -+} -+static int had_iec958_mask_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ ucontrol->value.iec958.status[0] = 0xff; -+ ucontrol->value.iec958.status[1] = 0xff; -+ ucontrol->value.iec958.status[2] = 0xff; -+ ucontrol->value.iec958.status[3] = 0xff; -+ return 0; -+} -+static int had_iec958_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ unsigned int val; -+ struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); -+ -+ pr_debug("entered had_iec958_put\n"); -+ val = (ucontrol->value.iec958.status[0] << 0) | -+ (ucontrol->value.iec958.status[1] << 8) | -+ (ucontrol->value.iec958.status[2] << 16) | -+ (ucontrol->value.iec958.status[3] << 24); -+ if (intelhaddata->aes_bits != val) { -+ intelhaddata->aes_bits = val; -+ return 1; -+ } -+ return 1; -+} -+ -+static struct snd_kcontrol_new had_control_iec958_mask = { -+ .access = SNDRV_CTL_ELEM_ACCESS_READ, -+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, -+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), -+ .info = had_iec958_info, /* shared */ -+ .get = had_iec958_mask_get, -+}; -+ -+static struct snd_kcontrol_new had_control_iec958 = { -+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, -+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), -+ .info = had_iec958_info, -+ .get = had_iec958_get, -+ .put = had_iec958_put -+}; -+ -+static struct snd_intel_had_interface had_interface = { -+ .name = "hdmi-audio", -+ .query = hdmi_audio_query, -+ .suspend = hdmi_audio_suspend, -+ .resume = hdmi_audio_resume, -+}; -+ -+static struct had_ops had_ops_v1 = { -+ .enable_audio = snd_intelhad_enable_audio_v1, -+ .reset_audio = snd_intelhad_reset_audio_v1, -+ .prog_n = snd_intelhad_prog_n_v1, -+ .prog_cts = snd_intelhad_prog_cts_v1, -+ .audio_ctrl = snd_intelhad_prog_audio_ctrl_v1, -+ .prog_dip = snd_intelhad_prog_dip_v1, -+ .handle_underrun = had_clear_underrun_intr_v1, -+}; -+ -+static struct had_ops had_ops_v2 = { -+ .enable_audio = snd_intelhad_enable_audio_v2, -+ .reset_audio = snd_intelhad_reset_audio_v2, -+ .prog_n = snd_intelhad_prog_n_v2, -+ .prog_cts = snd_intelhad_prog_cts_v2, -+ .audio_ctrl = snd_intelhad_prog_audio_ctrl_v2, -+ .prog_dip = snd_intelhad_prog_dip_v2, -+ .handle_underrun = had_clear_underrun_intr_v2, -+}; -+/** -+ * hdmi_audio_probe - to create sound card instance for HDMI audio playabck -+ * -+ *@haddata: pointer to HAD private data -+ *@card_id: card for which probe is called -+ * -+ * This function is called when the hdmi cable is plugged in. This function -+ * creates and registers the sound card with ALSA -+ */ -+static int hdmi_audio_probe(struct platform_device *devptr) -+{ -+ -+ int retval; -+ struct snd_pcm *pcm; -+ struct snd_card *card; -+ struct had_callback_ops ops_cb; -+ struct snd_intelhad *intelhaddata; -+ struct had_pvt_data *had_stream; -+ -+ pr_debug("Enter %s\n", __func__); -+ -+ pr_debug("hdmi_audio_probe dma_mask: %d\n", devptr->dev.dma_mask); -+ -+ /* allocate memory for saving internal context and working */ -+ intelhaddata = kzalloc(sizeof(*intelhaddata), GFP_KERNEL); -+ if (!intelhaddata) { -+ pr_err("mem alloc failed\n"); -+ return -ENOMEM; -+ } -+ -+ had_stream = kzalloc(sizeof(*had_stream), GFP_KERNEL); -+ if (!had_stream) { -+ pr_err("mem alloc failed\n"); -+ retval = -ENOMEM; -+ goto free_haddata; -+ } -+ -+ had_data = intelhaddata; -+ ops_cb.intel_had_event_call_back = had_event_handler; -+ -+ /* registering with display driver to get access to display APIs */ -+ -+ retval = mid_hdmi_audio_setup( -+ ops_cb.intel_had_event_call_back, -+ &(intelhaddata->reg_ops), -+ &(intelhaddata->query_ops)); -+ if (retval) { -+ pr_err("querying display driver APIs failed %#x\n", retval); -+ goto free_hadstream; -+ } -+ mutex_lock(&had_mutex); -+ spin_lock_init(&intelhaddata->had_spinlock); -+ intelhaddata->drv_status = HAD_DRV_DISCONNECTED; -+ /* create a card instance with ALSA framework */ -+ retval = snd_card_new(&devptr->dev, hdmi_card_index, hdmi_card_id, -+ THIS_MODULE, 0, &card); -+ -+ if (retval) -+ goto unlock_mutex; -+ intelhaddata->card = card; -+ intelhaddata->card_id = hdmi_card_id; -+ intelhaddata->card_index = card->number; -+ intelhaddata->private_data = had_stream; -+ intelhaddata->flag_underrun = 0; -+ intelhaddata->aes_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; -+ strncpy(card->driver, INTEL_HAD, strlen(INTEL_HAD)); -+ strncpy(card->shortname, INTEL_HAD, strlen(INTEL_HAD)); -+ -+ retval = snd_pcm_new(card, INTEL_HAD, PCM_INDEX, MAX_PB_STREAMS, -+ MAX_CAP_STREAMS, &pcm); -+ if (retval) -+ goto err; -+ -+ /* setup private data which can be retrieved when required */ -+ pcm->private_data = intelhaddata; -+ pcm->private_free = snd_intelhad_pcm_free; -+ pcm->info_flags = 0; -+ strncpy(pcm->name, card->shortname, strlen(card->shortname)); -+ /* setup the ops for palyabck */ -+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, -+ &snd_intelhad_playback_ops); -+ /* allocate dma pages for ALSA stream operations -+ * memory allocated is based on size, not max value -+ * thus using same argument for max & size -+ */ -+ retval = snd_pcm_lib_preallocate_pages_for_all(pcm, -+ SNDRV_DMA_TYPE_DEV, NULL, -+ HAD_MAX_BUFFER, HAD_MAX_BUFFER); -+ -+ if (card->dev == NULL) -+ pr_debug("card->dev is NULL!!!!! Should not be this case\n"); -+ else if (card->dev->dma_mask == NULL) -+ pr_debug("hdmi_audio_probe dma_mask is NULL!!!!!\n"); -+ else -+ pr_debug("hdmi_audio_probe dma_mask is : %d\n", card->dev->dma_mask); -+ -+ if (retval) -+ goto err; -+ -+ /* internal function call to register device with ALSA */ -+ retval = snd_intelhad_create(intelhaddata, card); -+ if (retval) -+ goto err; -+ -+ card->private_data = &intelhaddata; -+ retval = snd_card_register(card); -+ if (retval) -+ goto err; -+ -+ /* IEC958 controls */ -+ retval = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958_mask, -+ intelhaddata)); -+ if (retval < 0) -+ goto err; -+ retval = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958, -+ intelhaddata)); -+ if (retval < 0) -+ goto err; -+ -+ init_channel_allocations(); -+ -+ /* Register channel map controls */ -+ retval = had_register_chmap_ctls(intelhaddata, pcm); -+ if (retval < 0) -+ goto err; -+ -+ intelhaddata->dev = &devptr->dev; -+ pm_runtime_set_active(intelhaddata->dev); -+ pm_runtime_enable(intelhaddata->dev); -+ -+ mutex_unlock(&had_mutex); -+ retval = mid_hdmi_audio_register(&had_interface, intelhaddata); -+ if (retval) { -+ pr_err("registering with display driver failed %#x\n", retval); -+ snd_card_free(card); -+ goto free_hadstream; -+ } -+ -+ intelhaddata->hw_silence = 1; -+ intelhaddata->ops = &had_ops_v2; -+ -+ return retval; -+err: -+ snd_card_free(card); -+unlock_mutex: -+ mutex_unlock(&had_mutex); -+free_hadstream: -+ kfree(had_stream); -+ pm_runtime_disable(intelhaddata->dev); -+ intelhaddata->dev = NULL; -+free_haddata: -+ kfree(intelhaddata); -+ intelhaddata = NULL; -+ pr_err("Error returned from %s api %#x\n", __func__, retval); -+ return retval; -+} -+ -+/** -+ * hdmi_audio_remove - removes the alsa card -+ * -+ *@haddata: pointer to HAD private data -+ * -+ * This function is called when the hdmi cable is un-plugged. This function -+ * free the sound card. -+ */ -+static int hdmi_audio_remove(struct platform_device *devptr) -+{ -+ struct snd_intelhad *intelhaddata = had_data; -+ int caps; -+ -+ pr_debug("Enter %s\n", __func__); -+ -+ if (!intelhaddata) -+ return 0; -+ -+ if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) { -+ caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; -+ had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); -+ had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); -+ } -+ snd_card_free(intelhaddata->card); -+ kfree(intelhaddata->private_data); -+ kfree(intelhaddata); -+ return 0; -+} -+ -+static int had_pm_runtime_suspend(struct device *dev) -+{ -+ return 0; -+} -+ -+static int had_pm_runtime_resume(struct device *dev) -+{ -+ return 0; -+} -+ -+static int had_pm_runtime_idle(struct device *dev) -+{ -+ return pm_schedule_suspend(dev, HAD_SUSPEND_DELAY); -+} -+ -+const struct dev_pm_ops had_pm_ops = { -+ .runtime_idle = had_pm_runtime_idle, -+ .runtime_suspend = had_pm_runtime_suspend, -+ .runtime_resume = had_pm_runtime_resume, -+}; -+ -+static const struct acpi_device_id had_acpi_ids[] = { -+ { "HAD0F28", 0 }, -+ { "HAD022A8", 0 }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(acpi, had_acpi_ids); -+ -+static struct platform_driver had_driver = { -+ .probe = hdmi_audio_probe, -+ .remove = hdmi_audio_remove, -+ .suspend = NULL, -+ .resume = NULL, -+ .driver = { -+ .name = HDMI_AUDIO_DRIVER, -+#ifdef CONFIG_PM -+ .pm = &had_pm_ops, -+#endif -+ .acpi_match_table = ACPI_PTR(had_acpi_ids), -+ }, -+}; -+ -+/* -+* alsa_card_intelhad_init- driver init function -+* This function is called when driver module is inserted -+*/ -+static int __init alsa_card_intelhad_init(void) -+{ -+ int retval; -+ -+ pr_debug("Enter %s\n", __func__); -+ -+ pr_info("******** HAD DRIVER loading.. Ver: %s\n", -+ HAD_DRIVER_VERSION); -+ -+ retval = platform_driver_register(&had_driver); -+ if (retval < 0) { -+ pr_err("Platform driver register failed\n"); -+ return retval; -+ } -+ -+ pr_debug("init complete\n"); -+ return retval; -+} -+ -+/** -+* alsa_card_intelhad_exit- driver exit function -+* This function is called when driver module is removed -+*/ -+static void __exit alsa_card_intelhad_exit(void) -+{ -+ pr_debug("had_exit called\n"); -+ platform_driver_unregister(&had_driver); -+} -+late_initcall(alsa_card_intelhad_init); -+module_exit(alsa_card_intelhad_exit); -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.h b/sound/hdmi_audio/intel_mid_hdmi_audio.h -new file mode 100644 -index 0000000..7c54b97 ---- /dev/null -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.h -@@ -0,0 +1,740 @@ -+/* -+ * intel_mid_hdmi_audio.h - Intel HDMI audio driver for MID -+ * -+ * Copyright (C) 2010 Intel Corp -+ * Authors: Sailaja Bandarupalli -+ * Ramesh Babu K V -+ * Vaibhav Agarwal -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * 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. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * ALSA driver for Intel MID HDMI audio controller -+ */ -+#ifndef __INTEL_MID_HDMI_AUDIO_H -+#define __INTEL_MID_HDMI_AUDIO_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define OSPM_DISPLAY_ISLAND 0x40 -+ -+typedef enum _UHBUsage { -+ OSPM_UHB_ONLY_IF_ON = 0, -+ OSPM_UHB_FORCE_POWER_ON, -+} UHBUsage; -+ -+bool ospm_power_using_hw_begin(int hw_island, UHBUsage usage); -+void ospm_power_using_hw_end(int hw_island); -+ -+/* -+ * Use this function to do an instantaneous check for if the hw is on. -+ * Only use this in cases where you know the g_state_change_mutex -+ * is already held such as in irq install/uninstall and you need to -+ * prevent a deadlock situation. Otherwise use ospm_power_using_hw_begin(). -+ */ -+bool ospm_power_is_hw_on(int hw_islands); -+ -+#define HAD_DRIVER_VERSION "0.01.003" -+#define HAD_MAX_DEVICES 1 -+#define HAD_MIN_CHANNEL 2 -+#define HAD_MAX_CHANNEL 8 -+#define HAD_NUM_OF_RING_BUFS 4 -+ -+/* 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 DRIVER_NAME "intelmid_hdmi_audio" -+#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 "IntelHDMI" -+ -+/* _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 84 -+ -+typedef union { -+ uint8_t eld_data[OTM_HDMI_ELD_SIZE]; -+ #pragma pack(1) -+ 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() -+} otm_hdmi_eld_t; -+ -+/** -+ * enum had_status - Audio stream states -+ * -+ * @STREAM_INIT: Stream initialized -+ * @STREAM_RUNNING: Stream running -+ * @STREAM_PAUSED: Stream paused -+ * @STREAM_DROPPED: Stream dropped -+ */ -+enum had_stream_status { -+ STREAM_INIT = 0, -+ STREAM_RUNNING = 1, -+ STREAM_PAUSED = 2, -+ STREAM_DROPPED = 3 -+}; -+ -+/** -+ * enum had_status_stream - HAD stream states -+ */ -+enum had_status_stream { -+ HAD_INIT = 0, -+ HAD_RUNNING_STREAM, -+}; -+ -+enum had_drv_status { -+ HAD_DRV_CONNECTED, -+ HAD_DRV_RUNNING, -+ HAD_DRV_DISCONNECTED, -+ HAD_DRV_SUSPENDED, -+ HAD_DRV_ERR, -+}; -+ -+/* enum intel_had_aud_buf_type - HDMI controller ring buffer types */ -+enum intel_had_aud_buf_type { -+ HAD_BUF_TYPE_A = 0, -+ HAD_BUF_TYPE_B = 1, -+ HAD_BUF_TYPE_C = 2, -+ HAD_BUF_TYPE_D = 3, -+}; -+ -+enum num_aud_ch { -+ CH_STEREO = 0, -+ CH_THREE_FOUR = 1, -+ CH_FIVE_SIX = 2, -+ CH_SEVEN_EIGHT = 3 -+}; -+ -+ -+/* HDMI controller register offsets */ -+enum hdmi_ctrl_reg_offset_v1 { -+ AUD_CONFIG = 0x0, -+ AUD_CH_STATUS_0 = 0x08, -+ AUD_CH_STATUS_1 = 0x0C, -+ AUD_HDMI_CTS = 0x10, -+ AUD_N_ENABLE = 0x14, -+ AUD_SAMPLE_RATE = 0x18, -+ AUD_BUF_CONFIG = 0x20, -+ AUD_BUF_CH_SWAP = 0x24, -+ AUD_BUF_A_ADDR = 0x40, -+ AUD_BUF_A_LENGTH = 0x44, -+ AUD_BUF_B_ADDR = 0x48, -+ AUD_BUF_B_LENGTH = 0x4c, -+ AUD_BUF_C_ADDR = 0x50, -+ AUD_BUF_C_LENGTH = 0x54, -+ AUD_BUF_D_ADDR = 0x58, -+ AUD_BUF_D_LENGTH = 0x5c, -+ AUD_CNTL_ST = 0x60, -+ AUD_HDMI_STATUS = 0x68, -+ AUD_HDMIW_INFOFR = 0x114, -+}; -+ -+/* -+ * Delta changes in HDMI controller register offsets -+ * compare to v1 version -+ */ -+ -+enum hdmi_ctrl_reg_offset_v2 { -+ AUD_HDMI_STATUS_v2 = 0x64, -+ AUD_HDMIW_INFOFR_v2 = 0x68, -+}; -+ -+/* -+ * CEA speaker placement: -+ * -+ * FL FLC FC FRC FR -+ * -+ * LFE -+ * -+ * RL RLC RC RRC RR -+ * -+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to -+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. -+ */ -+enum cea_speaker_placement { -+ FL = (1 << 0), /* Front Left */ -+ FC = (1 << 1), /* Front Center */ -+ FR = (1 << 2), /* Front Right */ -+ FLC = (1 << 3), /* Front Left Center */ -+ FRC = (1 << 4), /* Front Right Center */ -+ RL = (1 << 5), /* Rear Left */ -+ RC = (1 << 6), /* Rear Center */ -+ RR = (1 << 7), /* Rear Right */ -+ RLC = (1 << 8), /* Rear Left Center */ -+ RRC = (1 << 9), /* Rear Right Center */ -+ LFE = (1 << 10), /* Low Frequency Effect */ -+}; -+ -+struct cea_channel_speaker_allocation { -+ int ca_index; -+ int speakers[8]; -+ -+ /* derived values, just for convenience */ -+ int channels; -+ int spk_mask; -+}; -+ -+struct channel_map_table { -+ unsigned char map; /* ALSA API channel map position */ -+ unsigned char cea_slot; /* CEA slot value */ -+ int spk_mask; /* speaker position bit mask */ -+}; -+ -+/** -+ * union aud_cfg - Audio configuration -+ * -+ * @cfg_regx: individual register bits -+ * @cfg_regval: full register value -+ * -+ */ -+union aud_cfg { -+ struct { -+ u32 aud_en:1; -+ u32 layout:1; -+ u32 fmt:2; -+ u32 num_ch:2; -+ u32 rsvd0:1; -+ u32 set:1; -+ u32 flat:1; -+ u32 val_bit:1; -+ u32 user_bit:1; -+ u32 underrun:1; -+ u32 rsvd1:20; -+ } cfg_regx; -+ struct { -+ u32 aud_en:1; -+ u32 layout:1; -+ u32 fmt:2; -+ u32 num_ch:3; -+ u32 set:1; -+ u32 flat:1; -+ u32 val_bit:1; -+ u32 user_bit:1; -+ u32 underrun:1; -+ u32 packet_mode:1; -+ u32 left_align:1; -+ u32 bogus_sample:1; -+ u32 dp_modei:1; -+ u32 rsvd:16; -+ } cfg_regx_v2; -+ u32 cfg_regval; -+}; -+ -+/** -+ * union aud_ch_status_0 - Audio Channel Status 0 Attributes -+ * -+ * @status_0_regx:individual register bits -+ * @status_0_regval:full register value -+ * -+ */ -+union aud_ch_status_0 { -+ struct { -+ u32 ch_status:1; -+ u32 lpcm_id:1; -+ u32 cp_info:1; -+ u32 format:3; -+ u32 mode:2; -+ u32 ctg_code:8; -+ u32 src_num:4; -+ u32 ch_num:4; -+ u32 samp_freq:4; -+ u32 clk_acc:2; -+ u32 rsvd:2; -+ } status_0_regx; -+ u32 status_0_regval; -+}; -+ -+/** -+ * union aud_ch_status_1 - Audio Channel Status 1 Attributes -+ * -+ * @status_1_regx: individual register bits -+ * @status_1_regval: full register value -+ * -+ */ -+union aud_ch_status_1 { -+ struct { -+ u32 max_wrd_len:1; -+ u32 wrd_len:3; -+ u32 rsvd:28; -+ } status_1_regx; -+ u32 status_1_regval; -+}; -+ -+/** -+ * union aud_hdmi_cts - CTS register -+ * -+ * @cts_regx: individual register bits -+ * @cts_regval: full register value -+ * -+ */ -+union aud_hdmi_cts { -+ struct { -+ u32 cts_val:20; -+ u32 en_cts_prog:1; -+ u32 rsvd:11; -+ } cts_regx; -+ struct { -+ u32 cts_val:24; -+ u32 en_cts_prog:1; -+ u32 rsvd:7; -+ } cts_regx_v2; -+ u32 cts_regval; -+}; -+ -+/** -+ * union aud_hdmi_n_enable - N register -+ * -+ * @n_regx: individual register bits -+ * @n_regval: full register value -+ * -+ */ -+union aud_hdmi_n_enable { -+ struct { -+ u32 n_val:20; -+ u32 en_n_prog:1; -+ u32 rsvd:11; -+ } n_regx; -+ struct { -+ u32 n_val:24; -+ u32 en_n_prog:1; -+ u32 rsvd:7; -+ } n_regx_v2; -+ u32 n_regval; -+}; -+ -+/** -+ * union aud_buf_config - Audio Buffer configurations -+ * -+ * @buf_cfg_regx: individual register bits -+ * @buf_cfgval: full register value -+ * -+ */ -+union aud_buf_config { -+ struct { -+ u32 fifo_width:8; -+ u32 rsvd0:8; -+ u32 aud_delay:8; -+ u32 rsvd1:8; -+ } buf_cfg_regx; -+ struct { -+ u32 audio_fifo_watermark:8; -+ u32 dma_fifo_watermark:3; -+ u32 rsvd0:5; -+ u32 aud_delay:8; -+ u32 rsvd1:8; -+ } buf_cfg_regx_v2; -+ u32 buf_cfgval; -+}; -+ -+/** -+ * union aud_buf_ch_swap - Audio Sample Swapping offset -+ * -+ * @buf_ch_swap_regx: individual register bits -+ * @buf_ch_swap_val: full register value -+ * -+ */ -+union aud_buf_ch_swap { -+ struct { -+ u32 first_0:3; -+ u32 second_0:3; -+ u32 first_1:3; -+ u32 second_1:3; -+ u32 first_2:3; -+ u32 second_2:3; -+ u32 first_3:3; -+ u32 second_3:3; -+ u32 rsvd:8; -+ } buf_ch_swap_regx; -+ u32 buf_ch_swap_val; -+}; -+ -+/** -+ * union aud_buf_addr - Address for Audio Buffer -+ * -+ * @buf_addr_regx: individual register bits -+ * @buf_addr_val: full register value -+ * -+ */ -+union aud_buf_addr { -+ struct { -+ u32 valid:1; -+ u32 intr_en:1; -+ u32 rsvd:4; -+ u32 addr:26; -+ } buf_addr_regx; -+ u32 buf_addr_val; -+}; -+ -+/** -+ * union aud_buf_len - Length of Audio Buffer -+ * -+ * @buf_len_regx: individual register bits -+ * @buf_len_val: full register value -+ * -+ */ -+union aud_buf_len { -+ struct { -+ u32 buf_len:20; -+ u32 rsvd:12; -+ } buf_len_regx; -+ u32 buf_len_val; -+}; -+ -+/** -+ * union aud_ctrl_st - Audio Control State Register offset -+ * -+ * @ctrl_regx: individual register bits -+ * @ctrl_val: full register value -+ * -+ */ -+union aud_ctrl_st { -+ struct { -+ u32 ram_addr:4; -+ u32 eld_ack:1; -+ u32 eld_addr:4; -+ u32 eld_buf_size:5; -+ u32 eld_valid:1; -+ u32 cp_ready:1; -+ u32 dip_freq:2; -+ u32 dip_idx:3; -+ u32 dip_en_sta:4; -+ u32 rsvd:7; -+ } ctrl_regx; -+ u32 ctrl_val; -+}; -+ -+/** -+ * union aud_info_frame1 - Audio HDMI Widget Data Island Packet offset -+ * -+ * @fr1_regx: individual register bits -+ * @fr1_val: full register value -+ * -+ */ -+union aud_info_frame1 { -+ struct { -+ u32 pkt_type:8; -+ u32 ver_num:8; -+ u32 len:5; -+ u32 rsvd:11; -+ } fr1_regx; -+ u32 fr1_val; -+}; -+ -+/** -+ * union aud_info_frame2 - DIP frame 2 -+ * -+ * @fr2_regx: individual register bits -+ * @fr2_val: full register value -+ * -+ */ -+union aud_info_frame2 { -+ struct { -+ u32 chksum:8; -+ u32 chnl_cnt:3; -+ u32 rsvd0:1; -+ u32 coding_type:4; -+ u32 smpl_size:2; -+ u32 smpl_freq:3; -+ u32 rsvd1:3; -+ u32 format:8; -+ } fr2_regx; -+ u32 fr2_val; -+}; -+ -+/** -+ * union aud_info_frame3 - DIP frame 3 -+ * -+ * @fr3_regx: individual register bits -+ * @fr3_val: full register value -+ * -+ */ -+union aud_info_frame3 { -+ struct { -+ u32 chnl_alloc:8; -+ u32 rsvd0:3; -+ u32 lsv:4; -+ u32 dm_inh:1; -+ u32 rsvd1:16; -+ } fr3_regx; -+ u32 fr3_val; -+}; -+ -+ -+struct pcm_stream_info { -+ int str_id; -+ void *had_substream; -+ void (*period_elapsed)(void *had_substream); -+ u32 buffer_ptr; -+ u64 buffer_rendered; -+ u32 ring_buf_size; -+ int sfreq; -+}; -+ -+struct ring_buf_info { -+ uint32_t buf_addr; -+ uint32_t buf_size; -+ uint8_t is_valid; -+}; -+ -+struct had_stream_pvt { -+ enum had_stream_status stream_status; -+ int stream_ops; -+ ssize_t dbg_cum_bytes; -+}; -+ -+struct had_pvt_data { -+ enum had_status_stream stream_type; -+}; -+ -+struct had_callback_ops { -+ had_event_call_back intel_had_event_call_back; -+}; -+ -+/** -+ * struct snd_intelhad - intelhad driver structure -+ * -+ * @card: ptr to hold card details -+ * @card_index: sound card index -+ * @card_id: detected sound card id -+ * @reg_ops: register operations to program registers -+ * @query_ops: caps call backs for get/set operations -+ * @drv_status: driver status -+ * @buf_info: ring buffer info -+ * @stream_info: stream information -+ * @eeld: holds EELD info -+ * @curr_buf: pointer to hold current active ring buf -+ * @valid_buf_cnt: ring buffer count for stream -+ * @had_spinlock: driver lock -+ * @aes_bits: IEC958 status bits -+ * @buff_done: id of current buffer done intr -+ * @dev: platoform device handle -+ * @kctl: holds kctl ptrs used for channel map -+ * @chmap: holds channel map info -+ * @audio_reg_base: hdmi audio register base offset -+ * @hw_silence: flag indicates SoC support for HW silence/Keep alive -+ * @ops: holds ops functions based on platform -+ */ -+struct snd_intelhad { -+ struct snd_card *card; -+ int card_index; -+ char *card_id; -+ struct hdmi_audio_registers_ops reg_ops; -+ struct hdmi_audio_query_set_ops query_ops; -+ enum had_drv_status drv_status; -+ struct ring_buf_info buf_info[HAD_NUM_OF_RING_BUFS]; -+ struct pcm_stream_info stream_info; -+ otm_hdmi_eld_t eeld; -+ enum intel_had_aud_buf_type curr_buf; -+ int valid_buf_cnt; -+ unsigned int aes_bits; -+ int flag_underrun; -+ struct had_pvt_data *private_data; -+ spinlock_t had_spinlock; -+ enum intel_had_aud_buf_type buff_done; -+ struct device *dev; -+ struct snd_kcontrol *kctl; -+ struct snd_pcm_chmap *chmap; -+ unsigned int audio_reg_base; -+ bool hw_silence; -+ struct had_ops *ops; -+}; -+ -+struct had_ops { -+ void (*enable_audio)(struct snd_pcm_substream *substream, -+ u8 enable); -+ void (*reset_audio)(u8 reset); -+ int (*prog_n)(u32 aud_samp_freq, u32 *n_param, -+ struct snd_intelhad *intelhaddata); -+ void (*prog_cts)(u32 aud_samp_freq, u32 tmds, u32 n_param, -+ struct snd_intelhad *intelhaddata); -+ int (*audio_ctrl)(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata); -+ void (*prog_dip)(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata); -+ void (*handle_underrun)(struct snd_intelhad *intelhaddata); -+}; -+ -+ -+int had_event_handler(enum had_event_type event_type, void *data); -+ -+int hdmi_audio_query(void *drv_data, hdmi_audio_event_t event); -+int hdmi_audio_suspend(void *drv_data, hdmi_audio_event_t event); -+int hdmi_audio_resume(void *drv_data); -+int hdmi_audio_mode_change(struct snd_pcm_substream *substream); -+extern struct snd_pcm_ops snd_intelhad_playback_ops; -+ -+int snd_intelhad_init_audio_ctrl(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata, -+ int flag_silence); -+int snd_intelhad_prog_buffer(struct snd_intelhad *intelhaddata, -+ int start, int end); -+int snd_intelhad_invd_buffer(int start, int end); -+inline int snd_intelhad_read_len(struct snd_intelhad *intelhaddata); -+void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata); -+ -+/* Register access functions */ -+inline int had_get_hwstate(struct snd_intelhad *intelhaddata); -+inline int had_get_caps(enum had_caps_list query_element, void *capabilties); -+inline int had_set_caps(enum had_caps_list set_element, void *capabilties); -+inline int had_read_register(uint32_t reg_addr, uint32_t *data); -+inline int had_write_register(uint32_t reg_addr, uint32_t data); -+inline int had_read_modify(uint32_t reg_addr, uint32_t data, uint32_t mask); -+#endif -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio_if.c b/sound/hdmi_audio/intel_mid_hdmi_audio_if.c -new file mode 100644 -index 0000000..acc407d ---- /dev/null -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio_if.c -@@ -0,0 +1,533 @@ -+/* -+ * intel_mid_hdmi_audio_if.c - Intel HDMI audio driver for MID -+ * -+ * Copyright (C) 2010 Intel Corp -+ * Authors: Sailaja Bandarupalli -+ * Ramesh Babu K V -+ * Vaibhav Agarwal -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * 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. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * ALSA driver for Intel MID HDMI audio controller. This file contains -+ * interface functions exposed to HDMI Display driver and code to register -+ * with ALSA framework.. -+ */ -+ -+#define pr_fmt(fmt) "had: " fmt -+ -+#include -+#include -+#include -+#include -+#include "intel_mid_hdmi_audio.h" -+ -+ -+/** -+ * hdmi_audio_query - hdmi audio query function -+ * -+ *@haddata: pointer to HAD private data -+ *@event: audio event for which this method is invoked -+ * -+ * This function is called by client driver to query the -+ * hdmi audio. -+ */ -+int hdmi_audio_query(void *haddata, hdmi_audio_event_t event) -+{ -+ struct snd_pcm_substream *substream = NULL; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; -+ -+ if (intelhaddata->stream_info.had_substream) -+ substream = intelhaddata->stream_info.had_substream; -+ had_stream = intelhaddata->private_data; -+ switch (event.type) { -+ case HAD_EVENT_QUERY_IS_AUDIO_BUSY: -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ -+ if ((had_stream->stream_type == HAD_RUNNING_STREAM) || -+ substream) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, -+ flag_irqs); -+ pr_debug("Audio stream active\n"); -+ return -EBUSY; -+ } -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ break; -+ -+ case HAD_EVENT_QUERY_IS_AUDIO_SUSPENDED: -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_SUSPENDED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, -+ flag_irqs); -+ pr_debug("Audio is suspended\n"); -+ return 1; -+ } -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ break; -+ -+ default: -+ pr_debug("error un-handled event !!\n"); -+ return -EINVAL; -+ break; -+ -+ } -+ -+ return 0; -+} -+ -+/** -+ * hdmi_audio_suspend - power management suspend function -+ * -+ *@haddata: pointer to HAD private data -+ *@event: pm event for which this method is invoked -+ * -+ * This function is called by client driver to suspend the -+ * hdmi audio. -+ */ -+int hdmi_audio_suspend(void *haddata, hdmi_audio_event_t event) -+{ -+ int caps, retval = 0; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ struct snd_pcm_substream *substream; -+ struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; -+ -+ pr_debug("Enter:%s", __func__); -+ -+ had_stream = intelhaddata->private_data; -+ substream = intelhaddata->stream_info.had_substream; -+ -+ if (intelhaddata->dev->power.runtime_status != RPM_SUSPENDED) { -+ pr_err("audio stream is active\n"); -+ return -EAGAIN; -+ } -+ -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_debug("had not connected\n"); -+ return retval; -+ } -+ -+ if (intelhaddata->drv_status == HAD_DRV_SUSPENDED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_debug("had already suspended\n"); -+ return retval; -+ } -+ -+ intelhaddata->drv_status = HAD_DRV_SUSPENDED; -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ /* -+ * ToDo: Need to disable UNDERRUN interrupts as well -+ * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; -+ */ -+ caps = HDMI_AUDIO_BUFFER_DONE; -+ had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); -+ had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); -+ pr_debug("Exit:%s", __func__); -+ return retval; -+} -+ -+/** -+ * hdmi_audio_resume - power management resume function -+ * -+ *@haddata: pointer to HAD private data -+ * -+ * This function is called by client driver to resume the -+ * hdmi audio. -+ */ -+int hdmi_audio_resume(void *haddata) -+{ -+ int caps, retval = 0; -+ struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; -+ unsigned long flag_irqs; -+ -+ pr_debug("Enter:%s", __func__); -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_debug("had not connected\n"); -+ return 0; -+ } -+ -+ if (intelhaddata->drv_status != HAD_DRV_SUSPENDED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_err("had is not in suspended state\n"); -+ return 0; -+ } -+ -+ if (had_get_hwstate(intelhaddata)) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_err("Failed to resume. Device not accessible\n"); -+ return -ENODEV; -+ } -+ -+ intelhaddata->drv_status = HAD_DRV_CONNECTED; -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ /* -+ * ToDo: Need to enable UNDERRUN interrupts as well -+ * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; -+ */ -+ caps = HDMI_AUDIO_BUFFER_DONE; -+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO_INT, &caps); -+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO, NULL); -+ pr_debug("Exit:%s", __func__); -+ return retval; -+} -+ -+static inline int had_chk_intrmiss(struct snd_intelhad *intelhaddata, -+ enum intel_had_aud_buf_type buf_id) -+{ -+ int i, intr_count = 0; -+ enum intel_had_aud_buf_type buff_done; -+ u32 buf_size, buf_addr; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ -+ had_stream = intelhaddata->private_data; -+ -+ buff_done = buf_id; -+ -+ intr_count = snd_intelhad_read_len(intelhaddata); -+ if (intr_count > 1) { -+ /* In case of active playback */ -+ pr_err("Driver detected %d missed buffer done interrupt(s)!!!!\n", -+ (intr_count - 1)); -+ if (intr_count > 3) -+ return intr_count; -+ -+ buf_id += (intr_count - 1); -+ /* Reprogram registers*/ -+ for (i = buff_done; i < buf_id; i++) { -+ int j = i % 4; -+ -+ buf_size = intelhaddata->buf_info[j].buf_size; -+ buf_addr = intelhaddata->buf_info[j].buf_addr; -+ had_write_register(AUD_BUF_A_LENGTH + -+ (j * HAD_REG_WIDTH), buf_size); -+ had_write_register( -+ AUD_BUF_A_ADDR+(j * HAD_REG_WIDTH), -+ (buf_addr | BIT(0) | BIT(1))); -+ } -+ buf_id = buf_id % 4; -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ intelhaddata->buff_done = buf_id; -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ } -+ -+ return intr_count; -+} -+ -+int had_process_buffer_done(struct snd_intelhad *intelhaddata) -+{ -+ int retval = 0; -+ u32 len = 1; -+ enum intel_had_aud_buf_type buf_id; -+ enum intel_had_aud_buf_type buff_done; -+ struct pcm_stream_info *stream; -+ u32 buf_size; -+ struct had_pvt_data *had_stream; -+ int intr_count; -+ enum had_status_stream stream_type; -+ unsigned long flag_irqs; -+ -+ had_stream = intelhaddata->private_data; -+ stream = &intelhaddata->stream_info; -+ intr_count = 1; -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_err("%s:Device already disconnected\n", __func__); -+ return retval; -+ } -+ buf_id = intelhaddata->curr_buf; -+ intelhaddata->buff_done = buf_id; -+ buff_done = intelhaddata->buff_done; -+ buf_size = intelhaddata->buf_info[buf_id].buf_size; -+ stream_type = had_stream->stream_type; -+ -+ pr_debug("Enter:%s buf_id=%d", __func__, buf_id); -+ -+ /* Every debug statement has an implication -+ * of ~5msec. Thus, avoid having >3 debug statements -+ * for each buffer_done handling. -+ */ -+ -+ /* Check for any intr_miss in case of active playback */ -+ if (had_stream->stream_type == HAD_RUNNING_STREAM) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ intr_count = had_chk_intrmiss(intelhaddata, buf_id); -+ if (!intr_count || (intr_count > 3)) { -+ pr_err("HAD SW state in non-recoverable!!! mode\n"); -+ pr_err("Already played stale data\n"); -+ return retval; -+ } -+ buf_id += (intr_count - 1); -+ buf_id = buf_id % 4; -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ } -+ -+ intelhaddata->buf_info[buf_id].is_valid = true; -+ if (intelhaddata->valid_buf_cnt-1 == buf_id) { -+ if (had_stream->stream_type >= HAD_RUNNING_STREAM) -+ intelhaddata->curr_buf = HAD_BUF_TYPE_A; -+ } else -+ intelhaddata->curr_buf = buf_id + 1; -+ -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ -+ if (had_get_hwstate(intelhaddata)) { -+ pr_err("HDMI cable plugged-out\n"); -+ return retval; -+ } -+ -+ /*Reprogram the registers with addr and length*/ -+ had_write_register(AUD_BUF_A_LENGTH + -+ (buf_id * HAD_REG_WIDTH), buf_size); -+ had_write_register(AUD_BUF_A_ADDR+(buf_id * HAD_REG_WIDTH), -+ intelhaddata->buf_info[buf_id].buf_addr| -+ BIT(0) | BIT(1)); -+ -+ had_read_register(AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH), -+ &len); -+ pr_debug("%s:Enabled buf[%d]\n", __func__, buf_id); -+ -+ /* In case of actual data, -+ * report buffer_done to above ALSA layer -+ */ -+ buf_size = intelhaddata->buf_info[buf_id].buf_size; -+ if (stream_type >= HAD_RUNNING_STREAM) { -+ intelhaddata->stream_info.buffer_rendered += -+ (intr_count * buf_size); -+ stream->period_elapsed(stream->had_substream); -+ } -+ -+ return retval; -+} -+ -+int had_process_buffer_underrun(struct snd_intelhad *intelhaddata) -+{ -+ int retval = 0; -+ enum intel_had_aud_buf_type buf_id; -+ struct pcm_stream_info *stream; -+ struct had_pvt_data *had_stream; -+ enum had_status_stream stream_type; -+ unsigned long flag_irqs; -+ int drv_status; -+ -+ had_stream = intelhaddata->private_data; -+ stream = &intelhaddata->stream_info; -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ buf_id = intelhaddata->curr_buf; -+ stream_type = had_stream->stream_type; -+ intelhaddata->buff_done = buf_id; -+ drv_status = intelhaddata->drv_status; -+ if (stream_type == HAD_RUNNING_STREAM) -+ intelhaddata->curr_buf = HAD_BUF_TYPE_A; -+ -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ -+ pr_debug("Enter:%s buf_id=%d, stream_type=%d\n", -+ __func__, buf_id, stream_type); -+ -+ intelhaddata->ops->handle_underrun(intelhaddata); -+ -+ if (drv_status == HAD_DRV_DISCONNECTED) { -+ pr_err("%s:Device already disconnected\n", __func__); -+ return retval; -+ } -+ -+ if (stream_type == HAD_RUNNING_STREAM) { -+ /* Report UNDERRUN error to above layers */ -+ intelhaddata->flag_underrun = 1; -+ stream->period_elapsed(stream->had_substream); -+ } -+ -+ return retval; -+} -+ -+int had_process_hot_plug(struct snd_intelhad *intelhaddata) -+{ -+ int retval = 0; -+ enum intel_had_aud_buf_type buf_id; -+ struct snd_pcm_substream *substream; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ -+ pr_debug("Enter:%s", __func__); -+ -+ substream = intelhaddata->stream_info.had_substream; -+ had_stream = intelhaddata->private_data; -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_CONNECTED) { -+ pr_debug("Device already connected\n"); -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ return retval; -+ } -+ buf_id = intelhaddata->curr_buf; -+ intelhaddata->buff_done = buf_id; -+ intelhaddata->drv_status = HAD_DRV_CONNECTED; -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ -+ pr_debug("Processing HOT_PLUG, buf_id = %d\n", buf_id); -+ -+ /* Query display driver for audio register base */ -+ if (intelhaddata->reg_ops.hdmi_audio_get_register_base -+ (&intelhaddata->audio_reg_base)) { -+ pr_err("Unable to get audio reg base from Display driver\n"); -+ goto err; -+ } -+ if(intelhaddata->audio_reg_base == 0){ -+ pr_err("audio reg base value is NULL\n"); -+ goto err; -+ } -+ -+ pr_debug("%s audio_reg_base = %x\n",__func__, intelhaddata->audio_reg_base); -+ -+ /* Safety check */ -+ if (substream) { -+ pr_debug("There should not be active PB from ALSA\n"); -+ pr_debug("Signifies, cable is plugged-in even before\n"); -+ pr_debug("processing snd_pcm_disconnect\n"); -+ /* Set runtime->state to hw_params done */ -+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); -+ } -+ -+ had_build_channel_allocation_map(intelhaddata); -+ -+ return retval; -+ -+err: -+ pm_runtime_disable(intelhaddata->dev); -+ intelhaddata->dev = NULL; -+ return retval; -+} -+ -+int had_process_hot_unplug(struct snd_intelhad *intelhaddata) -+{ -+ int caps, retval = 0; -+ enum intel_had_aud_buf_type buf_id; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ -+ pr_debug("Enter:%s", __func__); -+ -+ had_stream = intelhaddata->private_data; -+ buf_id = intelhaddata->curr_buf; -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { -+ pr_debug("Device already disconnected\n"); -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ return retval; -+ } else { -+ /* Disable Audio */ -+ caps = HDMI_AUDIO_BUFFER_DONE; -+ retval = had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); -+ retval = had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); -+ intelhaddata->ops->enable_audio(intelhaddata->stream_info.had_substream, 0); -+ } -+ -+ intelhaddata->drv_status = HAD_DRV_DISCONNECTED; -+ /* Report to above ALSA layer */ -+ if (intelhaddata->stream_info.had_substream != NULL) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_debug("%s: unlock -> sending pcm_stop -> lock\n", __func__); -+ snd_pcm_stop(intelhaddata->stream_info.had_substream, -+ SNDRV_PCM_STATE_DISCONNECTED); -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ } -+ -+ had_stream->stream_type = HAD_INIT; -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ kfree(intelhaddata->chmap->chmap); -+ intelhaddata->chmap->chmap = NULL; -+ intelhaddata->audio_reg_base = 0; -+ pr_debug("%s: unlocked -> returned\n", __func__); -+ -+ return retval; -+} -+ -+/** -+ * had_event_handler - Call back function to handle events -+ * -+ * @event_type: Event type to handle -+ * @data: data related to the event_type -+ * -+ * This function is invoked to handle HDMI events from client driver. -+ */ -+int had_event_handler(enum had_event_type event_type, void *data) -+{ -+ int retval = 0; -+ struct snd_intelhad *intelhaddata = data; -+ enum intel_had_aud_buf_type buf_id; -+ struct snd_pcm_substream *substream; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ -+ buf_id = intelhaddata->curr_buf; -+ had_stream = intelhaddata->private_data; -+ -+ /* Switching to a function can drop atomicity even in INTR context. -+ * Thus, a big lock is acquired to maintain atomicity. -+ * This can be optimized later. -+ * Currently, only buffer_done/_underrun executes in INTR context. -+ * Also, locking is implemented separately to avoid real contention -+ * of data(struct intelhaddata) between IRQ/SOFT_IRQ/PROCESS context. -+ */ -+ substream = intelhaddata->stream_info.had_substream; -+ switch (event_type) { -+ case HAD_EVENT_AUDIO_BUFFER_DONE: -+ retval = had_process_buffer_done(intelhaddata); -+ break; -+ -+ case HAD_EVENT_AUDIO_BUFFER_UNDERRUN: -+ retval = had_process_buffer_underrun(intelhaddata); -+ break; -+ -+ case HAD_EVENT_HOT_PLUG: -+ retval = had_process_hot_plug(intelhaddata); -+ break; -+ -+ case HAD_EVENT_HOT_UNPLUG: -+ retval = had_process_hot_unplug(intelhaddata); -+ break; -+ -+ case HAD_EVENT_MODE_CHANGING: -+ pr_debug(" called _event_handler with _MODE_CHANGE event\n"); -+ /* Process only if stream is active & cable Plugged-in */ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status >= HAD_DRV_DISCONNECTED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, -+ flag_irqs); -+ break; -+ } -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ if ((had_stream->stream_type == HAD_RUNNING_STREAM) -+ && substream) -+ retval = hdmi_audio_mode_change(substream); -+ break; -+ -+ default: -+ pr_debug("error un-handled event !!\n"); -+ retval = -EINVAL; -+ break; -+ -+ } -+ return retval; -+} - -From e39481dfca2ad3840f86dd7012e7c8968b794ab5 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Sat, 20 Feb 2016 18:08:41 -0600 -Subject: [PATCH 09/14] add dependency on PM_RUNTIME - -Signed-off-by: Pierre-Louis Bossart ---- - sound/Kconfig | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/sound/Kconfig b/sound/Kconfig -index 75c679e..b8b4fce 100644 ---- a/sound/Kconfig -+++ b/sound/Kconfig -@@ -138,6 +138,7 @@ config AC97_BUS - config SUPPORT_HDMI - bool "SUPPORT_HDMI" - depends on DRM_I915 -+ select PM_RUNTIME - default n - help - Choose this option to support HDMI. - -From a00e9b947155b75973b30609e2f2a808a42845fe Mon Sep 17 00:00:00 2001 -From: David Henningsson -Date: Fri, 21 Aug 2015 11:08:47 +0200 -Subject: [PATCH 10/14] hdmi_audio: Improve position reporting - -Using a hw register to calculate sub-period position reports. - -This makes PulseAudio happier. - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart ---- - sound/hdmi_audio/intel_mid_hdmi_audio.c | 12 +++++++++++- - 1 file changed, 11 insertions(+), 1 deletion(-) - -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -index d8c5574..b2337c3 100644 ---- a/sound/hdmi_audio/intel_mid_hdmi_audio.c -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -1550,6 +1550,8 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( - { - struct snd_intelhad *intelhaddata; - u32 bytes_rendered = 0; -+ u32 t; -+ int buf_id; - - /* pr_debug("snd_intelhad_pcm_pointer called\n"); */ - -@@ -1560,6 +1562,14 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( - return SNDRV_PCM_POS_XRUN; - } - -+ buf_id = intelhaddata->curr_buf % 4; -+ had_read_register(AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH), &t); -+ if (t == 0) { -+ pr_debug("discovered buffer done for buf %d\n", buf_id); -+ /* had_process_buffer_done(intelhaddata); */ -+ } -+ t = intelhaddata->buf_info[buf_id].buf_size - t; -+ - if (intelhaddata->stream_info.buffer_rendered) - div_u64_rem(intelhaddata->stream_info.buffer_rendered, - intelhaddata->stream_info.ring_buf_size, -@@ -1567,7 +1577,7 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( - - intelhaddata->stream_info.buffer_ptr = bytes_to_frames( - substream->runtime, -- bytes_rendered); -+ bytes_rendered + t); - return intelhaddata->stream_info.buffer_ptr; - } - - -From 36226b455aab985f7e414a5fda7d3844df43078d Mon Sep 17 00:00:00 2001 -From: David Henningsson -Date: Fri, 21 Aug 2015 11:18:19 +0200 -Subject: [PATCH 11/14] hdmi_audio: Fixup some monitor - -I think this change was given to us, and they claimed it fixed an issue -on some monitor brand. I'm not sure what this patch actually does. - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart ---- - sound/hdmi_audio/intel_mid_hdmi_audio.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -index b2337c3..1667748 100644 ---- a/sound/hdmi_audio/intel_mid_hdmi_audio.c -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -386,6 +386,7 @@ static void snd_intelhad_reset_audio_v2(u8 reset) - static int had_prog_status_reg(struct snd_pcm_substream *substream, - struct snd_intelhad *intelhaddata) - { -+ union aud_cfg cfg_val = {.cfg_regval = 0}; - union aud_ch_status_0 ch_stat0 = {.status_0_regval = 0}; - union aud_ch_status_1 ch_stat1 = {.status_1_regval = 0}; - int format; -@@ -396,6 +397,8 @@ static int had_prog_status_reg(struct snd_pcm_substream *substream, - IEC958_AES0_NONAUDIO)>>1; - ch_stat0.status_0_regx.clk_acc = (intelhaddata->aes_bits & - IEC958_AES3_CON_CLOCK)>>4; -+ cfg_val.cfg_regx.val_bit = ch_stat0.status_0_regx.lpcm_id; -+ - switch (substream->runtime->rate) { - case AUD_SAMPLE_RATE_32: - ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_32KHZ; -@@ -474,7 +477,6 @@ int snd_intelhad_prog_audio_ctrl_v2(struct snd_pcm_substream *substream, - else - cfg_val.cfg_regx_v2.layout = LAYOUT1; - -- cfg_val.cfg_regx_v2.val_bit = 1; - had_write_register(AUD_CONFIG, cfg_val.cfg_regval); - return 0; - } -@@ -530,7 +532,6 @@ int snd_intelhad_prog_audio_ctrl_v1(struct snd_pcm_substream *substream, - - } - -- cfg_val.cfg_regx.val_bit = 1; - had_write_register(AUD_CONFIG, cfg_val.cfg_regval); - return 0; - } - -From 6f7fadbd23959ca99e2b4f53f36aada2eff75f9d Mon Sep 17 00:00:00 2001 -From: Toyo Abe -Date: Thu, 3 Mar 2016 12:57:41 +0900 -Subject: [PATCH 12/14] hdmi_audio: Fix mishandling of AUD_HDMI_STATUS_v2 - register. - -According to the datasheet, write one to clear these UNDERRUN flag bits. -This fixes the following warning in dmesg. - -[15357.574902] had: Unable to clear UNDERRUN bits - -Signed-off-by: Toyo Abe -Signed-off-by: Pierre-Louis Bossart ---- - sound/hdmi_audio/intel_mid_hdmi_audio.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -index 1667748..86db38e 100644 ---- a/sound/hdmi_audio/intel_mid_hdmi_audio.c -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -1135,7 +1135,6 @@ static void had_clear_underrun_intr_v2(struct snd_intelhad *intelhaddata) - pr_debug("HDMI status =0x%x\n", hdmi_status); - if (hdmi_status & AUD_CONFIG_MASK_UNDERRUN) { - i++; -- hdmi_status &= ~AUD_CONFIG_MASK_UNDERRUN; - had_write_register(AUD_HDMI_STATUS_v2, hdmi_status); - } else - break; - -From 4525147d750c00368edb1632b658778d53fc8c85 Mon Sep 17 00:00:00 2001 -From: Jerome Anand -Date: Fri, 1 Apr 2016 11:07:48 +0530 -Subject: [PATCH 13/14] Create a platform device for hdmi audio driver and - allocate full resources - -Signed-off-by: Pierre-Louis Bossart ---- - sound/hdmi_audio/intel_mid_hdmi_audio.c | 22 +++++++++++++++++++--- - 1 file changed, 19 insertions(+), 3 deletions(-) - -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -index 86db38e..6497b6f 100644 ---- a/sound/hdmi_audio/intel_mid_hdmi_audio.c -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -47,6 +47,8 @@ static int hdmi_card_index = SNDRV_DEFAULT_IDX1; - static char *hdmi_card_id = SNDRV_DEFAULT_STR1; - static struct snd_intelhad *had_data; - -+struct platform_device *gpdev; -+ - module_param(hdmi_card_index, int, 0444); - MODULE_PARM_DESC(hdmi_card_index, - "Index value for INTEL Intel HDMI Audio controller."); -@@ -1481,9 +1483,9 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream) - } - - pr_debug("period_size=%d\n", -- frames_to_bytes(runtime, runtime->period_size)); -+ (int)frames_to_bytes(runtime, runtime->period_size)); - pr_debug("periods=%d\n", runtime->periods); -- pr_debug("buffer_size=%d\n", snd_pcm_lib_buffer_bytes(substream)); -+ pr_debug("buffer_size=%d\n", (int)snd_pcm_lib_buffer_bytes(substream)); - pr_debug("rate=%d\n", runtime->rate); - pr_debug("channels=%d\n", runtime->channels); - -@@ -1997,10 +1999,16 @@ static struct platform_driver had_driver = { - #ifdef CONFIG_PM - .pm = &had_pm_ops, - #endif -- .acpi_match_table = ACPI_PTR(had_acpi_ids), -+ //.acpi_match_table = ACPI_PTR(had_acpi_ids), - }, - }; - -+static const struct platform_device_info hdmi_audio_dev_info = { -+ .name = HDMI_AUDIO_DRIVER, -+ .id = -1, -+ .dma_mask = DMA_BIT_MASK(32), -+}; -+ - /* - * alsa_card_intelhad_init- driver init function - * This function is called when driver module is inserted -@@ -2020,6 +2028,13 @@ static int __init alsa_card_intelhad_init(void) - return retval; - } - -+ //gpdev = platform_device_register_simple(HDMI_AUDIO_DRIVER, -1, NULL, 0); -+ gpdev = platform_device_register_full(&hdmi_audio_dev_info); -+ if (!gpdev) { -+ pr_err("[jerome] Failed to register platform device \n"); -+ return -1; -+ } -+ - pr_debug("init complete\n"); - return retval; - } -@@ -2032,6 +2047,7 @@ static void __exit alsa_card_intelhad_exit(void) - { - pr_debug("had_exit called\n"); - platform_driver_unregister(&had_driver); -+ platform_device_unregister(gpdev); - } - late_initcall(alsa_card_intelhad_init); - module_exit(alsa_card_intelhad_exit); - -From c0a058d6abd7616ef12e7970d104ca09fc31ccbc Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Wed, 11 May 2016 14:03:04 -0500 -Subject: [PATCH 14/14] drm/i915: fix LPE audio issues when resolution changes - -Make sure N/CTS parameters are updated on each resolution -change, not just at boot - -Signed-off-by: Pierre-Louis Bossart ---- - drivers/gpu/drm/i915/intel_display.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c -index 82584aa..661d232 100644 ---- a/drivers/gpu/drm/i915/intel_display.c -+++ b/drivers/gpu/drm/i915/intel_display.c -@@ -7962,7 +7962,7 @@ static int i9xx_crtc_compute_clock(struct intel_crtc *crtc, - - /* Added for HDMI Audio */ - if ((IS_CHERRYVIEW(dev)) || (IS_VALLEYVIEW(dev))) { -- if(intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) { -+ if (intel_pipe_will_have_type(crtc_state, INTEL_OUTPUT_HDMI)) { - dev_priv->tmds_clock_speed = crtc_state->port_clock; - - mid_hdmi_audio_signal_event(dev_priv->dev,