[alsa-devel] [RFC 00/11] ASoC: hda - Add ASoC Skylake HD audio driver
This series of 11 patches adds support for ASoC Skylake HD Audio driver.
Modified the hda core bus framework to support id_table match for ASoC DEV type instead of adding new wrapper. So dropped below RFC [RFC 3/3] ALSA: hda - add soc hda bus wrapper from the series [RFC 0/3] ALSA: Add soc hda bus support
Jeeja KP (11): ALSA: hda - add id table support for hdac device/driver ALSA: hda - Add generic helper function to register/unregister driver ALSA: hda - add generic function to unregister all devices on bus ALSA: hda: corrected snd_hdac_stream_init() declaration ALSA: hda - exported link_reset enter/exit ALSA: hda - moved alloc/free stream pages function to controller library ALSA: hda - add generic functions to set hdac stream params ASoC: hda - add Skylake platform driver ALSA: hda - moved interrupt handler to controller library ASoC: hda - add Skylake HD audio driver ASoC: hda - enable ASoC Skylake HD audio driver
include/sound/hdaudio.h | 43 ++- sound/hda/hda_bus_type.c | 2 + sound/hda/hdac_bus.c | 55 +++ sound/hda/hdac_controller.c | 78 ++++- sound/hda/hdac_stream.c | 29 ++ sound/soc/Kconfig | 3 +- sound/soc/Makefile | 1 + sound/soc/hda/Kconfig | 29 ++ sound/soc/hda/Makefile | 6 + sound/soc/hda/hda_skl.c | 829 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/hda/hda_skl.h | 49 +++ sound/soc/hda/hda_skl_pcm.c | 501 ++++++++++++++++++++++++++ 12 files changed, 1619 insertions(+), 6 deletions(-) create mode 100644 sound/soc/hda/Kconfig create mode 100644 sound/soc/hda/Makefile create mode 100644 sound/soc/hda/hda_skl.c create mode 100644 sound/soc/hda/hda_skl.h create mode 100644 sound/soc/hda/hda_skl_pcm.c
From: Jeeja KP jeeja.kp@intel.com
For device/driver matching, uses id table if device type is HDA_DEV_ASOC.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdaudio.h | 16 ++++++++++++++++ sound/hda/hda_bus_type.c | 2 ++ sound/hda/hdac_bus.c | 24 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 1652764..3628a09 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -42,6 +42,14 @@ struct hdac_widget_tree; */ extern struct bus_type snd_hda_bus_type;
+/*HDA device table*/ +#define HDA_NAME_SIZE 20 +struct hdac_device_id { + __u32 id; + char name[HDA_NAME_SIZE]; + unsigned long driver_data; +}; + /* * HD-audio codec base device */ @@ -82,6 +90,8 @@ struct hdac_device {
/* sysfs */ struct hdac_widget_tree *widgets; + + const struct hdac_device_id *id_entry; };
/* device/driver type used for matching */ @@ -96,6 +106,9 @@ enum { HDA_INPUT, HDA_OUTPUT };
+ +#define hda_get_device_id(pdev) ((pdev)->id_entry) + #define dev_to_hdac_dev(_dev) container_of(_dev, struct hdac_device, dev)
int snd_hdac_device_init(struct hdac_device *dev, struct hdac_bus *bus, @@ -126,12 +139,14 @@ static inline void snd_hdac_power_up(struct hdac_device *codec) {} static inline void snd_hdac_power_down(struct hdac_device *codec) {} #endif
+ /* * HD-audio codec base driver */ struct hdac_driver { struct device_driver driver; int type; + struct hdac_device_id *id_table; int (*match)(struct hdac_device *dev, struct hdac_driver *drv); void (*unsol_event)(struct hdac_device *dev, unsigned int event); }; @@ -248,6 +263,7 @@ void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex); int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec); void snd_hdac_bus_remove_device(struct hdac_bus *bus, struct hdac_device *codec); +int snd_hdac_bus_match_id(struct hdac_device *dev, struct hdac_driver *drv);
static inline void snd_hdac_codec_link_up(struct hdac_device *codec) { diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c index 519914a..8899c56 100644 --- a/sound/hda/hda_bus_type.c +++ b/sound/hda/hda_bus_type.c @@ -19,6 +19,8 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv) return 0; if (hdrv->match) return hdrv->match(hdev, hdrv); + if (hdrv->id_table) + return snd_hdac_bus_match_id(hdev, hdrv); return 1; }
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 3294fd4..f0ee295 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -195,3 +195,27 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus, bus->num_codecs--; } EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device); + +/** + * snd_hdac_bus_match_id - bind hdac device to hdac driver. + * @hdev: device. + * @hdrv: driver. + * + */ +int snd_hdac_bus_match_id(struct hdac_device *dev, + struct hdac_driver *drv) +{ + if (drv->id_table) { + struct hdac_device_id *id = drv->id_table; + + while (id->name[0]) { + if (dev->vendor_id == id->id) { + dev->id_entry = id; + return 1; + } + id++; + } + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_match_id);
At Sun, 12 Apr 2015 18:06:08 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
For device/driver matching, uses id table if device type is HDA_DEV_ASOC.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 16 ++++++++++++++++ sound/hda/hda_bus_type.c | 2 ++ sound/hda/hdac_bus.c | 24 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 1652764..3628a09 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -42,6 +42,14 @@ struct hdac_widget_tree; */ extern struct bus_type snd_hda_bus_type;
+/*HDA device table*/ +#define HDA_NAME_SIZE 20 +struct hdac_device_id {
- __u32 id;
- char name[HDA_NAME_SIZE];
- unsigned long driver_data;
+};
/*
- HD-audio codec base device
*/ @@ -82,6 +90,8 @@ struct hdac_device {
/* sysfs */ struct hdac_widget_tree *widgets;
- const struct hdac_device_id *id_entry;
};
/* device/driver type used for matching */ @@ -96,6 +106,9 @@ enum { HDA_INPUT, HDA_OUTPUT };
+#define hda_get_device_id(pdev) ((pdev)->id_entry)
#define dev_to_hdac_dev(_dev) container_of(_dev, struct hdac_device, dev)
int snd_hdac_device_init(struct hdac_device *dev, struct hdac_bus *bus, @@ -126,12 +139,14 @@ static inline void snd_hdac_power_up(struct hdac_device *codec) {} static inline void snd_hdac_power_down(struct hdac_device *codec) {} #endif
/*
- HD-audio codec base driver
*/ struct hdac_driver { struct device_driver driver; int type;
- struct hdac_device_id *id_table; int (*match)(struct hdac_device *dev, struct hdac_driver *drv); void (*unsol_event)(struct hdac_device *dev, unsigned int event);
}; @@ -248,6 +263,7 @@ void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex); int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec); void snd_hdac_bus_remove_device(struct hdac_bus *bus, struct hdac_device *codec); +int snd_hdac_bus_match_id(struct hdac_device *dev, struct hdac_driver *drv);
static inline void snd_hdac_codec_link_up(struct hdac_device *codec) { diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c index 519914a..8899c56 100644 --- a/sound/hda/hda_bus_type.c +++ b/sound/hda/hda_bus_type.c @@ -19,6 +19,8 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv) return 0; if (hdrv->match) return hdrv->match(hdev, hdrv);
- if (hdrv->id_table)
return 1;return snd_hdac_bus_match_id(hdev, hdrv);
}
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 3294fd4..f0ee295 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -195,3 +195,27 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus, bus->num_codecs--; } EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);
+/**
- snd_hdac_bus_match_id - bind hdac device to hdac driver.
- @hdev: device.
- @hdrv: driver.
- */
+int snd_hdac_bus_match_id(struct hdac_device *dev,
struct hdac_driver *drv)
+{
- if (drv->id_table) {
struct hdac_device_id *id = drv->id_table;
while (id->name[0]) {
if (dev->vendor_id == id->id) {
dev->id_entry = id;
return 1;
}
id++;
}
- }
- return 0;
+} +EXPORT_SYMBOL_GPL(snd_hdac_bus_match_id);
Please make these rather ASoC specific. These ID checks aren't needed for the legacy driver as they need more other fields checks like revision id.
For example,
struct hda_soc_device { struct hdac_device core; const struct hda_soc_device_id *id_entry; .... };
and give the match ops to hdac_driver for checking the ID.
thanks,
Takashi
On Mon, Apr 13, 2015 at 01:46:25PM +0200, Takashi Iwai wrote:
At Sun, 12 Apr 2015 18:06:08 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c index 519914a..8899c56 100644 --- a/sound/hda/hda_bus_type.c +++ b/sound/hda/hda_bus_type.c @@ -19,6 +19,8 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv) return 0; if (hdrv->match) return hdrv->match(hdev, hdrv);
- if (hdrv->id_table)
return 1;return snd_hdac_bus_match_id(hdev, hdrv);
}
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 3294fd4..f0ee295 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -195,3 +195,27 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus, bus->num_codecs--; } EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);
+/**
- snd_hdac_bus_match_id - bind hdac device to hdac driver.
- @hdev: device.
- @hdrv: driver.
- */
+int snd_hdac_bus_match_id(struct hdac_device *dev,
struct hdac_driver *drv)
+{
- if (drv->id_table) {
struct hdac_device_id *id = drv->id_table;
while (id->name[0]) {
if (dev->vendor_id == id->id) {
dev->id_entry = id;
return 1;
}
id++;
}
- }
- return 0;
+} +EXPORT_SYMBOL_GPL(snd_hdac_bus_match_id);
Please make these rather ASoC specific. These ID checks aren't needed for the legacy driver as they need more other fields checks like revision id.
For example,
struct hda_soc_device { struct hdac_device core; const struct hda_soc_device_id *id_entry; .... };
and give the match ops to hdac_driver for checking the ID.
Okay, will move these to asoc then. I am planning to move this to generic asoc layer and not in skl driver specfic
From: Jeeja KP jeeja.kp@intel.com
ASOC HD codec drivers will use the generic functions to register/unregister driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdaudio.h | 3 +++ sound/hda/hdac_bus.c | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 3628a09..b63cd03 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -153,6 +153,9 @@ struct hdac_driver {
#define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver)
+int snd_hdac_driver_register(struct hdac_driver *drv); +void snd_hdac_driver_unregister(struct hdac_driver *drv); + /* * HD-audio bus base driver */ diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index f0ee295..4ecb4ec 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -219,3 +219,24 @@ int snd_hdac_bus_match_id(struct hdac_device *dev, return 0; } EXPORT_SYMBOL_GPL(snd_hdac_bus_match_id); + +/** + * snd_hdac_driver_register - register a driver for hda devices + * @drv: hdac driver structure + */ +int snd_hdac_driver_register(struct hdac_driver *drv) +{ + drv->driver.bus = &snd_hda_bus_type; + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(snd_hdac_driver_register); + +/** + * snd_hdac_driver_unregister - unregister a driver for hda devices + * @drv: hdac driver structure + */ +void snd_hdac_driver_unregister(struct hdac_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(snd_hdac_driver_unregister);
At Sun, 12 Apr 2015 18:06:09 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
ASOC HD codec drivers will use the generic functions to register/unregister driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 3 +++ sound/hda/hdac_bus.c | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 3628a09..b63cd03 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -153,6 +153,9 @@ struct hdac_driver {
#define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver)
+int snd_hdac_driver_register(struct hdac_driver *drv); +void snd_hdac_driver_unregister(struct hdac_driver *drv);
/*
- HD-audio bus base driver
*/ diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index f0ee295..4ecb4ec 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -219,3 +219,24 @@ int snd_hdac_bus_match_id(struct hdac_device *dev, return 0; } EXPORT_SYMBOL_GPL(snd_hdac_bus_match_id);
+/**
- snd_hdac_driver_register - register a driver for hda devices
- @drv: hdac driver structure
- */
+int snd_hdac_driver_register(struct hdac_driver *drv) +{
- drv->driver.bus = &snd_hda_bus_type;
- return driver_register(&drv->driver);
+} +EXPORT_SYMBOL_GPL(snd_hdac_driver_register);
+/**
- snd_hdac_driver_unregister - unregister a driver for hda devices
- @drv: hdac driver structure
- */
+void snd_hdac_driver_unregister(struct hdac_driver *drv) +{
- driver_unregister(&drv->driver);
+} +EXPORT_SYMBOL_GPL(snd_hdac_driver_unregister);
Hrm, I see no big merits to wrap such a single function call. Once when struct hdac_driver itself is embedded, another redirection is needed.
thanks,
Takashi
From: Jeeja KP jeeja.kp@intel.com
This will be used by SKL controller in driver remove to unregister all hda devices added during probe.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdaudio.h | 2 ++ sound/hda/hdac_bus.c | 10 ++++++++++ 2 files changed, 12 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index b63cd03..247c4a7 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -268,6 +268,8 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus, struct hdac_device *codec); int snd_hdac_bus_match_id(struct hdac_device *dev, struct hdac_driver *drv);
+void snd_hdac_bus_device_unregister(struct hdac_bus *bus); + static inline void snd_hdac_codec_link_up(struct hdac_device *codec) { set_bit(codec->addr, &codec->bus->codec_powered); diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 4ecb4ec..23b6076 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -240,3 +240,13 @@ void snd_hdac_driver_unregister(struct hdac_driver *drv) driver_unregister(&drv->driver); } EXPORT_SYMBOL_GPL(snd_hdac_driver_unregister); + +void snd_hdac_bus_device_unregister(struct hdac_bus *bus) +{ + struct hdac_device *hdev; + + /*unregister all devices on bus */ + list_for_each_entry(hdev, &bus->codec_list, list) + snd_hdac_device_unregister(hdev); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_device_unregister);
At Sun, 12 Apr 2015 18:06:10 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
This will be used by SKL controller in driver remove to unregister all hda devices added during probe.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 2 ++ sound/hda/hdac_bus.c | 10 ++++++++++ 2 files changed, 12 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index b63cd03..247c4a7 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -268,6 +268,8 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus, struct hdac_device *codec); int snd_hdac_bus_match_id(struct hdac_device *dev, struct hdac_driver *drv);
+void snd_hdac_bus_device_unregister(struct hdac_bus *bus);
static inline void snd_hdac_codec_link_up(struct hdac_device *codec) { set_bit(codec->addr, &codec->bus->codec_powered); diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 4ecb4ec..23b6076 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -240,3 +240,13 @@ void snd_hdac_driver_unregister(struct hdac_driver *drv) driver_unregister(&drv->driver); } EXPORT_SYMBOL_GPL(snd_hdac_driver_unregister);
+void snd_hdac_bus_device_unregister(struct hdac_bus *bus) +{
- struct hdac_device *hdev;
- /*unregister all devices on bus */
- list_for_each_entry(hdev, &bus->codec_list, list)
snd_hdac_device_unregister(hdev);
+} +EXPORT_SYMBOL_GPL(snd_hdac_bus_device_unregister);
Again, I don't think we need to keep this in the core code until it's really shared among multiple drivers.
thanks,
Takashi
On Mon, Apr 13, 2015 at 01:54:19PM +0200, Takashi Iwai wrote:
At Sun, 12 Apr 2015 18:06:10 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
This will be used by SKL controller in driver remove to unregister all hda devices added during probe.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 2 ++ sound/hda/hdac_bus.c | 10 ++++++++++ 2 files changed, 12 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index b63cd03..247c4a7 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -268,6 +268,8 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus, struct hdac_device *codec); int snd_hdac_bus_match_id(struct hdac_device *dev, struct hdac_driver *drv);
+void snd_hdac_bus_device_unregister(struct hdac_bus *bus);
static inline void snd_hdac_codec_link_up(struct hdac_device *codec) { set_bit(codec->addr, &codec->bus->codec_powered); diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 4ecb4ec..23b6076 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -240,3 +240,13 @@ void snd_hdac_driver_unregister(struct hdac_driver *drv) driver_unregister(&drv->driver); } EXPORT_SYMBOL_GPL(snd_hdac_driver_unregister);
+void snd_hdac_bus_device_unregister(struct hdac_bus *bus) +{
- struct hdac_device *hdev;
- /*unregister all devices on bus */
- list_for_each_entry(hdev, &bus->codec_list, list)
snd_hdac_device_unregister(hdev);
+} +EXPORT_SYMBOL_GPL(snd_hdac_bus_device_unregister);
Again, I don't think we need to keep this in the core code until it's really shared among multiple drivers.
Sure, since this was quote generic we kept it in corre, but its fine in asoc too
From: Jeeja KP jeeja.kp@intel.com
Corrected snd_hdac_stream_init() declaration in header file.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdaudio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 247c4a7..bffb2a9 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -340,7 +340,7 @@ struct hdac_stream { struct mutex dsp_mutex; };
-void snd_hdac_bus_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, +void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, int idx, int direction, int tag); struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream);
At Sun, 12 Apr 2015 18:06:11 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
Corrected snd_hdac_stream_init() declaration in header file.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 247c4a7..bffb2a9 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -340,7 +340,7 @@ struct hdac_stream { struct mutex dsp_mutex; };
-void snd_hdac_bus_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, +void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, int idx, int direction, int tag); struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream);
I suppose you're referring to the code in topic/hda-dev2 branch of sound-unstable git tree? Then I'll fold the fix into it.
thanks,
Takashi
On Mon, Apr 13, 2015 at 01:52:23PM +0200, Takashi Iwai wrote:
At Sun, 12 Apr 2015 18:06:11 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
Corrected snd_hdac_stream_init() declaration in header file.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 247c4a7..bffb2a9 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -340,7 +340,7 @@ struct hdac_stream { struct mutex dsp_mutex; };
-void snd_hdac_bus_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, +void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, int idx, int direction, int tag); struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream);
I suppose you're referring to the code in topic/hda-dev2 branch of sound-unstable git tree? Then I'll fold the fix into it.
Yes all the code is based on 7 patches you had posted plus the patch series V3 I had sent (which is based on hda-dev2). Please do fold this up and push updated barcnh for me so that I cna rebase these
From: Jeeja KP jeeja.kp@intel.com
Exported azx_enter_link_reset and azx_exit_link_reset functions to be used by the SKL controller driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdaudio.h | 3 +++ sound/hda/hdac_controller.c | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index bffb2a9..4eebc31 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -286,6 +286,8 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
void snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset); void snd_hdac_bus_stop_chip(struct hdac_bus *bus); +void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus); +void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus);
void snd_hdac_bus_update_rirb(struct hdac_bus *bus); void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, @@ -359,6 +361,7 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, unsigned int streams); void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, unsigned int streams); + /*DSP loader functions */ int snd_hdac_load_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, unsigned int byte_size, diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 5b1cd94..23aec0e 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -212,7 +212,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response); */
/* enter link reset */ -static void azx_enter_link_reset(struct hdac_bus *bus) +void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus) { unsigned long timeout;
@@ -224,9 +224,10 @@ static void azx_enter_link_reset(struct hdac_bus *bus) time_before(jiffies, timeout)) usleep_range(500, 1000); } +EXPORT_SYMBOL_GPL(snd_hdac_bus_enter_link_reset);
/* exit link reset */ -static void azx_exit_link_reset(struct hdac_bus *bus) +void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus) { unsigned long timeout;
@@ -236,6 +237,7 @@ static void azx_exit_link_reset(struct hdac_bus *bus) while (!azx_readb(bus, GCTL) && time_before(jiffies, timeout)) usleep_range(500, 1000); } +EXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset);
/* reset codec link */ static int azx_reset(struct hdac_bus *bus, bool full_reset) @@ -247,7 +249,7 @@ static int azx_reset(struct hdac_bus *bus, bool full_reset) azx_writew(bus, STATESTS, STATESTS_INT_MASK);
/* reset controller */ - azx_enter_link_reset(bus); + snd_hdac_bus_enter_link_reset(bus);
/* delay for >= 100us for codec PLL to settle per spec * Rev 0.9 section 5.5.1 @@ -255,7 +257,7 @@ static int azx_reset(struct hdac_bus *bus, bool full_reset) usleep_range(500, 1000);
/* Bring controller out of reset */ - azx_exit_link_reset(bus); + snd_hdac_bus_exit_link_reset(bus);
/* Brent Chartrand said to wait >= 540us for codecs to initialize */ usleep_range(1000, 1200);
At Sun, 12 Apr 2015 18:06:12 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
Exported azx_enter_link_reset and azx_exit_link_reset functions to be used by the SKL controller driver
OK, I can fold them to my patchset.
Takashi
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 3 +++ sound/hda/hdac_controller.c | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index bffb2a9..4eebc31 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -286,6 +286,8 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
void snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset); void snd_hdac_bus_stop_chip(struct hdac_bus *bus); +void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus); +void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus);
void snd_hdac_bus_update_rirb(struct hdac_bus *bus); void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, @@ -359,6 +361,7 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, unsigned int streams); void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, unsigned int streams);
/*DSP loader functions */ int snd_hdac_load_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, unsigned int byte_size, diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 5b1cd94..23aec0e 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -212,7 +212,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response); */
/* enter link reset */ -static void azx_enter_link_reset(struct hdac_bus *bus) +void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus) { unsigned long timeout;
@@ -224,9 +224,10 @@ static void azx_enter_link_reset(struct hdac_bus *bus) time_before(jiffies, timeout)) usleep_range(500, 1000); } +EXPORT_SYMBOL_GPL(snd_hdac_bus_enter_link_reset);
/* exit link reset */ -static void azx_exit_link_reset(struct hdac_bus *bus) +void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus) { unsigned long timeout;
@@ -236,6 +237,7 @@ static void azx_exit_link_reset(struct hdac_bus *bus) while (!azx_readb(bus, GCTL) && time_before(jiffies, timeout)) usleep_range(500, 1000); } +EXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset);
/* reset codec link */ static int azx_reset(struct hdac_bus *bus, bool full_reset) @@ -247,7 +249,7 @@ static int azx_reset(struct hdac_bus *bus, bool full_reset) azx_writew(bus, STATESTS, STATESTS_INT_MASK);
/* reset controller */
- azx_enter_link_reset(bus);
snd_hdac_bus_enter_link_reset(bus);
/* delay for >= 100us for codec PLL to settle per spec
- Rev 0.9 section 5.5.1
@@ -255,7 +257,7 @@ static int azx_reset(struct hdac_bus *bus, bool full_reset) usleep_range(500, 1000);
/* Bring controller out of reset */
- azx_exit_link_reset(bus);
snd_hdac_bus_exit_link_reset(bus);
/* Brent Chartrand said to wait >= 540us for codecs to initialize */ usleep_range(1000, 1200);
-- 1.9.0
On Mon, Apr 13, 2015 at 02:04:32PM +0200, Takashi Iwai wrote:
At Sun, 12 Apr 2015 18:06:12 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
Exported azx_enter_link_reset and azx_exit_link_reset functions to be used by the SKL controller driver
OK, I can fold them to my patchset.
Sounds good :)
From: Jeeja KP jeeja.kp@intel.com
Moved azx_alloc_stream_pages and azx_free_stream_pages to controller library.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdaudio.h | 3 +++ sound/hda/hdac_controller.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 4eebc31..5fc6d81 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -294,6 +294,9 @@ void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, void (*ack)(struct hdac_bus *, struct hdac_stream *));
+int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus); +void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus); + /* * HD-audio stream */ diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 23aec0e..4d4e0d6 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -401,3 +401,47 @@ void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, } } EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq); + +int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus) +{ + struct hdac_stream *hstream; + int num_streams = 0; + int err; + + list_for_each_entry(hstream, &bus->stream_list, list) { + dsp_lock_init(&hstream); + /* allocate memory for the BDL for each stream */ + err = bus->ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, + BDL_SIZE, + &hstream->bdl); + num_streams++; + if (err < 0) + return -ENOMEM; + } + /* allocate memory for the position buffer */ + err = bus->ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, + num_streams * 8, &bus->posbuf); + if (err < 0) + return -ENOMEM; + + /* single page (at least 4096 bytes) must suffice for both ringbuffes */ + return bus->ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, + PAGE_SIZE, &bus->rb); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages); + +void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus) +{ + struct hdac_stream *hstream; + + list_for_each_entry(hstream, &bus->stream_list, list) { + if (hstream->bdl.area) + bus->ops->dma_free_pages( + bus, &hstream->bdl); + } + if (bus->rb.area) + bus->ops->dma_free_pages(bus, &bus->rb); + if (bus->posbuf.area) + bus->ops->dma_free_pages(bus, &bus->posbuf); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages);
From: Jeeja KP jeeja.kp@intel.com
This will be used by hda controller driver to setup stream params in prepare. This function will setup the bdl and periods.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdaudio.h | 2 ++ sound/hda/hdac_stream.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 5fc6d81..1f9f4ec 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -364,6 +364,8 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, unsigned int streams); void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, unsigned int streams); +int snd_set_hdac_stream_params(struct hdac_stream *azx_dev, + unsigned int format_val);
/*DSP loader functions */ int snd_hdac_load_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 394fd15..ce25f8f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -472,6 +472,35 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, } EXPORT_SYMBOL_GPL(snd_hdac_stream_sync);
+int snd_set_hdac_stream_params(struct hdac_stream *azx_dev, + unsigned int format_val) +{ + + unsigned int bufsize, period_bytes; + struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + bufsize = snd_pcm_lib_buffer_bytes(substream); + period_bytes = snd_pcm_lib_period_bytes(substream); + + if (bufsize != azx_dev->bufsize || + period_bytes != azx_dev->period_bytes || + format_val != azx_dev->format_val || + runtime->no_period_wakeup != azx_dev->no_period_wakeup) { + azx_dev->bufsize = bufsize; + azx_dev->period_bytes = period_bytes; + azx_dev->format_val = format_val; + azx_dev->no_period_wakeup = runtime->no_period_wakeup; + err = snd_hdac_stream_setup_periods(azx_dev); + if (err < 0) + return err; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_set_hdac_stream_params); + + int snd_hdac_load_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, unsigned int byte_size, struct snd_dma_buffer *bufp)
At Sun, 12 Apr 2015 18:06:14 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
This will be used by hda controller driver to setup stream params in prepare. This function will setup the bdl and periods.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 2 ++ sound/hda/hdac_stream.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 5fc6d81..1f9f4ec 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -364,6 +364,8 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, unsigned int streams); void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, unsigned int streams); +int snd_set_hdac_stream_params(struct hdac_stream *azx_dev,
unsigned int format_val);
/*DSP loader functions */ int snd_hdac_load_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 394fd15..ce25f8f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -472,6 +472,35 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, } EXPORT_SYMBOL_GPL(snd_hdac_stream_sync);
+int snd_set_hdac_stream_params(struct hdac_stream *azx_dev,
unsigned int format_val)
I'd name it a bit more consistently, e.g. snd_hdac_stream_set_format() or snd_hdac_stream_set_params() or so.
+{
- unsigned int bufsize, period_bytes;
- struct snd_pcm_substream *substream = azx_dev->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
I would do a NULL check for substream to be sure.
thanks,
Takashi
- int err;
- bufsize = snd_pcm_lib_buffer_bytes(substream);
- period_bytes = snd_pcm_lib_period_bytes(substream);
- if (bufsize != azx_dev->bufsize ||
period_bytes != azx_dev->period_bytes ||
format_val != azx_dev->format_val ||
runtime->no_period_wakeup != azx_dev->no_period_wakeup) {
azx_dev->bufsize = bufsize;
azx_dev->period_bytes = period_bytes;
azx_dev->format_val = format_val;
azx_dev->no_period_wakeup = runtime->no_period_wakeup;
err = snd_hdac_stream_setup_periods(azx_dev);
if (err < 0)
return err;
- }
- return 0;
+} +EXPORT_SYMBOL_GPL(snd_set_hdac_stream_params);
int snd_hdac_load_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, unsigned int byte_size, struct snd_dma_buffer *bufp) -- 1.9.0
On Mon, Apr 13, 2015 at 02:04:10PM +0200, Takashi Iwai wrote:
At Sun, 12 Apr 2015 18:06:14 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
This will be used by hda controller driver to setup stream params in prepare. This function will setup the bdl and periods.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 2 ++ sound/hda/hdac_stream.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 5fc6d81..1f9f4ec 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -364,6 +364,8 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, unsigned int streams); void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, unsigned int streams); +int snd_set_hdac_stream_params(struct hdac_stream *azx_dev,
unsigned int format_val);
/*DSP loader functions */ int snd_hdac_load_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 394fd15..ce25f8f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -472,6 +472,35 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, } EXPORT_SYMBOL_GPL(snd_hdac_stream_sync);
+int snd_set_hdac_stream_params(struct hdac_stream *azx_dev,
unsigned int format_val)
I'd name it a bit more consistently, e.g. snd_hdac_stream_set_format() or snd_hdac_stream_set_params() or so.
Ah thats a bad miss on our side, will fix in next rev
+{
- unsigned int bufsize, period_bytes;
- struct snd_pcm_substream *substream = azx_dev->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
I would do a NULL check for substream to be sure.
sure...
From: Jeeja KP jeeja.kp@intel.com
Defines SoC cpu dais, DMA driver ops and implements ALSA operations for SKL.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/hda/hda_skl_pcm.c | 501 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 501 insertions(+) create mode 100644 sound/soc/hda/hda_skl_pcm.c
diff --git a/sound/soc/hda/hda_skl_pcm.c b/sound/soc/hda/hda_skl_pcm.c new file mode 100644 index 0000000..22d4465 --- /dev/null +++ b/sound/soc/hda/hda_skl_pcm.c @@ -0,0 +1,501 @@ +/* + * hda_skl_pcm.c -ASOC HDA Platform driver file implementing PCM functionality + * + * Copyright (C) 2015 Intel Corp + * Author: Jeeja KP jeeja.kp@intel.com + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include <linux/pci.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/hdaudio.h> +#include "hda_skl.h" + +static struct snd_pcm_hardware azx_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + /* No full-resume yet implemented */ + /* SNDRV_PCM_INFO_RESUME |*/ + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ + SNDRV_PCM_INFO_HAS_LINK_ATIME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = AZX_MAX_BUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = AZX_MAX_BUF_SIZE / 2, + .periods_min = 2, + .periods_max = AZX_MAX_FRAG, + .fifo_size = 0, +}; +static inline +struct hdac_stream *get_azx_dev(struct snd_pcm_substream *substream) +{ + return substream->runtime->private_data; +} + +static struct hdac_bus *get_chip_ctx(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct device *dev = rtd->cpu_dai->dev; + struct hdac_bus *chip = dev_get_drvdata(dev); + return chip; +} + +static int substream_alloc_pages(struct hdac_bus *chip, + struct snd_pcm_substream *substream, + size_t size) +{ + struct hdac_stream *azx_dev = get_azx_dev(substream); + int ret; + + azx_dev->bufsize = 0; + azx_dev->period_bytes = 0; + azx_dev->format_val = 0; + ret = snd_pcm_lib_malloc_pages(substream, size); + if (ret < 0) + return ret; + return 0; +} + +static int substream_free_pages(struct hdac_bus *chip, + struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +void soc_hda_set_pcm_constrains(struct hdac_bus *bus, + struct snd_pcm_runtime *runtime) +{ + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + /* avoid wrap-around with wall-clock */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, + 20, + 178000000); + +} + +static int soc_hda_pcm_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_bus *chip = dev_get_drvdata(dai->dev); + struct hdac_stream *azx_dev; + struct snd_pcm_runtime *runtime = substream->runtime; + struct soc_hda_dma_params *dma_params; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + azx_dev = snd_hdac_stream_assign(chip, substream); + if (azx_dev == NULL) + return -EBUSY; + + soc_hda_set_pcm_constrains(chip, runtime); + + /* disable WALLCLOCK timestamps for capture streams + * until we figure out how to handle digital inputs + */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */ + runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME; + } + + runtime->private_data = azx_dev; + + dma_params = kzalloc(sizeof(*dma_params), GFP_KERNEL); + dma_params->stream_tag = azx_dev->stream_tag; + snd_soc_dai_set_dma_data(dai, substream, (void *)dma_params); + + dev_dbg(dai->dev, "stream tag set in dma params=%d\n", + dma_params->stream_tag); + snd_pcm_set_sync(substream); + return 0; +} +static int soc_hda_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct hdac_stream *azx_dev = get_azx_dev(substream); + unsigned int format_val; + int err; + struct soc_hda_dma_params *dma_params; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + if (azx_dev->prepared) { + dev_dbg(dai->dev, "already stream is prepared - returning\n"); + return 0; + } + + dma_params = (struct soc_hda_dma_params *) + snd_soc_dai_get_dma_data(codec_dai, substream); + format_val = dma_params->format; + + dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n", + azx_dev->stream_tag, format_val); + snd_hdac_stream_reset(azx_dev); + + err = snd_set_hdac_stream_params(azx_dev, format_val); + + if (err < 0) + return err; + snd_hdac_stream_setup(azx_dev); + azx_dev->prepared = 1; + return err; +} + +static int soc_hda_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdac_bus *chip = dev_get_drvdata(dai->dev); + int ret; + struct snd_pcm_runtime *runtime = substream->runtime; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + ret = substream_alloc_pages(chip, substream, + params_buffer_bytes(params)); + + memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); + + dev_dbg(dai->dev, "format_val, rate=%d, ch=%d, format=%d\n", + runtime->rate, runtime->channels, runtime->format); + + return ret; +} + +static void soc_hda_pcm_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_stream *azx_dev = get_azx_dev(substream); + struct soc_hda_dma_params *dma_params; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + snd_hdac_stream_release(azx_dev); + dma_params = (struct soc_hda_dma_params *) + snd_soc_dai_get_dma_data(dai, substream); + kfree(dma_params); +} + +static int soc_hda_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_bus *chip = dev_get_drvdata(dai->dev); + struct hdac_stream *azx_dev = get_azx_dev(substream); + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + snd_hdac_stream_cleanup(azx_dev); + + azx_dev->prepared = 0; + + return substream_free_pages(chip, substream); +} + +static struct snd_soc_dai_ops hda_pcm_dai_ops = { + .startup = soc_hda_pcm_open, + .shutdown = soc_hda_pcm_close, + .prepare = soc_hda_pcm_prepare, + .hw_params = soc_hda_pcm_hw_params, + .hw_free = soc_hda_pcm_hw_free, +}; + +static struct snd_soc_dai_driver soc_hda_platform_dai[] = { +{ + .name = "System Pin", + .ops = &hda_pcm_dai_ops, + .playback = { + .stream_name = "System Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "System Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +}; + +static int soc_hda_platform_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dev_dbg(rtd->cpu_dai->dev, "In %s:%s\n", __func__, + dai_link->cpu_dai_name); + + runtime = substream->runtime; + snd_soc_set_runtime_hwparams(substream, &azx_pcm_hw); + return 0; +} + +static int soc_hda_platform_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct hdac_bus *chip = get_chip_ctx(substream); + struct hdac_stream *azx_dev; + struct snd_pcm_substream *s; + int rstart = 0, start, nsync = 0, sbits = 0; + unsigned long cookie; + + azx_dev = get_azx_dev(substream); + + dev_dbg(chip->dev, "In %s cmd=%d\n", __func__, cmd); + + if (!azx_dev->prepared) + return -EPIPE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + rstart = 1; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + start = 1; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + start = 0; + break; + default: + return -EINVAL; + } + + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + sbits |= 1 << azx_dev->index; + nsync++; + snd_pcm_trigger_done(s, substream); + } + + spin_lock_irqsave(&chip->reg_lock, cookie); + + /* first, set SYNC bits of corresponding streams */ + snd_hdac_stream_sync_trigger(azx_dev, start, sbits, 0); + + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + if (start) + snd_hdac_stream_start(azx_dev, rstart); + else + snd_hdac_stream_clear(azx_dev); + } + spin_unlock_irqrestore(&chip->reg_lock, cookie); + snd_hdac_stream_sync(azx_dev, start, sbits); + spin_lock_irqsave(&chip->reg_lock, cookie); + + /* reset SYNC bits */ + snd_hdac_stream_sync_trigger(azx_dev, start, sbits, 0); + + if (start) + snd_hdac_stream_timecounter_init(azx_dev, sbits); + spin_unlock_irqrestore(&chip->reg_lock, cookie); + return 0; +} + +unsigned int azx_get_position(struct hdac_stream *azx_dev, int codec_delay) +{ + struct snd_pcm_substream *substream = azx_dev->substream; + struct hdac_bus *hbus = get_chip_ctx(substream); + unsigned int pos; + int stream = substream->stream; + int delay = 0; + + /* use the position buffer as default */ + pos = snd_hdac_bus_get_pos_posbuf(hbus, azx_dev); + + if (pos >= azx_dev->bufsize) + pos = 0; + + if (substream->runtime) { + if (hbus->get_delay[stream]) + delay += hbus->get_delay[stream](hbus, azx_dev, pos); + delay += codec_delay; + substream->runtime->delay += delay; + } + + return pos; +} + +static snd_pcm_uframes_t soc_hda_platform_pcm_pointer + (struct snd_pcm_substream *substream) +{ + struct hdac_stream *azx_dev = get_azx_dev(substream); + + return bytes_to_frames(substream->runtime, + azx_get_position(azx_dev, 0)); +} + +static int soc_hda_platform_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *area) +{ + return snd_pcm_lib_default_mmap(substream, area); +} + +static u64 soc_azx_adjust_codec_delay(struct snd_pcm_substream *substream, + u64 nsec) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_dai *codec_dai = rtd->codec_dai; + u64 codec_frames, codec_nsecs; + + if (!codec_dai->driver->ops->delay) + return nsec; + + codec_frames = codec_dai->driver->ops->delay(substream, codec_dai); + codec_nsecs = div_u64(codec_frames * 1000000000LL, + substream->runtime->rate); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + return nsec + codec_nsecs; + + return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0; +} + +static int soc_hda_get_time_info(struct snd_pcm_substream *substream, + struct timespec *system_ts, struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report) +{ + struct hdac_stream *azx_dev = get_azx_dev(substream); + u64 nsec; + + if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && + (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) { + + snd_pcm_gettime(substream->runtime, system_ts); + + nsec = timecounter_read(&azx_dev->tc); + nsec = div_u64(nsec, 3); /* can be optimized */ + if (audio_tstamp_config->report_delay) + nsec = soc_azx_adjust_codec_delay(substream, nsec); + + *audio_ts = ns_to_timespec(nsec); + + audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; + audio_tstamp_report->accuracy_report = 1; /* rest of struct is valid */ + audio_tstamp_report->accuracy = 42; /* 24MHzWallClk == 42ns resolution */ + + } else + audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; + + return 0; +} + +static struct snd_pcm_ops soc_hda_platform_ops = { + .open = soc_hda_platform_open, + .ioctl = snd_pcm_lib_ioctl, + .trigger = soc_hda_platform_pcm_trigger, + .pointer = soc_hda_platform_pcm_pointer, + .get_time_info = soc_hda_get_time_info, + .mmap = soc_hda_platform_pcm_mmap, + .page = snd_pcm_sgbuf_ops_page, +}; + +static void soc_hda_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +#define MAX_PREALLOC_SIZE (32 * 1024 * 1024) + +static int soc_hda_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->cpu_dai; + struct hdac_bus *chip = dev_get_drvdata(dai->dev); + struct snd_pcm *pcm = rtd->pcm; + unsigned int size; + int retval = 0; + struct soc_hda_skl *hda = + container_of(chip, struct soc_hda_skl, chip); + + dev_dbg(chip->dev, "In %s\n", __func__); + if (dai->driver->playback.channels_min || + dai->driver->capture.channels_min) { + /* buffer pre-allocation */ + size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024; + if (size > MAX_PREALLOC_SIZE) + size = MAX_PREALLOC_SIZE; + retval = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(hda->pci), + size, MAX_PREALLOC_SIZE); + if (retval) { + dev_err(chip->dev, "dma buffer allocationf fail\n"); + return retval; + } + } + return retval; +} + +static struct snd_soc_platform_driver soc_hda_platform_drv = { + .ops = &soc_hda_platform_ops, + .pcm_new = soc_hda_pcm_new, + .pcm_free = soc_hda_pcm_free, +}; + +static const struct snd_soc_component_driver soc_hda_component = { + .name = "pcm", +}; + +int soc_hda_platform_register(struct device *dev) +{ + int ret = 0; + + ret = snd_soc_register_platform(dev, + &soc_hda_platform_drv); + if (ret) { + dev_err(dev, "soc platform registration failed\n"); + return ret; + } + ret = snd_soc_register_component(dev, &soc_hda_component, + soc_hda_platform_dai, + ARRAY_SIZE(soc_hda_platform_dai)); + if (ret) { + dev_err(dev, "soc component registration failed\n"); + snd_soc_unregister_platform(dev); + } + + dev_dbg(dev, "In%s platform registration completed\n", __func__); + return ret; + +} + +int soc_hda_platform_unregister(struct device *dev) +{ + snd_soc_unregister_component(dev); + snd_soc_unregister_platform(dev); + return 0; +}
From: Jeeja KP jeeja.kp@intel.com
This will be used by controller legacy and SKL driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdaudio.h | 1 + sound/hda/hdac_controller.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 1f9f4ec..2d62410 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -293,6 +293,7 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus); void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, void (*ack)(struct hdac_bus *, struct hdac_stream *)); +irqreturn_t snd_hdac_bus_interrupt(int irq, void *dev_id);
int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus); void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus); diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 4d4e0d6..831cc0d 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -5,6 +5,7 @@ #include <linux/kernel.h> #include <linux/delay.h> #include <linux/export.h> +#include <linux/pm_runtime.h> #include <sound/core.h> #include <sound/hdaudio.h> #include <sound/hda_registers.h> @@ -381,6 +382,29 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip); /* * interrupt handler */ +irqreturn_t snd_hdac_bus_interrupt(int irq, void *dev_id) +{ + struct hdac_bus *chip = dev_id; + u32 status; + +#ifdef CONFIG_PM + if (!pm_runtime_active(chip->dev)) + return IRQ_NONE; +#endif + + spin_lock(&chip->reg_lock); + + status = azx_readl(chip, INTSTS); + if (status == 0 || status == 0xffffffff) { + spin_unlock(&chip->reg_lock); + return IRQ_NONE; + } + spin_unlock(&chip->reg_lock); + + return IRQ_WAKE_THREAD; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_interrupt); + void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, void (*ack)(struct hdac_bus *, struct hdac_stream *))
At Sun, 12 Apr 2015 18:06:16 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
This will be used by controller legacy and SKL driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 1 + sound/hda/hdac_controller.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 1f9f4ec..2d62410 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -293,6 +293,7 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus); void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, void (*ack)(struct hdac_bus *, struct hdac_stream *)); +irqreturn_t snd_hdac_bus_interrupt(int irq, void *dev_id);
int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus); void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus); diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 4d4e0d6..831cc0d 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -5,6 +5,7 @@ #include <linux/kernel.h> #include <linux/delay.h> #include <linux/export.h> +#include <linux/pm_runtime.h> #include <sound/core.h> #include <sound/hdaudio.h> #include <sound/hda_registers.h> @@ -381,6 +382,29 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip); /*
- interrupt handler
*/ +irqreturn_t snd_hdac_bus_interrupt(int irq, void *dev_id) +{
- struct hdac_bus *chip = dev_id;
- u32 status;
+#ifdef CONFIG_PM
- if (!pm_runtime_active(chip->dev))
return IRQ_NONE;
+#endif
- spin_lock(&chip->reg_lock);
- status = azx_readl(chip, INTSTS);
- if (status == 0 || status == 0xffffffff) {
spin_unlock(&chip->reg_lock);
return IRQ_NONE;
- }
- spin_unlock(&chip->reg_lock);
- return IRQ_WAKE_THREAD;
+} +EXPORT_SYMBOL_GPL(snd_hdac_bus_interrupt);
Again, this is specific to ASoC driver, so let's keep it local.
thanks,
Takashi
On Mon, Apr 13, 2015 at 02:01:48PM +0200, Takashi Iwai wrote:
At Sun, 12 Apr 2015 18:06:16 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
This will be used by controller legacy and SKL driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 1 + sound/hda/hdac_controller.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 1f9f4ec..2d62410 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -293,6 +293,7 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus); void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, void (*ack)(struct hdac_bus *, struct hdac_stream *)); +irqreturn_t snd_hdac_bus_interrupt(int irq, void *dev_id);
int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus); void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus); diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 4d4e0d6..831cc0d 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -5,6 +5,7 @@ #include <linux/kernel.h> #include <linux/delay.h> #include <linux/export.h> +#include <linux/pm_runtime.h> #include <sound/core.h> #include <sound/hdaudio.h> #include <sound/hda_registers.h> @@ -381,6 +382,29 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip); /*
- interrupt handler
*/ +irqreturn_t snd_hdac_bus_interrupt(int irq, void *dev_id) +{
- struct hdac_bus *chip = dev_id;
- u32 status;
+#ifdef CONFIG_PM
- if (!pm_runtime_active(chip->dev))
return IRQ_NONE;
+#endif
- spin_lock(&chip->reg_lock);
- status = azx_readl(chip, INTSTS);
- if (status == 0 || status == 0xffffffff) {
spin_unlock(&chip->reg_lock);
return IRQ_NONE;
- }
- spin_unlock(&chip->reg_lock);
- return IRQ_WAKE_THREAD;
+} +EXPORT_SYMBOL_GPL(snd_hdac_bus_interrupt);
Again, this is specific to ASoC driver, so let's keep it local.
Hmmm, but most of this is based on HDA spec and not doing much wrt driver. Moving to ASoC is fine too... WIll move
At Tue, 14 Apr 2015 10:13:56 +0530, Vinod Koul wrote:
On Mon, Apr 13, 2015 at 02:01:48PM +0200, Takashi Iwai wrote:
At Sun, 12 Apr 2015 18:06:16 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
This will be used by controller legacy and SKL driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 1 + sound/hda/hdac_controller.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 1f9f4ec..2d62410 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -293,6 +293,7 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus); void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, void (*ack)(struct hdac_bus *, struct hdac_stream *)); +irqreturn_t snd_hdac_bus_interrupt(int irq, void *dev_id);
int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus); void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus); diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 4d4e0d6..831cc0d 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -5,6 +5,7 @@ #include <linux/kernel.h> #include <linux/delay.h> #include <linux/export.h> +#include <linux/pm_runtime.h> #include <sound/core.h> #include <sound/hdaudio.h> #include <sound/hda_registers.h> @@ -381,6 +382,29 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip); /*
- interrupt handler
*/ +irqreturn_t snd_hdac_bus_interrupt(int irq, void *dev_id) +{
- struct hdac_bus *chip = dev_id;
- u32 status;
+#ifdef CONFIG_PM
- if (!pm_runtime_active(chip->dev))
return IRQ_NONE;
+#endif
- spin_lock(&chip->reg_lock);
- status = azx_readl(chip, INTSTS);
- if (status == 0 || status == 0xffffffff) {
spin_unlock(&chip->reg_lock);
return IRQ_NONE;
- }
- spin_unlock(&chip->reg_lock);
- return IRQ_WAKE_THREAD;
+} +EXPORT_SYMBOL_GPL(snd_hdac_bus_interrupt);
Again, this is specific to ASoC driver, so let's keep it local.
Hmmm, but most of this is based on HDA spec and not doing much wrt driver. Moving to ASoC is fine too... WIll move
The assumption of threaded IRQ alone is driver-specific :)
Takashi
From: Jeeja KP jeeja.kp@intel.com
Add ASoC Skylake HD audio controller driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdaudio.h | 11 + sound/soc/hda/hda_skl.c | 829 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/hda/hda_skl.h | 49 +++ 3 files changed, 889 insertions(+) create mode 100644 sound/soc/hda/hda_skl.c create mode 100644 sound/soc/hda/hda_skl.h
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 2d62410..1ef0b79 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -197,6 +197,11 @@ struct hda_rb { u32 res[HDA_MAX_CODECS]; /* last read value */ };
+typedef unsigned int (*hdac_stream_get_pos_callback_t)(struct hdac_bus *, + struct hdac_stream *); +typedef int (*hdac_stream_get_delay_callback_t)(struct hdac_bus *, + struct hdac_stream *, unsigned int pos); + struct hdac_bus { struct device *dev; const struct hdac_bus_ops *ops; @@ -233,6 +238,10 @@ struct hdac_bus { struct snd_dma_buffer rb; struct snd_dma_buffer posbuf;
+ /* position adjustment callbacks */ + hdac_stream_get_pos_callback_t get_position[2]; + hdac_stream_get_delay_callback_t get_delay[2]; + /* hdac_stream linked list */ struct list_head stream_list;
@@ -330,6 +339,8 @@ struct hdac_stream {
unsigned int opened:1; unsigned int running:1; + unsigned int prepared:1; + unsigned int irq_pending:1; unsigned int no_period_wakeup:1; unsigned int locked:1;
diff --git a/sound/soc/hda/hda_skl.c b/sound/soc/hda/hda_skl.c new file mode 100644 index 0000000..ef1a7c2 --- /dev/null +++ b/sound/soc/hda/hda_skl.c @@ -0,0 +1,829 @@ +/* + * soc-hda-core.c - Implementation of primary ASoC Intel HD Audio driver + * + * Copyright (C) 2015 Intel Corp + * Author: Jeeja KP jeeja.kp@intel.com + * + * Copyright (c) 2004 Takashi Iwai tiwai@suse.de + * PeiSen Hou pshou@realtek.com.tw + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pci.h> +#include <linux/mutex.h> +#include <linux/io.h> +#include <linux/pm_runtime.h> + +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/hda_registers.h> +#include <sound/hdaudio.h> +#include "hda_skl.h" + +static char *model; +static int bdl_pos_adj = -1; +static int probe_only; +static int jackpoll_ms; +static int enable_msi = -1; + +module_param(model, charp, 0444); +MODULE_PARM_DESC(model, "Use the given board model."); +module_param(bdl_pos_adj, int, 0644); +MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); +module_param(probe_only, int, 0444); +MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); +module_param(jackpoll_ms, int, 0444); +MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)"); +module_param(enable_msi, bint, 0444); +MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); + +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("{Intel, PCH},"); +MODULE_DESCRIPTION("Intel HDA driver"); + +/* + * initialize the PCI registers + */ +/* update bits in a PCI register byte */ +static void update_pci_byte(struct pci_dev *pci, unsigned int reg, + unsigned char mask, unsigned char val) +{ + unsigned char data; + + pci_read_config_byte(pci, reg, &data); + data &= ~mask; + data |= (val & mask); + pci_write_config_byte(pci, reg, data); +} + +static void azx_init_pci(struct hdac_bus *chip) +{ + struct soc_hda_skl *hda = container_of(chip, struct soc_hda_skl, chip); + + /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44) + * TCSEL == Traffic Class Select Register, which sets PCI express QOS + * Ensuring these bits are 0 clears playback static on some HD Audio + * codecs. + * The PCI register TCSEL is defined in the Intel manuals. + */ + dev_dbg(chip->dev, "Clearing TCSEL\n"); + update_pci_byte(hda->pci, AZX_PCIREG_TCSEL, 0x07, 0); +} + +void azx_position_check(struct hdac_bus *chip, + struct hdac_stream *azx_dev); + +irqreturn_t azx_threaded_handler(int irq, void *dev_id) +{ + struct hdac_bus *chip = dev_id; + u32 status; + unsigned long cookie; + + status = azx_readl(chip, INTSTS); + spin_lock_irqsave(&chip->reg_lock, cookie); + + snd_hdac_bus_handle_stream_irq(chip, status, &azx_position_check); + + /* clear rirb int */ + status = azx_readb(chip, RIRBSTS); + if (status & RIRB_INT_MASK) { + if (status & RIRB_INT_RESPONSE) + snd_hdac_bus_update_rirb(chip); + azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); + } + + spin_unlock_irqrestore(&chip->reg_lock, cookie); + + return IRQ_HANDLED; +} + +/* initialize SD streams, use seprate streeam tag for PB and CP */ +int azx_init_stream(struct hdac_bus *chip, int num_stream, int direction) +{ + int i, tag; + int stream_tag = 0; + + /* initialize each stream (aka device) + * assign the starting bdl address to each stream (device) + * and initialize + */ + for (i = 0; i < num_stream; i++) { + struct hdac_stream *hdac_stream = + devm_kzalloc(chip->dev, sizeof(*hdac_stream), GFP_KERNEL); + if (!hdac_stream) { + dev_err(chip->dev, "kzalloc block failed"); + return -ENOMEM; + } + tag = ++stream_tag; + snd_hdac_stream_init(chip, hdac_stream, i, direction, tag); + list_add_tail(&hdac_stream->list, &chip->stream_list); + } + return 0; +} + +/* calculate runtime delay from LPIB */ +static int azx_get_delay_from_lpib(struct hdac_bus *chip, + struct hdac_stream *azx_dev, + unsigned int pos) +{ + struct snd_pcm_substream *substream = azx_dev->substream; + int stream = substream->stream; + unsigned int lpib_pos = snd_hdac_bus_get_pos_lpib(chip, azx_dev); + int delay; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + delay = pos - lpib_pos; + else + delay = lpib_pos - pos; + if (delay < 0) { + if (delay >= azx_dev->delay_negative_threshold) + delay = 0; + else + delay += azx_dev->bufsize; + } + + if (delay >= azx_dev->period_bytes) { + dev_info(chip->dev, + "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", + delay, azx_dev->period_bytes); + delay = 0; + chip->get_delay[stream] = NULL; + } + + return bytes_to_frames(substream->runtime, delay); +} + +static int azx_position_ok(struct hdac_bus *chip, struct hdac_stream *azx_dev); + +/* called from IRQ */ +void azx_position_check(struct hdac_bus *chip, struct hdac_stream *azx_dev) +{ + struct soc_hda_skl *hda = container_of(chip, struct soc_hda_skl, chip); + int ok; + + ok = azx_position_ok(chip, azx_dev); + if (ok == 1) { + snd_pcm_period_elapsed(azx_dev->substream); + azx_dev->irq_pending = 0; + } else if (ok == 0) { + /* bogus IRQ, process it later */ + azx_dev->irq_pending = 1; + schedule_work(&hda->irq_pending_work); + } +} + +/* + * Check whether the current DMA position is acceptable for updating + * periods. Returns non-zero if it's OK. + * + * Many HD-audio controllers appear pretty inaccurate about + * the update-IRQ timing. The IRQ is issued before actually the + * data is processed. So, we need to process it afterwords in a + * workqueue. + */ +static int azx_position_ok(struct hdac_bus *chip, struct hdac_stream *azx_dev) +{ + struct snd_pcm_substream *substream = azx_dev->substream; + int stream = substream->stream; + u32 wallclk; + unsigned int pos = 0; + + wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk; + if (wallclk < (azx_dev->period_wallclk * 2) / 3) + return -1; /* bogus (too early) interrupt */ + + if (chip->use_posbuf) { + /* use the position buffer as default */ + pos = snd_hdac_bus_get_pos_posbuf(chip, azx_dev); + if (!pos || pos == (u32)-1) { + dev_info(chip->dev, + "Invalid position buffer, using LPIB read method instead.\n"); + pos = snd_hdac_bus_get_pos_lpib(chip, azx_dev); + chip->get_delay[stream] = NULL; + } else { + chip->get_position[stream] = snd_hdac_bus_get_pos_posbuf; + chip->get_delay[stream] = azx_get_delay_from_lpib; + } + + } + + if (pos >= azx_dev->bufsize) + pos = 0; + + if (WARN_ONCE(!azx_dev->period_bytes, + "hda-intel: zero azx_dev->period_bytes")) + return -1; /* this shouldn't happen! */ + if (wallclk < (azx_dev->period_wallclk * 5) / 4 && + pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) + /* NG - it's below the first next period boundary */ + return chip->bdl_pos_adj ? 0 : -1; + azx_dev->start_wallclk += wallclk; + return 1; /* OK, it's fine */ +} + +/* + * The work for pending PCM period updates. + */ +static void azx_irq_pending_work(struct work_struct *work) +{ + struct soc_hda_skl *hda = container_of(work, struct soc_hda_skl, irq_pending_work); + struct hdac_bus *chip = &hda->chip; + struct hdac_stream *azx_dev; + int pending, ok; + + if (!hda->irq_pending_warned) { + dev_info(chip->dev, + "IRQ timing workaround is activated. Suggest a bigger bdl_pos_adj.\n"); + hda->irq_pending_warned = 1; + } + + for (;;) { + pending = 0; + spin_lock_irq(&chip->reg_lock); + list_for_each_entry(azx_dev, &chip->stream_list, list) { + if (!azx_dev->irq_pending || + !azx_dev->substream || + !azx_dev->running) + continue; + ok = azx_position_ok(chip, azx_dev); + if (ok > 0) { + azx_dev->irq_pending = 0; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(azx_dev->substream); + spin_lock(&chip->reg_lock); + } else if (ok < 0) { + pending = 0; /* too early */ + } else + pending++; + } + spin_unlock_irq(&chip->reg_lock); + if (!pending) + return; + msleep(1); + } +} + +/* clear irq_pending flags and assure no on-going workq */ +static void azx_clear_irq_pending(struct hdac_bus *chip) +{ + struct hdac_stream *azx_dev; + unsigned long cookie; + + spin_lock_irqsave(&chip->reg_lock, cookie); + list_for_each_entry(azx_dev, &chip->stream_list, list) + azx_dev->irq_pending = 0; + spin_unlock_irqrestore(&chip->reg_lock, cookie); +} + +static int azx_acquire_irq(struct hdac_bus *chip, int do_disconnect) +{ + struct soc_hda_skl *hda = container_of(chip, struct soc_hda_skl, chip); + + if (request_threaded_irq(hda->pci->irq, snd_hdac_bus_interrupt, + azx_threaded_handler, + hda->msi ? 0 : IRQF_SHARED, + KBUILD_MODNAME, chip)) { + dev_err(chip->dev, + "unable to grab IRQ %d, disabling device\n", + hda->pci->irq); + return -1; + } + pci_intx(hda->pci, !hda->msi); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +/* + * power management + */ +static int azx_suspend(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_bus *chip = pci_get_drvdata(pci); + struct soc_hda_skl *hda = container_of(chip, struct soc_hda_skl, chip); + + azx_clear_irq_pending(chip); + snd_hdac_bus_stop_chip(chip); + snd_hdac_bus_enter_link_reset(chip); + if (pci->irq >= 0) + free_irq(pci->irq, chip); + if (hda->msi) + pci_disable_msi(pci); + pci_disable_device(pci); + pci_save_state(pci); + pci_set_power_state(pci, PCI_D3hot); + return 0; +} + +static int azx_resume(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_bus *chip = pci_get_drvdata(pci); + struct soc_hda_skl *hda = container_of(chip, struct soc_hda_skl, chip); + + pci_set_power_state(pci, PCI_D0); + pci_restore_state(pci); + + if (pci_enable_device(pci) < 0) { + dev_err(dev, "hda-intel: pci_enable_device failed, disabling device\n"); + return -EIO; + } + pci_set_master(pci); + if (hda->msi) + if (pci_enable_msi(pci) < 0) + hda->msi = 0; + if (azx_acquire_irq(chip, 1) < 0) + return -EIO; + azx_init_pci(chip); + + snd_hdac_bus_init_chip(chip, 1); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int azx_runtime_suspend(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_bus *chip = pci_get_drvdata(pci); + + dev_dbg(chip->dev, "in %s\n", __func__); + + /* enable controller wake up event */ + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | + STATESTS_INT_MASK); + + snd_hdac_bus_stop_chip(chip); + snd_hdac_bus_enter_link_reset(chip); + azx_clear_irq_pending(chip); + return 0; +} + +static int azx_runtime_resume(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_bus *chip = pci_get_drvdata(pci); + int status; + + dev_dbg(chip->dev, "in %s\n", __func__); + + /* Read STATESTS before controller reset */ + status = azx_readw(chip, STATESTS); + + azx_init_pci(chip); + snd_hdac_bus_init_chip(chip, true); + /* disable controller Wake Up event*/ + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & + ~STATESTS_INT_MASK); + return 0; +} + +static const struct dev_pm_ops azx_pm = { + SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume) + SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, NULL) +}; +#endif /* CONFIG_PM */ + +/* + * destructor + */ +static int azx_free(struct hdac_bus *chip) +{ + struct soc_hda_skl *hda = container_of(chip, struct soc_hda_skl, chip); + struct pci_dev *pci = hda->pci; + struct hdac_stream *azx_dev; + + hda->init_failed = 1; /* to be sure */ + + if (chip->chip_init) { + azx_clear_irq_pending(chip); + list_for_each_entry(azx_dev, &chip->stream_list, list) + snd_hdac_stream_stop(azx_dev); + snd_hdac_bus_stop_chip(chip); + } + + if (pci->irq >= 0) + free_irq(pci->irq, (void *)chip); + if (hda->msi) + pci_disable_msi(hda->pci); + if (chip->remap_addr) + iounmap(chip->remap_addr); + + snd_hdac_bus_free_stream_pages(chip); + pci_release_regions(hda->pci); + pci_disable_device(hda->pci); + kfree(chip); + + return 0; +} + +static void check_msi(struct hdac_bus *chip) +{ + struct soc_hda_skl *hda = container_of(chip, struct soc_hda_skl, chip); + + if (enable_msi >= 0) { + hda->msi = !!enable_msi; + return; + } + hda->msi = 1; /* enable MSI as default */ +} + +/* check the snoop mode availability */ +static void azx_check_snoop_available(struct hdac_bus *chip) +{ + bool snoop = chip->snoop; + + if (snoop != chip->snoop) { + dev_info(chip->dev, "Force to %s mode\n", + snoop ? "snoop" : "non-snoop"); + chip->snoop = snoop; + } +} + +/*load module */ +static int azx_load_generic_mach(struct device *dev) +{ + struct platform_device *pdev; + int ret; + + pdev = platform_device_alloc("mach_hda_generic", -1); + if (!pdev) { + dev_dbg(dev, "failed to allocate hda device\n"); + return -1; + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(dev, "failed to add generic machine device\n"); + platform_device_put(pdev); + return -1; + } + + return 0; +} + +static int azx_add_codec_device(int addr, struct hdac_bus *bus) +{ + struct hdac_device *hdev = NULL; + char name[10]; + int ret; + + hdev = devm_kzalloc(bus->dev, sizeof(*hdev), GFP_KERNEL); + if (!hdev) { + dev_err(bus->dev, "Cannot allocate pdev\n"); + return -ENOMEM; + } + + snprintf(name, sizeof(name), "codec#%03x", addr); + + ret = snd_hdac_device_init(hdev, bus, name, addr); + if (ret < 0) { + dev_err(bus->dev, "device init failed for hdac device\n"); + return ret; + } + hdev->type = HDA_DEV_ASOC; + + ret = snd_hdac_device_register(hdev); + if (ret) { + dev_err(bus->dev, "failed to register hdac device\n"); + snd_hdac_device_exit(hdev); + return ret; + } + return 0; +} + +/* + * Probe the given codec address + */ +static int probe_codec(struct hdac_bus *chip, int addr) +{ + unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | + (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; + unsigned int res; + + mutex_lock(&chip->cmd_mutex); + snd_hdac_bus_send_cmd(chip, cmd); + snd_hdac_bus_get_response(chip, addr, &res); + mutex_unlock(&chip->cmd_mutex); + if (res == -1) + return -EIO; + dev_dbg(chip->dev, "codec #%d probed OK\n", addr); + return azx_add_codec_device(addr, chip); +} + +/* Codec initialization */ +int azx_codec_create(struct hdac_bus *chip, const char *model) +{ + int c, max_slots; + + max_slots = AZX_DEFAULT_CODECS; + + /* First try to probe all given codec slots */ + for (c = 0; c < max_slots; c++) { + if ((chip->codec_mask & (1 << c))) { + if (probe_codec(chip, c) < 0) { + /* Some BIOSen give you wrong codec addresses + * that don't exist + */ + dev_warn(chip->dev, + "Codec #%d probe error; disabling it...\n", c); + chip->codec_mask &= ~(1 << c); + /* More badly, accessing to a non-existing + * codec often screws up the controller chip, + * and disturbs the further communications. + * Thus if an error occurs during probing, + * better to reset the controller chip to + * get back to the sanity state. + */ + snd_hdac_bus_stop_chip(chip); + snd_hdac_bus_init_chip(chip, true); + } + } + } + return 0; +} + +/* + * constructor + */ +static int azx_create(struct pci_dev *pci, + const struct hdac_bus_ops *hda_ops, + struct soc_hda_skl **rhda) +{ + struct soc_hda_skl *hda; + struct hdac_bus *chip; + + int err; + + *rhda = NULL; + + err = pci_enable_device(pci); + if (err < 0) + return err; + + hda = devm_kzalloc(&pci->dev, sizeof(*hda), GFP_KERNEL); + if (!hda) { + dev_err(&pci->dev, "Cannot allocate chip\n"); + pci_disable_device(pci); + return -ENOMEM; + } + chip = &hda->chip; + snd_hdac_bus_init(chip, &pci->dev, hda_ops); + chip->use_posbuf = 1; + hda->pci = pci; + check_msi(chip); + INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work); + + azx_check_snoop_available(chip); + + if (bdl_pos_adj < 0) + bdl_pos_adj = 0; + chip->bdl_pos_adj = bdl_pos_adj; + + *rhda = hda; + return 0; +} + +static int azx_first_init(struct hdac_bus *chip) +{ + struct soc_hda_skl *hda = container_of(chip, struct soc_hda_skl, chip); + struct pci_dev *pci = hda->pci; + int err; + unsigned short gcap; + int capture_streams, playback_streams; + + err = pci_request_regions(pci, "ICH HD audio"); + if (err < 0) + return err; + + chip->addr = pci_resource_start(pci, 0); + chip->remap_addr = pci_ioremap_bar(pci, 0); + if (chip->remap_addr == NULL) { + dev_err(chip->dev, "ioremap error\n"); + return -ENXIO; + } + + if (hda->msi) + if (pci_enable_msi(pci) < 0) + hda->msi = 0; + + if (azx_acquire_irq(chip, 0) < 0) + return -EBUSY; + + pci_set_master(pci); + synchronize_irq(pci->irq); + + gcap = azx_readw(chip, GCAP); + dev_dbg(chip->dev, "chipset global capabilities = 0x%x\n", gcap); + + /* allow 64bit DMA address if supported by H/W */ + if (!pci_set_dma_mask(pci, DMA_BIT_MASK(64))) + pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64)); + else { + pci_set_dma_mask(pci, DMA_BIT_MASK(32)); + pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)); + } + + /* read number of streams from GCAP register instead of using + * hardcoded value + */ + capture_streams = (gcap >> 8) & 0x0f; + playback_streams = (gcap >> 12) & 0x0f; + if (!playback_streams && !capture_streams) + return -EIO; + + /* initialize streams */ + azx_init_stream(chip, capture_streams, SNDRV_PCM_STREAM_CAPTURE); + azx_init_stream(chip, playback_streams, SNDRV_PCM_STREAM_PLAYBACK); + + err = snd_hdac_bus_alloc_stream_pages(chip); + if (err < 0) + return err; + + /* initialize chip */ + azx_init_pci(chip); + + snd_hdac_bus_init_chip(chip, (probe_only & 2) == 0); + + /* codec detection */ + if (!chip->codec_mask) { + dev_err(chip->dev, "no codecs found!\n"); + return -ENODEV; + } + + return 0; +} + +/* + * HDA controller ops. + */ + +/* PCI register access. */ +static void pci_azx_writel(u32 value, u32 __iomem *addr) +{ + writel(value, addr); +} + +static u32 pci_azx_readl(u32 __iomem *addr) +{ + return readl(addr); +} + +static void pci_azx_writew(u16 value, u16 __iomem *addr) +{ + writew(value, addr); +} + +static u16 pci_azx_readw(u16 __iomem *addr) +{ + return readw(addr); +} + +static void pci_azx_writeb(u8 value, u8 __iomem *addr) +{ + writeb(value, addr); +} + +static u8 pci_azx_readb(u8 __iomem *addr) +{ + return readb(addr); +} + +/* DMA page allocation helpers. */ +static int dma_alloc_pages(struct hdac_bus *chip, + int type, + size_t size, + struct snd_dma_buffer *buf) +{ + int err; + + err = snd_dma_alloc_pages(type, + chip->dev, + size, buf); + if (err < 0) + return err; + return 0; +} + +static void dma_free_pages(struct hdac_bus *chip, struct snd_dma_buffer *buf) +{ + snd_dma_free_pages(buf); +} + +static const struct hdac_bus_ops hda_bus_ops = { + .command = snd_hdac_bus_send_cmd, + .get_response = snd_hdac_bus_get_response, + .reg_writel = pci_azx_writel, + .reg_readl = pci_azx_readl, + .reg_writew = pci_azx_writew, + .reg_readw = pci_azx_readw, + .reg_writeb = pci_azx_writeb, + .reg_readb = pci_azx_readb, + .dma_alloc_pages = dma_alloc_pages, + .dma_free_pages = dma_free_pages, +}; + +static int azx_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + struct soc_hda_skl *hda; + struct hdac_bus *chip = NULL; + int err; + + err = azx_create(pci, &hda_bus_ops, &hda); + if (err < 0) + return err; + + chip = &hda->chip; + + err = azx_first_init(chip); + if (err < 0) + goto out_free; + + /*register platfrom dai and controls */ + err = soc_hda_platform_register(chip->dev); + if (err < 0) + goto out_free; + /* create codec instances */ + err = azx_codec_create(chip, model); + if (err < 0) + goto out_unregister; + + pci_set_drvdata(hda->pci, chip); + + err = azx_load_generic_mach(chip->dev); + if (err < 0) + goto out_load_machine_fail; + + /*configure PM */ + pm_runtime_set_autosuspend_delay(chip->dev, HDA_SKL_SUSPEND_DELAY); + pm_runtime_use_autosuspend(chip->dev); + pm_runtime_put_noidle(chip->dev); + pm_runtime_allow(chip->dev); + + pci_set_drvdata(hda->pci, chip); + return 0; + +out_load_machine_fail: + snd_hdac_bus_device_unregister(chip); +out_unregister: + soc_hda_platform_unregister(chip->dev); +out_free: + hda->init_failed = 1; + azx_free(chip); + snd_hdac_bus_exit(chip); + pci_set_drvdata(hda->pci, NULL); + return err; +} + +static void azx_remove(struct pci_dev *pci) +{ + struct hdac_bus *chip = pci_get_drvdata(pci); + + if (pci_dev_run_wake(pci)) + pm_runtime_get_noresume(&pci->dev); + pci_dev_put(pci); + soc_hda_platform_unregister(&pci->dev); + snd_hdac_bus_device_unregister(chip); + azx_free(chip); + snd_hdac_bus_exit(chip); + dev_set_drvdata(&pci->dev, NULL); +} + +/* PCI IDs */ +static const struct pci_device_id azx_ids[] = { + /* Sunrise Point-LP */ + { PCI_DEVICE(0x8086, 0x9d70), 0}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, azx_ids); + +/* pci_driver definition */ +static struct pci_driver azx_driver = { + .name = KBUILD_MODNAME, + .id_table = azx_ids, + .probe = azx_probe, + .remove = azx_remove, + .driver = { + .pm = &azx_pm, + }, +}; + +module_pci_driver(azx_driver); diff --git a/sound/soc/hda/hda_skl.h b/sound/soc/hda/hda_skl.h new file mode 100644 index 0000000..f23c049 --- /dev/null +++ b/sound/soc/hda/hda_skl.h @@ -0,0 +1,49 @@ +/* + * for SKL HD Audio. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __SOUND_SOC_HDA_SKL_H +#define __SOUND_SOC_HDA_SKL_H + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/hdaudio.h> + +#define HDA_SKL_SUSPEND_DELAY 2000 + +struct soc_hda_skl { + struct hdac_bus chip; + struct pci_dev *pci; + + /* for pending irqs */ + struct work_struct irq_pending_work; + + /* extra flags */ + unsigned int irq_pending_warned:1; + + unsigned int init_failed:1; /* delayed init failed */ + + unsigned int msi:1; + /* secondary power domain for hdmi audio under vga device */ + struct dev_pm_domain hdmi_pm_domain; +}; + +/*to pass dai dma data */ +struct soc_hda_dma_params { + u32 format; + u8 stream_tag; +}; + +int soc_hda_platform_unregister(struct device *dev); +int soc_hda_platform_register(struct device *dev); +#endif /* __SOUND_SOC_HDA_SKL_H */
At Sun, 12 Apr 2015 18:06:17 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
Add ASoC Skylake HD audio controller driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 11 + sound/soc/hda/hda_skl.c | 829 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/hda/hda_skl.h | 49 +++ 3 files changed, 889 insertions(+) create mode 100644 sound/soc/hda/hda_skl.c create mode 100644 sound/soc/hda/hda_skl.h
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 2d62410..1ef0b79 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -197,6 +197,11 @@ struct hda_rb { u32 res[HDA_MAX_CODECS]; /* last read value */ };
+typedef unsigned int (*hdac_stream_get_pos_callback_t)(struct hdac_bus *,
struct hdac_stream *);
+typedef int (*hdac_stream_get_delay_callback_t)(struct hdac_bus *,
struct hdac_stream *, unsigned int pos);
struct hdac_bus { struct device *dev; const struct hdac_bus_ops *ops; @@ -233,6 +238,10 @@ struct hdac_bus { struct snd_dma_buffer rb; struct snd_dma_buffer posbuf;
- /* position adjustment callbacks */
- hdac_stream_get_pos_callback_t get_position[2];
- hdac_stream_get_delay_callback_t get_delay[2];
So, do you still need the dynamic switch of get_position and get_delay functions on SKL? These are basically for old platforms that have no position buffer support or unstable reads.
Otherwise you can use always LPIB and position buffers for position and delay calculations. That's why I didn't put them at the first place.
- /* hdac_stream linked list */ struct list_head stream_list;
@@ -330,6 +339,8 @@ struct hdac_stream {
unsigned int opened:1; unsigned int running:1;
- unsigned int prepared:1;
- unsigned int irq_pending:1;
The irq_pending is also another question. This was a workaround, and whether it's still needed for the modern chips is not sure.
thanks,
Takashi
On Mon, Apr 13, 2015 at 02:00:07PM +0200, Takashi Iwai wrote:
At Sun, 12 Apr 2015 18:06:17 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
Add ASoC Skylake HD audio controller driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdaudio.h | 11 + sound/soc/hda/hda_skl.c | 829 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/hda/hda_skl.h | 49 +++ 3 files changed, 889 insertions(+) create mode 100644 sound/soc/hda/hda_skl.c create mode 100644 sound/soc/hda/hda_skl.h
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 2d62410..1ef0b79 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -197,6 +197,11 @@ struct hda_rb { u32 res[HDA_MAX_CODECS]; /* last read value */ };
+typedef unsigned int (*hdac_stream_get_pos_callback_t)(struct hdac_bus *,
struct hdac_stream *);
+typedef int (*hdac_stream_get_delay_callback_t)(struct hdac_bus *,
struct hdac_stream *, unsigned int pos);
struct hdac_bus { struct device *dev; const struct hdac_bus_ops *ops; @@ -233,6 +238,10 @@ struct hdac_bus { struct snd_dma_buffer rb; struct snd_dma_buffer posbuf;
- /* position adjustment callbacks */
- hdac_stream_get_pos_callback_t get_position[2];
- hdac_stream_get_delay_callback_t get_delay[2];
So, do you still need the dynamic switch of get_position and get_delay functions on SKL? These are basically for old platforms that have no position buffer support or unstable reads.
Otherwise you can use always LPIB and position buffers for position and delay calculations. That's why I didn't put them at the first place.
Jeeja tested this last evening so we dont need this on SKL and will remove, and you were right :)
- /* hdac_stream linked list */ struct list_head stream_list;
@@ -330,6 +339,8 @@ struct hdac_stream {
unsigned int opened:1; unsigned int running:1;
- unsigned int prepared:1;
- unsigned int irq_pending:1;
The irq_pending is also another question. This was a workaround, and whether it's still needed for the modern chips is not sure.
nope, will drop this patch
From: Jeeja KP jeeja.kp@intel.com
Add makefile and Kconfig to enable Skylake HD audio driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/Kconfig | 3 ++- sound/soc/Makefile | 1 + sound/soc/hda/Kconfig | 29 +++++++++++++++++++++++++++++ sound/soc/hda/Makefile | 6 ++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 sound/soc/hda/Kconfig create mode 100644 sound/soc/hda/Makefile
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index dcc79aa..1baa2a4 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -14,7 +14,7 @@ menuconfig SND_SOC
If you want ASoC support, you should say Y here and also to the specific driver for your SoC platform below. - + ASoC provides power efficient ALSA support for embedded battery powered SoC based systems like PDA's, Phones and Personal Media Players.
@@ -40,6 +40,7 @@ source "sound/soc/cirrus/Kconfig" source "sound/soc/davinci/Kconfig" source "sound/soc/dwc/Kconfig" source "sound/soc/fsl/Kconfig" +source "sound/soc/hda/Kconfig" source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 5b3c8f6..3269d19 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_SND_SOC) += cirrus/ obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ +obj-$(CONFIG_SND_SOC) += hda/ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += mxs/ diff --git a/sound/soc/hda/Kconfig b/sound/soc/hda/Kconfig new file mode 100644 index 0000000..1711126 --- /dev/null +++ b/sound/soc/hda/Kconfig @@ -0,0 +1,29 @@ +menu "SKL-HD-Audio" + +config SND_SOC_HDA + tristate + select SND_HDA_CORE + +if SND_SOC_HDA + +config SND_HDA_PREALLOC_SIZE + int "Pre-allocated buffer size for HD-audio driver" + range 0 32768 + default 64 + help + Specifies the default pre-allocated buffer-size in kB for the + HD-audio driver. A larger buffer (e.g. 2048) is preferred + for systems using PulseAudio. The default 64 is chosen just + for compatibility reasons. +endif + +config SND_SOC_HDA_SKL + tristate "ASoC Intel HD Audio" + select SND_SOC_HDA + help + Say Y here to include support for ASoC Intel "High Definition + Audio" (Azalia) and its compatible devices. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-hda-skl. +endmenu diff --git a/sound/soc/hda/Makefile b/sound/soc/hda/Makefile new file mode 100644 index 0000000..7057b08 --- /dev/null +++ b/sound/soc/hda/Makefile @@ -0,0 +1,6 @@ +ccflags-y += -Werror + +snd-soc-hda-skl-objs := hda_skl.o hda_skl_pcm.o + +# common driver +obj-$(CONFIG_SND_SOC_HDA_SKL) += snd-soc-hda-skl.o
At Sun, 12 Apr 2015 18:06:18 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com
Add makefile and Kconfig to enable Skylake HD audio driver
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
sound/soc/Kconfig | 3 ++- sound/soc/Makefile | 1 + sound/soc/hda/Kconfig | 29 +++++++++++++++++++++++++++++ sound/soc/hda/Makefile | 6 ++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 sound/soc/hda/Kconfig create mode 100644 sound/soc/hda/Makefile
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index dcc79aa..1baa2a4 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -14,7 +14,7 @@ menuconfig SND_SOC
If you want ASoC support, you should say Y here and also to the specific driver for your SoC platform below.
- ASoC provides power efficient ALSA support for embedded battery powered SoC based systems like PDA's, Phones and Personal Media Players.
@@ -40,6 +40,7 @@ source "sound/soc/cirrus/Kconfig" source "sound/soc/davinci/Kconfig" source "sound/soc/dwc/Kconfig" source "sound/soc/fsl/Kconfig" +source "sound/soc/hda/Kconfig" source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 5b3c8f6..3269d19 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_SND_SOC) += cirrus/ obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ +obj-$(CONFIG_SND_SOC) += hda/ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += mxs/ diff --git a/sound/soc/hda/Kconfig b/sound/soc/hda/Kconfig new file mode 100644 index 0000000..1711126 --- /dev/null +++ b/sound/soc/hda/Kconfig @@ -0,0 +1,29 @@ +menu "SKL-HD-Audio"
+config SND_SOC_HDA
tristate
select SND_HDA_CORE
+if SND_SOC_HDA
+config SND_HDA_PREALLOC_SIZE
The symbol conflicts.
Takashi
On Mon, Apr 13, 2015 at 02:01:04PM +0200, Takashi Iwai wrote:
At Sun, 12 Apr 2015 18:06:18 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com index 0000000..1711126 --- /dev/null +++ b/sound/soc/hda/Kconfig @@ -0,0 +1,29 @@ +menu "SKL-HD-Audio"
+config SND_SOC_HDA
tristate
select SND_HDA_CORE
+if SND_SOC_HDA
+config SND_HDA_PREALLOC_SIZE
The symbol conflicts.
Oops, any guidance for this name... ASOC_HDA_PREALLOC_SIZE or SND_SOC_SND_HDA_PREALLOC_SIZE (but its too long)
Or move this up to hdac and be used by both?
At Tue, 14 Apr 2015 10:16:22 +0530, Vinod Koul wrote:
On Mon, Apr 13, 2015 at 02:01:04PM +0200, Takashi Iwai wrote:
At Sun, 12 Apr 2015 18:06:18 +0530, Subhransu S. Prusty wrote:
From: Jeeja KP jeeja.kp@intel.com index 0000000..1711126 --- /dev/null +++ b/sound/soc/hda/Kconfig @@ -0,0 +1,29 @@ +menu "SKL-HD-Audio"
+config SND_SOC_HDA
tristate
select SND_HDA_CORE
+if SND_SOC_HDA
+config SND_HDA_PREALLOC_SIZE
The symbol conflicts.
Oops, any guidance for this name... ASOC_HDA_PREALLOC_SIZE or SND_SOC_SND_HDA_PREALLOC_SIZE (but its too long)
The latter or...
Or move this up to hdac and be used by both?
... this. I don't mind both.
Takashi
participants (3)
-
Subhransu S. Prusty
-
Takashi Iwai
-
Vinod Koul