From 6fc7e93896e980d8eebf9cff46501f495a1fc361 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:24:34 -0700 Subject: log: Fix up Kconfig log level names The log level numbers in the Kconfig are not actually correct. Fix them and also add a missing space in the header-file comment. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- include/log.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/log.h b/include/log.h index d7f64710061..33e99ab7030 100644 --- a/include/log.h +++ b/include/log.h @@ -14,7 +14,7 @@ /** Log levels supported, ranging from most to least important */ enum log_level_t { - LOGL_EMERG = 0, /*U-Boot is unstable */ + LOGL_EMERG = 0, /* U-Boot is unstable */ LOGL_ALERT, /* Action must be taken immediately */ LOGL_CRIT, /* Critical conditions */ LOGL_ERR, /* Error that prevents something from working */ -- cgit v1.3.1 From f9811e8575eb42e75c615ba9b44f0481cdb78330 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:24:37 -0700 Subject: log: Allow #define LOG_DEBUG to enable logging in a file At present it is possible to '#define DEBUG' at the top of a file which causes all debug() statements in that file to become active. There is currently no equivalent with logging, but this is a useful function. Add a LOG_DEBUG define along with documentation. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- doc/README.log | 15 +++++++++++++++ include/log.h | 7 ++++++- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/doc/README.log b/doc/README.log index 75350ecd41d..19856d43da2 100644 --- a/doc/README.log +++ b/doc/README.log @@ -69,6 +69,21 @@ If CONFIG_LOG is not set, then no logging will be available. The above have SPL versions also, e.g. CONFIG_SPL_MAX_LOG_LEVEL. +Temporary logging within a single file +-------------------------------------- + +Sometimes it is useful to turn on logging just in one file. You can use this: + + #define LOG_DEBUG + +to enable building in of all logging statements in a single file. Put it at +the top of the file, before any #includes. + +To actually get U-Boot to output this you need to also set the default logging +level - e.g. set CONFIG_LOG_DEFAULT_LEVEL to 7 (LOGL_DEBUG) or more. Otherwise +debug output is suppressed and will not be generated. + + Convenience functions --------------------- diff --git a/include/log.h b/include/log.h index 33e99ab7030..7566ba7f2db 100644 --- a/include/log.h +++ b/include/log.h @@ -111,11 +111,16 @@ int _log(enum log_category_t cat, enum log_level_t level, const char *file, #endif #if CONFIG_IS_ENABLED(LOG) +#ifdef LOG_DEBUG +#define _LOG_DEBUG 1 +#else +#define _LOG_DEBUG 0 +#endif /* Emit a log record if the level is less that the maximum */ #define log(_cat, _level, _fmt, _args...) ({ \ int _l = _level; \ - if (CONFIG_IS_ENABLED(LOG) && _l <= _LOG_MAX_LEVEL) \ + if (CONFIG_IS_ENABLED(LOG) && (_l <= _LOG_MAX_LEVEL || _LOG_DEBUG)) \ _log((enum log_category_t)(_cat), _l, __FILE__, __LINE__, \ __func__, \ pr_fmt(_fmt), ##_args); \ -- cgit v1.3.1 From 11503be4480d5091fb59c563a0c357b6967c24ba Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:24:40 -0700 Subject: pci: Don't export pci_hose_config_device() This function is not used outside this file so make it static. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- drivers/pci/pci.c | 7 ++----- include/pci.h | 6 ------ 2 files changed, 2 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 221054593ef..e2195726c8a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -184,11 +184,8 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index) return -1; } -int pci_hose_config_device(struct pci_controller *hose, - pci_dev_t dev, - unsigned long io, - pci_addr_t mem, - unsigned long command) +static int pci_hose_config_device(struct pci_controller *hose, pci_dev_t dev, + ulong io, pci_addr_t mem, ulong command) { u32 bar_response; unsigned int old_command; diff --git a/include/pci.h b/include/pci.h index 041f8e37476..dd5f051989d 100644 --- a/include/pci.h +++ b/include/pci.h @@ -735,12 +735,6 @@ extern pci_dev_t pci_find_device (unsigned int vendor, unsigned int device, int extern pci_dev_t pci_find_devices (struct pci_device_id *ids, int index); pci_dev_t pci_find_class(unsigned int find_class, int index); -extern int pci_hose_config_device(struct pci_controller *hose, - pci_dev_t dev, - unsigned long io, - pci_addr_t mem, - unsigned long command); - extern int pci_hose_find_capability(struct pci_controller *hose, pci_dev_t dev, int cap); extern int pci_hose_find_cap_start(struct pci_controller *hose, pci_dev_t dev, -- cgit v1.3.1 From 7d38db55f70a30eaa64f91b35720abc5f595783f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:24:41 -0700 Subject: pci: Fix comment in struct pci_child_platdata This is platdata, not private data, so the comment is currently incorrect. Fix it to avoid confusion. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- include/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/pci.h b/include/pci.h index dd5f051989d..936cfe975cb 100644 --- a/include/pci.h +++ b/include/pci.h @@ -822,7 +822,7 @@ struct udevice; * * Every device on a PCI bus has this per-child data. * - * It can be accessed using dev_get_parent_priv(dev) if dev->parent is a + * It can be accessed using dev_get_parent_platdata(dev) if dev->parent is a * PCI bus (i.e. UCLASS_PCI) * * @devfn: Encoded device and function index - see PCI_DEVFN() -- cgit v1.3.1 From 7d8fe15bb80e082c7e532f26b88b58ff64ba075a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:24:44 -0700 Subject: pci: Add IDs for Intel high-definition audio Add a few IDs for common HDA blocks and the ADSP used on samus. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- include/pci_ids.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/pci_ids.h b/include/pci_ids.h index fdda679cc05..bd59578ccb9 100644 --- a/include/pci_ids.h +++ b/include/pci_ids.h @@ -41,6 +41,7 @@ #define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 #define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 #define PCI_CLASS_MULTIMEDIA_PHONE 0x0402 +#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 #define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 #define PCI_BASE_CLASS_MEMORY 0x05 @@ -1363,6 +1364,7 @@ #define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 #define PCI_DEVICE_ID_CREATIVE_20K1 0x0005 #define PCI_DEVICE_ID_CREATIVE_20K2 0x000b +#define PCI_DEVICE_ID_CREATIVE_CA01322 0x0011 #define PCI_SUBDEVICE_ID_CREATIVE_SB0760 0x0024 #define PCI_SUBDEVICE_ID_CREATIVE_SB08801 0x0041 #define PCI_SUBDEVICE_ID_CREATIVE_SB08802 0x0042 @@ -2827,6 +2829,7 @@ #define PCI_DEVICE_ID_INTEL_ICH7_19 0x27dd #define PCI_DEVICE_ID_INTEL_ICH7_20 0x27de #define PCI_DEVICE_ID_INTEL_ICH7_21 0x27df +#define PCI_DEVICE_ID_INTEL_COUGARPOINT_HDMI 0x2806 #define PCI_DEVICE_ID_INTEL_ICH8_0 0x2810 #define PCI_DEVICE_ID_INTEL_ICH8_1 0x2811 #define PCI_DEVICE_ID_INTEL_ICH8_2 0x2812 @@ -3025,6 +3028,8 @@ #define PCI_DEVICE_ID_INTEL_LYNXPOINT_AHCI 0x9c03 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LPC 0x9c45 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_AHCI 0x9c83 +#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_HDA 0x9ca0 +#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_ADSP 0x9cb6 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LPC 0x9cc3 #define PCI_DEVICE_ID_INTEL_S21152BB 0xb152 -- cgit v1.3.1 From 1260f8c0efe126cf952c1bc19d975af34908a79d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:24:51 -0700 Subject: pch: Add ioctl support At present the PCH has 4 operations and these are reasonably widely used in the drivers. But sometimes we want to add rarely used operations, and each of these currently adds to the size of the PCH operations table. Add an ioctl() method which can be easily expanded without any more impact on the operations table. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- drivers/pch/pch-uclass.c | 10 ++++++++++ drivers/pch/sandbox_pch.c | 17 +++++++++++++++++ include/pch.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++- test/dm/pch.c | 19 +++++++++++++++++++ 4 files changed, 93 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/pch/pch-uclass.c b/drivers/pch/pch-uclass.c index 831b283d7b4..caf8b72803c 100644 --- a/drivers/pch/pch-uclass.c +++ b/drivers/pch/pch-uclass.c @@ -51,6 +51,16 @@ int pch_get_io_base(struct udevice *dev, u32 *iobasep) return ops->get_io_base(dev, iobasep); } +int pch_ioctl(struct udevice *dev, ulong req, void *data, int size) +{ + struct pch_ops *ops = pch_get_ops(dev); + + if (!ops->ioctl) + return -ENOSYS; + + return ops->ioctl(dev, req, data, size); +} + UCLASS_DRIVER(pch) = { .id = UCLASS_PCH, .name = "pch", diff --git a/drivers/pch/sandbox_pch.c b/drivers/pch/sandbox_pch.c index 81f8156c2e0..76f552527e3 100644 --- a/drivers/pch/sandbox_pch.c +++ b/drivers/pch/sandbox_pch.c @@ -48,11 +48,28 @@ static int sandbox_pch_get_io_base(struct udevice *dev, u32 *iobasep) return 0; } +int sandbox_pch_ioctl(struct udevice *dev, enum pch_req_t req, void *data, + int size) +{ + switch (req) { + case PCH_REQ_TEST1: + return -ENOSYS; + case PCH_REQ_TEST2: + return *(char *)data; + case PCH_REQ_TEST3: + *(char *)data = 'x'; + return 1; + default: + return -ENOSYS; + } +} + static const struct pch_ops sandbox_pch_ops = { .get_spi_base = sandbox_pch_get_spi_base, .set_spi_protect = sandbox_pch_set_spi_protect, .get_gpio_base = sandbox_pch_get_gpio_base, .get_io_base = sandbox_pch_get_io_base, + .ioctl = sandbox_pch_ioctl, }; static const struct udevice_id sandbox_pch_ids[] = { diff --git a/include/pch.h b/include/pch.h index 73994b8343f..b8b62d74acb 100644 --- a/include/pch.h +++ b/include/pch.h @@ -11,7 +11,20 @@ #define BIOS_CTRL_BIOSWE BIT(0) -/* Operations for the Platform Controller Hub */ +/* All the supported PCH ioctls */ +enum pch_req_t { + PCH_REQ_TEST1, /* Test requests for sandbox driver */ + PCH_REQ_TEST2, + PCH_REQ_TEST3, + + PCH_REQ_COUNT, /* Number of ioctrls supported */ +}; + +/** + * struct pch_ops - Operations for the Platform Controller Hub + * + * Consider using ioctl() to add rarely used or driver-specific operations. + */ struct pch_ops { /** * get_spi_base() - get the address of SPI base @@ -49,6 +62,23 @@ struct pch_ops { * @return 0 if OK, -ve on error (e.g. there is no IO base) */ int (*get_io_base)(struct udevice *dev, u32 *iobasep); + + /** + * ioctl() - perform misc read/write operations + * + * This is a catch-all operation intended to avoid adding lots of + * methods to this uclass, of which few are commonly used. Uncommon + * operations that pertain only to a few devices in this uclass should + * use this method instead of adding new methods. + * + * @dev: PCH device to check + * @req: PCH request ID + * @data: Input/output data + * @size: Size of input data (and maximum size of output data) + * @return size of output data on sucesss, -ve on error + */ + int (*ioctl)(struct udevice *dev, enum pch_req_t req, void *data, + int size); }; #define pch_get_ops(dev) ((struct pch_ops *)(dev)->driver->ops) @@ -90,4 +120,20 @@ int pch_get_gpio_base(struct udevice *dev, u32 *gbasep); */ int pch_get_io_base(struct udevice *dev, u32 *iobasep); +/** + * pch_ioctl() - perform misc read/write operations + * + * This is a catch-all operation intended to avoid adding lots of + * methods to this uclass, of which few are commonly used. Uncommon + * operations that pertain only to a few devices in this uclass should + * use this method instead of adding new methods. + * + * @dev: PCH device to check + * @req: PCH request ID + * @data: Input/output data + * @size: Size of input data (and maximum size of output data) + * @return size of output data on sucesss, -ve on error + */ +int pch_ioctl(struct udevice *dev, ulong req, void *data, int size); + #endif diff --git a/test/dm/pch.c b/test/dm/pch.c index f184445342b..54e33d187b6 100644 --- a/test/dm/pch.c +++ b/test/dm/pch.c @@ -34,3 +34,22 @@ static int dm_test_pch_base(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_pch_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test sandbox PCH ioctl */ +static int dm_test_pch_ioctl(struct unit_test_state *uts) +{ + struct udevice *dev; + char data; + + ut_assertok(uclass_first_device_err(UCLASS_PCH, &dev)); + + ut_asserteq(-ENOSYS, pch_ioctl(dev, PCH_REQ_TEST1, NULL, 0)); + + ut_asserteq('a', pch_ioctl(dev, PCH_REQ_TEST2, "a", 1)); + + ut_asserteq(1, pch_ioctl(dev, PCH_REQ_TEST3, &data, 1)); + ut_asserteq('x', data); + + return 0; +} +DM_TEST(dm_test_pch_ioctl, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From 67b0cda76a19c5fc8a0019cfdc4af9006bfad8d5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:24:52 -0700 Subject: x86: ivybridge: Add a way to get the HDA config setting Add a way check to whether HD audio is enabled. Use ioctl() to avoid adding too many unusual operations to PCH. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- arch/x86/cpu/ivybridge/bd82x6x.c | 27 +++++++++++++++++++++++++-- include/pch.h | 3 +++ 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/cpu/ivybridge/bd82x6x.c b/arch/x86/cpu/ivybridge/bd82x6x.c index a78bb025440..ed9bce64168 100644 --- a/arch/x86/cpu/ivybridge/bd82x6x.c +++ b/arch/x86/cpu/ivybridge/bd82x6x.c @@ -20,8 +20,12 @@ DECLARE_GLOBAL_DATA_PTR; -#define GPIO_BASE 0x48 -#define BIOS_CTRL 0xdc +#define GPIO_BASE 0x48 +#define BIOS_CTRL 0xdc + +#define RCBA_AUDIO_CONFIG 0x2030 +#define RCBA_AUDIO_CONFIG_HDA BIT(31) +#define RCBA_AUDIO_CONFIG_MASK 0xfe #ifndef CONFIG_HAVE_FSP static int pch_revision_id = -1; @@ -212,10 +216,29 @@ static int bd82x6x_get_gpio_base(struct udevice *dev, u32 *gbasep) return 0; } +static int bd82x6x_ioctl(struct udevice *dev, enum pch_req_t req, void *data, + int size) +{ + u32 rcba, val; + + switch (req) { + case PCH_REQ_HDA_CONFIG: + dm_pci_read_config32(dev, PCH_RCBA, &rcba); + val = readl(rcba + RCBA_AUDIO_CONFIG); + if (!(val & RCBA_AUDIO_CONFIG_HDA)) + return -ENOENT; + + return val & RCBA_AUDIO_CONFIG_MASK; + default: + return -ENOSYS; + } +} + static const struct pch_ops bd82x6x_pch_ops = { .get_spi_base = bd82x6x_pch_get_spi_base, .set_spi_protect = bd82x6x_set_spi_protect, .get_gpio_base = bd82x6x_get_gpio_base, + .ioctl = bd82x6x_ioctl, }; static const struct udevice_id bd82x6x_ids[] = { diff --git a/include/pch.h b/include/pch.h index b8b62d74acb..046a5fde3ab 100644 --- a/include/pch.h +++ b/include/pch.h @@ -13,6 +13,9 @@ /* All the supported PCH ioctls */ enum pch_req_t { + /* Returns HDA config info if Azalia V1CTL enabled, -ENOENT if not */ + PCH_REQ_HDA_CONFIG, + PCH_REQ_TEST1, /* Test requests for sandbox driver */ PCH_REQ_TEST2, PCH_REQ_TEST3, -- cgit v1.3.1 From e65f9ef9f21058a7e4f54e11da1af49a8c1b0579 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:24:53 -0700 Subject: sound: Mark sound_setup() as optional This method in the sound API is optional since some drivers can do this when probing or as part of SoC init. Mark it as such. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- drivers/sound/sound-uclass.c | 2 +- include/sound.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/sound/sound-uclass.c b/drivers/sound/sound-uclass.c index 2b836268896..067660623b2 100644 --- a/drivers/sound/sound-uclass.c +++ b/drivers/sound/sound-uclass.c @@ -16,7 +16,7 @@ int sound_setup(struct udevice *dev) struct sound_ops *ops = sound_get_ops(dev); if (!ops->setup) - return -ENOSYS; + return 0; return ops->setup(dev); } diff --git a/include/sound.h b/include/sound.h index b7959cc2607..7d528c479ef 100644 --- a/include/sound.h +++ b/include/sound.h @@ -54,7 +54,7 @@ void sound_create_square_wave(uint sample_rate, unsigned short *data, int size, /* Operations for sound */ struct sound_ops { /** - * setup() - Set up to play a sound + * setup() - Set up to play a sound (optional) */ int (*setup)(struct udevice *dev); -- cgit v1.3.1 From 2850266965ade165f913a66f679a0449faf21180 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:24:54 -0700 Subject: sound: Add uclass operations for beeping Some audio codecs such as Intel HDA do not need to use digital data to play sounds, but instead have a way to emit beeps. Add this interface as an option. If the beep interface is not supported, then the sound uclass falls back to the I2S interface. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- arch/sandbox/include/asm/test.h | 16 ++++++++++++++ drivers/sound/sandbox.c | 46 ++++++++++++++++++++++++++++++++++++++--- drivers/sound/sound-uclass.c | 34 +++++++++++++++++++++++++++++- include/sound.h | 44 +++++++++++++++++++++++++++++++++++++++ test/dm/sound.c | 21 +++++++++++++++++++ 5 files changed, 157 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index ce00ba51e47..fc52f47f821 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -161,6 +161,22 @@ int sandbox_get_setup_called(struct udevice *dev); */ int sandbox_get_sound_sum(struct udevice *dev); +/** + * sandbox_set_allow_beep() - Set whether the 'beep' interface is supported + * + * @dev: Device to update + * @allow: true to allow the start_beep() method, false to disallow it + */ +void sandbox_set_allow_beep(struct udevice *dev, bool allow); + +/** + * sandbox_get_beep_frequency() - Get the frequency of the current beep + * + * @dev: Device to check + * @return frequency of beep, if there is an active beep, else 0 + */ +int sandbox_get_beep_frequency(struct udevice *dev); + /** * sandbox_get_pch_spi_protect() - Get the PCI SPI protection status * diff --git a/drivers/sound/sandbox.c b/drivers/sound/sandbox.c index b0b07f3239b..600523160f0 100644 --- a/drivers/sound/sandbox.c +++ b/drivers/sound/sandbox.c @@ -24,7 +24,9 @@ struct sandbox_i2s_priv { struct sandbox_sound_priv { int setup_called; - int sum; /* Use to sum the provided audio data */ + int sum; /* Use to sum the provided audio data */ + bool allow_beep; /* true to allow the start_beep() interface */ + int frequency_hz; /* Beep frequency if active, else 0 */ }; void sandbox_get_codec_params(struct udevice *dev, int *interfacep, int *ratep, @@ -61,6 +63,20 @@ int sandbox_get_sound_sum(struct udevice *dev) return priv->sum; } +void sandbox_set_allow_beep(struct udevice *dev, bool allow) +{ + struct sandbox_sound_priv *priv = dev_get_priv(dev); + + priv->allow_beep = allow; +} + +int sandbox_get_beep_frequency(struct udevice *dev) +{ + struct sandbox_sound_priv *priv = dev_get_priv(dev); + + return priv->frequency_hz; +} + static int sandbox_codec_set_params(struct udevice *dev, int interface, int rate, int mclk_freq, int bits_per_sample, uint channels) @@ -128,6 +144,28 @@ static int sandbox_sound_play(struct udevice *dev, void *data, uint data_size) return i2s_tx_data(uc_priv->i2s, data, data_size); } +int sandbox_sound_start_beep(struct udevice *dev, int frequency_hz) +{ + struct sandbox_sound_priv *priv = dev_get_priv(dev); + + if (!priv->allow_beep) + return -ENOSYS; + priv->frequency_hz = frequency_hz; + + return 0; +} + +int sandbox_sound_stop_beep(struct udevice *dev) +{ + struct sandbox_sound_priv *priv = dev_get_priv(dev); + + if (!priv->allow_beep) + return -ENOSYS; + priv->frequency_hz = 0; + + return 0; +} + static int sandbox_sound_probe(struct udevice *dev) { return sound_find_codec_i2s(dev); @@ -169,8 +207,10 @@ U_BOOT_DRIVER(sandbox_i2s) = { }; static const struct sound_ops sandbox_sound_ops = { - .setup = sandbox_sound_setup, - .play = sandbox_sound_play, + .setup = sandbox_sound_setup, + .play = sandbox_sound_play, + .start_beep = sandbox_sound_start_beep, + .stop_beep = sandbox_sound_stop_beep, }; static const struct udevice_id sandbox_sound_ids[] = { diff --git a/drivers/sound/sound-uclass.c b/drivers/sound/sound-uclass.c index 067660623b2..d49f29bcd5b 100644 --- a/drivers/sound/sound-uclass.c +++ b/drivers/sound/sound-uclass.c @@ -31,10 +31,30 @@ int sound_play(struct udevice *dev, void *data, uint data_size) return ops->play(dev, data, data_size); } +int sound_start_beep(struct udevice *dev, int frequency_hz) +{ + struct sound_ops *ops = sound_get_ops(dev); + + if (!ops->start_beep) + return -ENOSYS; + + return ops->start_beep(dev, frequency_hz); +} + +int sound_stop_beep(struct udevice *dev) +{ + struct sound_ops *ops = sound_get_ops(dev); + + if (!ops->stop_beep) + return -ENOSYS; + + return ops->stop_beep(dev); +} + int sound_beep(struct udevice *dev, int msecs, int frequency_hz) { struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); - struct i2s_uc_priv *i2s_uc_priv = dev_get_uclass_priv(uc_priv->i2s); + struct i2s_uc_priv *i2s_uc_priv; unsigned short *data; uint data_size; int ret; @@ -43,7 +63,19 @@ int sound_beep(struct udevice *dev, int msecs, int frequency_hz) if (ret && ret != -EALREADY) return ret; + /* Try using the beep interface if available */ + ret = sound_start_beep(dev, frequency_hz); + if (ret != -ENOSYS) { + if (ret) + return ret; + mdelay(msecs); + ret = sound_stop_beep(dev); + + return ret; + } + /* Buffer length computation */ + i2s_uc_priv = dev_get_uclass_priv(uc_priv->i2s); data_size = i2s_uc_priv->samplingrate * i2s_uc_priv->channels; data_size *= (i2s_uc_priv->bitspersample / SOUND_BITS_IN_BYTE); data = malloc(data_size); diff --git a/include/sound.h b/include/sound.h index 7d528c479ef..47de9fa3ed3 100644 --- a/include/sound.h +++ b/include/sound.h @@ -67,6 +67,28 @@ struct sound_ops { * @return 0 if OK, -ve on error */ int (*play)(struct udevice *dev, void *data, uint data_size); + + /** + * start_beep() - Start beeping (optional) + * + * This tells the sound hardware to start a beep. It will continue until + * stopped by sound_stop_beep(). + * + * @dev: Sound device + * @frequency_hz: Beep frequency in hertz + * @return if OK, -ENOSYS if not supported, -ve on error + */ + int (*start_beep)(struct udevice *dev, int frequency_hz); + + /** + * stop_beep() - Stop beeping (optional) + * + * This tells the sound hardware to stop a previously started beep. + * + * @dev: Sound device + * @return if OK, -ve on error + */ + int (*stop_beep)(struct udevice *dev); }; #define sound_get_ops(dev) ((struct sound_ops *)(dev)->driver->ops) @@ -86,6 +108,28 @@ int sound_setup(struct udevice *dev); */ int sound_beep(struct udevice *dev, int msecs, int frequency_hz); +/** + * sound_start_beep() - Start beeping + * + * This tells the sound hardware to start a beep. It will continue until stopped + * by sound_stop_beep(). + * + * @dev: Sound device + * @frequency_hz: Beep frequency in hertz + * @return if OK, -ve on error + */ +int sound_start_beep(struct udevice *dev, int frequency_hz); + +/** + * sound_stop_beep() - Stop beeping + * + * This tells the sound hardware to stop a previously started beep. + * + * @dev: Sound device + * @return if OK, -ve on error + */ +int sound_stop_beep(struct udevice *dev); + /** * sound_find_codec_i2s() - Called by sound drivers to locate codec and i2s * diff --git a/test/dm/sound.c b/test/dm/sound.c index 7d0b36e7a56..3767abbd1c7 100644 --- a/test/dm/sound.c +++ b/test/dm/sound.c @@ -32,3 +32,24 @@ static int dm_test_sound(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_sound, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test of the 'start beep' operations */ +static int dm_test_sound_beep(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* check probe success */ + ut_assertok(uclass_first_device_err(UCLASS_SOUND, &dev)); + ut_asserteq(-ENOSYS, sound_start_beep(dev, 100)); + ut_asserteq(0, sandbox_get_beep_frequency(dev)); + + sandbox_set_allow_beep(dev, true); + ut_asserteq(0, sound_start_beep(dev, 100)); + ut_asserteq(100, sandbox_get_beep_frequency(dev)); + + ut_asserteq(0, sound_stop_beep(dev)); + ut_asserteq(0, sandbox_get_beep_frequency(dev)); + + return 0; +} +DM_TEST(dm_test_sound_beep, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From 2ca471379b471dc0d31459974d7cc4b54c824956 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:24:55 -0700 Subject: sound: Add support for Intel HDA The Intel High-definition Audio is a newer-generation audio system which provides for transfer of a large number of audio stream, each containing up to 16 channels. Add support for HDA as a library which can be used by other drivers. U-Boot currently uses only two channels (stereo). Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- doc/device-tree-bindings/sound/intel-hda.txt | 25 ++ drivers/sound/Kconfig | 9 + drivers/sound/Makefile | 1 + drivers/sound/hda_codec.c | 556 +++++++++++++++++++++++++++ include/dt-bindings/sound/azalia.h | 44 +++ include/hda_codec.h | 103 +++++ 6 files changed, 738 insertions(+) create mode 100644 doc/device-tree-bindings/sound/intel-hda.txt create mode 100644 drivers/sound/hda_codec.c create mode 100644 include/dt-bindings/sound/azalia.h create mode 100644 include/hda_codec.h (limited to 'include') diff --git a/doc/device-tree-bindings/sound/intel-hda.txt b/doc/device-tree-bindings/sound/intel-hda.txt new file mode 100644 index 00000000000..fb2ce550063 --- /dev/null +++ b/doc/device-tree-bindings/sound/intel-hda.txt @@ -0,0 +1,25 @@ +* Intel High-definition Audio + +Configuration is set using 'verbs' which are blocks of 16 bytes of data each +with a different purpose, a little like a simple instruction set. + +Top-level node +-------------- + +Required properties: +- compatible: "intel,hd-audio" +- beep-verbs: list of verbs to send for a beep + +Optional properties +- intel,beep-nid: Node ID to use for beep (will be detected if not provided) + +Required subnodes: +- codecs: Contains a list of codec nodes + + +* Codec nodes + +Required properties: +- vendor-id: 16-bit vendor ID for audio codec +- device-id: 16-bit device ID for audio codec +- verbs: List of verbs, each 4 cells in length diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig index 40f4f7598aa..0dca6ffe88c 100644 --- a/drivers/sound/Kconfig +++ b/drivers/sound/Kconfig @@ -40,6 +40,15 @@ config I2S_SAMSUNG option provides an implementation for sound_init() and sound_play(). +config SOUND_INTEL_HDA + bool "Intel HDA audio codec" + depends on SOUND + help + Most Intel chips have an HDA (High-definition audio) codec which can + be used by U-Boot to play simple beeps. This is also sometimes called + Azalia which was the development code-name. It requires setup + information in the device tree (see intel-hda.txt). + config SOUND_MAX98088 bool "Support Maxim max98088 audio codec" depends on I2S diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 170e06ad538..2e21a0d0674 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_SOUND_WM8994) += wm8994.o obj-$(CONFIG_SOUND_MAX98088) += max98088.o maxim_codec.o obj-$(CONFIG_SOUND_MAX98090) += max98090.o maxim_codec.o obj-$(CONFIG_SOUND_MAX98095) += max98095.o maxim_codec.o +obj-$(CONFIG_SOUND_INTEL_HDA) += hda_codec.o diff --git a/drivers/sound/hda_codec.c b/drivers/sound/hda_codec.c new file mode 100644 index 00000000000..572ef693cc0 --- /dev/null +++ b/drivers/sound/hda_codec.c @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implementation of per-board codec beeping + * Copyright (c) 2011 The Chromium OS Authors. + * Copyright 2018 Google LLC + */ + +#define LOG_CATEGORY UCLASS_SOUND + +#include +#include +#include +#include +#include +#include +#include + +/** + * struct hda_regs - HDA registers + * + * https://wiki.osdev.org/Intel_High_Definition_Audio + * https://www.intel.com/content/www/us/en/standards/high-definition-audio-specification.html + */ +struct hda_regs { + u16 gcap; + u8 vmin; + u8 vmaj; + u16 outpay; + u16 inpay; + u32 gctl; + u16 wakeen; + u16 statests; + u8 reserved[0x50]; + u32 cmd; /* 0x60 */ + u32 resp; + u32 icii; +}; + +enum { + HDA_ICII_BUSY = BIT(0), + HDA_ICII_VALID = BIT(1), + + /* Common node IDs */ + HDA_ROOT_NODE = 0x00, + + /* HDA verbs fields */ + HDA_VERB_NID_S = 20, + HDA_VERB_VERB_S = 8, + HDA_VERB_PARAM_S = 0, + + HDA_VERB_GET_PARAMS = 0xf00, + HDA_VERB_SET_BEEP = 0x70a, + + /* GET_PARAMS parameter IDs */ + GET_PARAMS_NODE_COUNT = 0x04, + GET_PARAMS_AUDIO_GROUP_CAPS = 0x08, + GET_PARAMS_AUDIO_WIDGET_CAPS = 0x09, + + /* Sub-node fields */ + NUM_SUB_NODES_S = 0, + NUM_SUB_NODES_M = 0xff << NUM_SUB_NODES_S, + FIRST_SUB_NODE_S = 16, + FIRST_SUB_NODE_M = 0xff << FIRST_SUB_NODE_S, + + /* Get Audio Function Group Capabilities fields */ + AUDIO_GROUP_CAPS_BEEP_GEN = 0x10000, + + /* Get Audio Widget Capabilities fields */ + AUDIO_WIDGET_TYPE_BEEP = 0x7, + AUDIO_WIDGET_TYPE_S = 20, + AUDIO_WIDGET_TYPE_M = 0xf << AUDIO_WIDGET_TYPE_S, + + BEEP_FREQ_BASE = 12000, +}; + +static inline uint hda_verb(uint nid, uint verb, uint param) +{ + return nid << HDA_VERB_NID_S | verb << HDA_VERB_VERB_S | + param << HDA_VERB_PARAM_S; +} + +int hda_wait_for_ready(struct hda_regs *regs) +{ + int timeout = 1000; /* Use a 1msec timeout */ + + while (timeout--) { + u32 reg32 = readl(®s->icii); + + if (!(reg32 & HDA_ICII_BUSY)) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int wait_for_response(struct hda_regs *regs, uint *response) +{ + int timeout = 1000; + u32 reg32; + + /* Send the verb to the codec */ + setbits_le32(®s->icii, HDA_ICII_BUSY | HDA_ICII_VALID); + + /* Use a 1msec timeout */ + while (timeout--) { + reg32 = readl(®s->icii); + if ((reg32 & (HDA_ICII_VALID | HDA_ICII_BUSY)) == + HDA_ICII_VALID) { + if (response) + *response = readl(®s->resp); + return 0; + } + udelay(1); + } + + return -ETIMEDOUT; +} + +int hda_wait_for_valid(struct hda_regs *regs) +{ + return wait_for_response(regs, NULL); +} + +static int set_bits(void *port, u32 mask, u32 val) +{ + u32 reg32; + int count; + + /* Write (val & mask) to port */ + clrsetbits_le32(port, mask, val); + + /* Wait for readback of register to match what was just written to it */ + count = 50; + do { + /* Wait 1ms based on BKDG wait time */ + mdelay(1); + reg32 = readl(port) & mask; + } while (reg32 != val && --count); + + /* Timeout occurred */ + if (!count) + return -ETIMEDOUT; + + return 0; +} + +int hda_codec_detect(struct hda_regs *regs) +{ + uint reg8; + + /* Set Bit 0 to 1 to exit reset state (BAR + 0x8)[0] */ + if (set_bits(®s->gctl, 1, 1)) + goto no_codec; + + /* Write back the value once reset bit is set */ + writew(readw(®s->gcap), ®s->gcap); + + /* Read in Codec location */ + reg8 = readb(®s->statests) & 0xf; + if (!reg8) + goto no_codec; + + return reg8; + +no_codec: + /* Codec Not found - put HDA back in reset */ + set_bits(®s->gctl, 1, 0); + log_debug("No codec\n"); + + return 0; +} + +static int find_verb_data(struct udevice *dev, uint id, ofnode *nodep) +{ + ofnode parent = dev_read_subnode(dev, "codecs"); + ofnode node; + u32 vendor_id, device_id; + + ofnode_for_each_subnode(node, parent) { + if (ofnode_read_u32(node, "vendor-id", &vendor_id) || + ofnode_read_u32(node, "device-id", &device_id)) { + log_debug("Cannot get IDs for '%s'\n", + ofnode_get_name(node)); + return -EINVAL; + } + if (id != (vendor_id << 16 | device_id)) { + log_debug("Skip codec node '%s' for %08x\n", + ofnode_get_name(node), id); + continue; + } + + log_debug("Found codec node '%s' for %08x\n", + ofnode_get_name(node), id); + *nodep = node; + return 0; + } + + return -ENOENT; +} + +static int send_verbs(ofnode node, const char *prop_name, struct hda_regs *regs) +{ + int ret, verb_size, i; + const u32 *verb; + + verb = ofnode_get_property(node, prop_name, &verb_size); + if (verb_size < 0) { + log_debug("No verb data\n"); + return -EINVAL; + } + log_debug("verb_size: %d\n", verb_size); + + for (i = 0; i < verb_size / sizeof(*verb); i++) { + ret = hda_wait_for_ready(regs); + if (ret) { + log_debug(" codec ready timeout\n"); + return ret; + } + + writel(fdt32_to_cpu(verb[i]), ®s->cmd); + + ret = hda_wait_for_valid(regs); + if (ret) { + log_debug(" codec valid timeout\n"); + return ret; + } + } + + return 0; +} + +static int codec_init(struct udevice *dev, struct hda_regs *regs, uint addr) +{ + ofnode node; + uint id; + int ret; + + log_debug("Initializing codec #%d\n", addr); + ret = hda_wait_for_ready(regs); + if (ret) { + log_debug(" codec not ready\n"); + return ret; + } + + /* Read the codec's vendor ID */ + writel(addr << AZALIA_CODEC_SHIFT | + AZALIA_OPCODE_READ_PARAM << AZALIA_VERB_SHIFT | + AZALIA_PARAM_VENDOR_ID, ®s->cmd); + ret = hda_wait_for_valid(regs); + if (ret) { + log_debug(" codec not valid\n"); + return ret; + } + + id = readl(®s->resp); + log_debug("codec vid/did: %08x\n", id); + ret = find_verb_data(dev, id, &node); + if (ret) { + log_debug("No verb (err=%d)\n", ret); + return ret; + } + ret = send_verbs(node, "verbs", regs); + if (ret) { + log_debug("failed to send verbs (err=%d)\n", ret); + return ret; + } + log_debug("verb loaded\n"); + + return 0; +} + +int hda_codecs_init(struct udevice *dev, struct hda_regs *regs, u32 codec_mask) +{ + int ret; + int i; + + for (i = 3; i >= 0; i--) { + if (codec_mask & (1 << i)) { + ret = codec_init(dev, regs, i); + if (ret) + return ret; + } + } + + ret = send_verbs(dev_ofnode(dev), "beep-verbs", regs); + if (ret) { + log_debug("failed to send beep verbs (err=%d)\n", ret); + return ret; + } + log_debug("beep verbs loaded\n"); + + return 0; +} + +/** + * exec_verb() - Write a verb to the codec + * + * @regs: HDA registers + * @val: Command to write + * @response: Set to response from codec + * @return 0 if OK, -ve on error + */ +static int exec_verb(struct hda_regs *regs, uint val, uint *response) +{ + int ret; + + ret = hda_wait_for_ready(regs); + if (ret) + return ret; + + writel(val, ®s->cmd); + + return wait_for_response(regs, response); +} + +/** + * get_subnode_info() - Get subnode information + * + * @regs: HDA registers + * @nid: Parent node ID to check + * @num_sub_nodesp: Returns number of subnodes + * @start_sub_node_nidp: Returns start subnode number + * @return 0 if OK, -ve on error + */ +static int get_subnode_info(struct hda_regs *regs, uint nid, + uint *num_sub_nodesp, uint *start_sub_node_nidp) +{ + uint response; + int ret; + + ret = exec_verb(regs, hda_verb(nid, HDA_VERB_GET_PARAMS, + GET_PARAMS_NODE_COUNT), + &response); + if (ret < 0) { + printf("Audio: Error reading sub-node info %d\n", nid); + return ret; + } + + *num_sub_nodesp = (response & NUM_SUB_NODES_M) >> NUM_SUB_NODES_S; + *start_sub_node_nidp = (response & FIRST_SUB_NODE_M) >> + FIRST_SUB_NODE_S; + + return 0; +} + +/** + * find_beep_node_in_group() - Finds the beeping node + * + * Searches the audio group for a node that supports beeping + * + * @regs: HDA registers + * @group_nid: Group node ID to check + * @return 0 if OK, -ve on error + */ +static uint find_beep_node_in_group(struct hda_regs *regs, uint group_nid) +{ + uint node_count = 0; + uint current_nid = 0; + uint response; + uint end_nid; + int ret; + + ret = get_subnode_info(regs, group_nid, &node_count, ¤t_nid); + if (ret < 0) + return 0; + + end_nid = current_nid + node_count; + while (current_nid < end_nid) { + ret = exec_verb(regs, + hda_verb(current_nid, HDA_VERB_GET_PARAMS, + GET_PARAMS_AUDIO_WIDGET_CAPS), + &response); + if (ret < 0) { + printf("Audio: Error reading widget caps\n"); + return 0; + } + + if ((response & AUDIO_WIDGET_TYPE_M) >> AUDIO_WIDGET_TYPE_S == + AUDIO_WIDGET_TYPE_BEEP) + return current_nid; + + current_nid++; + } + + return 0; /* no beep node found */ +} + +/** + * audio_group_has_beep_node() - Check if group has a beep node + * + * Checks if the given audio group contains a beep generator + * @regs: HDA registers + * @nid: Node ID to check + * @return 0 if OK, -ve on error + */ +static int audio_group_has_beep_node(struct hda_regs *regs, uint nid) +{ + uint response; + int ret; + + ret = exec_verb(regs, hda_verb(nid, HDA_VERB_GET_PARAMS, + GET_PARAMS_AUDIO_GROUP_CAPS), + &response); + if (ret < 0) { + printf("Audio: Error reading audio group caps %d\n", nid); + return 0; + } + + return !!(response & AUDIO_GROUP_CAPS_BEEP_GEN); +} + +/** + * get_hda_beep_nid() - Finds the node ID of the beep node + * + * Finds the nid of the beep node if it exists. Starts at the root node, for + * each sub-node checks if the group contains a beep node. If the group + * contains a beep node, polls each node in the group until it is found. + * + * If the device has a intel,beep-nid property, the value of that is used + * instead. + * + * @dev: Sound device + * @return Node ID >0 if found, -ve error code otherwise + */ +static int get_hda_beep_nid(struct udevice *dev) +{ + struct hda_codec_priv *priv = dev_get_priv(dev); + uint current_nid = 0; + uint node_count = 0; + uint end_nid; + int ret; + + /* If the field exists, use the beep nid set in the fdt */ + ret = dev_read_u32(dev, "intel,beep-nid", ¤t_nid); + if (!ret) + return current_nid; + + ret = get_subnode_info(priv->regs, HDA_ROOT_NODE, &node_count, + ¤t_nid); + if (ret < 0) + return ret; + + end_nid = current_nid + node_count; + while (current_nid < end_nid) { + if (audio_group_has_beep_node(priv->regs, current_nid)) + return find_beep_node_in_group(priv->regs, + current_nid); + current_nid++; + } + /* no beep node found */ + + return -ENOENT; +} + +/** + * set_beep_divisor() - Sets the beep divisor to set the pitch + * + * @priv: Device's private data + * @divider: Divider value (0 to disable the beep) + * @return 0 if OK, -ve on error + */ +static int set_beep_divisor(struct hda_codec_priv *priv, uint divider) +{ + return exec_verb(priv->regs, + hda_verb(priv->beep_nid, HDA_VERB_SET_BEEP, divider), + NULL); +} + +int hda_codec_init(struct udevice *dev) +{ + struct hda_codec_priv *priv = dev_get_priv(dev); + ulong base_addr; + + base_addr = dm_pci_read_bar32(dev, 0); + log_debug("base = %08lx\n", base_addr); + if (!base_addr) + return -EINVAL; + + priv->regs = (struct hda_regs *)base_addr; + + return 0; +} + +int hda_codec_finish_init(struct udevice *dev) +{ + struct hda_codec_priv *priv = dev_get_priv(dev); + int ret; + + ret = get_hda_beep_nid(dev); + if (ret <= 0) { + log_warning("Could not find beep NID (err=%d)\n", ret); + return ret ? ret : -ENOENT; + } + priv->beep_nid = ret; + + return 0; +} + +int hda_codec_start_beep(struct udevice *dev, int frequency_hz) +{ + struct hda_codec_priv *priv = dev_get_priv(dev); + uint divider_val; + + if (!priv->beep_nid) { + log_err("Failed to find a beep-capable node\n"); + return -ENOENT; + } + + if (!frequency_hz) + divider_val = 0; /* off */ + else if (frequency_hz > BEEP_FREQ_BASE) + divider_val = 1; + else if (frequency_hz < BEEP_FREQ_BASE / 0xff) + divider_val = 0xff; + else + divider_val = 0xff & (BEEP_FREQ_BASE / frequency_hz); + + return set_beep_divisor(priv, divider_val); +} + +int hda_codec_stop_beep(struct udevice *dev) +{ + struct hda_codec_priv *priv = dev_get_priv(dev); + + return set_beep_divisor(priv, 0); +} + +static const struct sound_ops hda_codec_ops = { + .setup = hda_codec_finish_init, + .start_beep = hda_codec_start_beep, + .stop_beep = hda_codec_stop_beep, +}; + +U_BOOT_DRIVER(hda_codec) = { + .name = "hda_codec", + .id = UCLASS_SOUND, + .ops = &hda_codec_ops, + .priv_auto_alloc_size = sizeof(struct hda_codec_priv), + .probe = hda_codec_init, +}; + +static struct pci_device_id hda_supported[] = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COUGARPOINT_HDA}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_HDA}, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_WILDCATPOINT_HDA) }, + + /* + * Note this driver is not necessarily generic, but it attempts to + * support any codec in the hd-audio class + */ + { PCI_DEVICE_CLASS(PCI_CLASS_MULTIMEDIA_HD_AUDIO, 0xffffff) }, +}; + +U_BOOT_PCI_DEVICE(hda_codec, hda_supported); diff --git a/include/dt-bindings/sound/azalia.h b/include/dt-bindings/sound/azalia.h new file mode 100644 index 00000000000..10ace3ef563 --- /dev/null +++ b/include/dt-bindings/sound/azalia.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Intel HDA audio codec config. This is a mechanicm to configure codecs when + * using Intel HDA audio. + * + * Copyright 2018 Google LLC + * Written by Simon Glass + */ + +#ifndef __AZALIA_H +#define __AZALIA_H + +#define AZALIA_CODEC_SHIFT 28 +#define AZALIA_NID_SHIFT 20 +#define AZALIA_VERB_SHIFT 8 + +/* Supported opcodes */ +#define AZALIA_OPCODE_CONFIG_DEFAULT 0x71c +#define AZALIA_OPCODE_IMPL_ID 0x720 +#define AZALIA_OPCODE_READ_PARAM 0xf00 + +#define AZALIA_PARAM_VENDOR_ID 0 + +/* Generate the register value to write a particular byte of a 32-bit value */ +#define AZALIA_SET_BYTE(codec, nid, opcode, val, byte) \ + ((codec) << AZALIA_CODEC_SHIFT | \ + (nid) << AZALIA_NID_SHIFT | \ + ((opcode) + (byte)) << AZALIA_VERB_SHIFT | \ + (((val) >> ((byte) * 8)) & 0xff)) + +/* Generate the register value to write all bytes of a 32-bit value */ +#define AZALIA_WORD(codec, nid, opcode, val) \ + (AZALIA_SET_BYTE(codec, nid, opcode, val, 0) | \ + AZALIA_SET_BYTE(codec, nid, opcode, val, 1) | \ + AZALIA_SET_BYTE(codec, nid, opcode, val, 2) | \ + AZALIA_SET_BYTE(codec, nid, opcode, val, 3)) + +#define AZALIA_PIN_CFG(codec, nid, val) \ + AZALIA_WORD(codec, nid, AZALIA_OPCODE_CONFIG_DEFAULT, val) + +#define AZALIA_SUBVENDOR(codec, val) \ + AZALIA_WORD(codec, 1, AZALIA_OPCODE_IMPL_ID, val) + +#endif /* __AZALIA_H */ diff --git a/include/hda_codec.h b/include/hda_codec.h new file mode 100644 index 00000000000..56de571f0f7 --- /dev/null +++ b/include/hda_codec.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Support for Intel High-Definition Audio codec + * + * Copyright 2018 Google LLC + * + * Taken from coreboot file of the same name + */ + +#ifndef __HDA_CODEC_H_ +#define __HDA_CODEC_H_ + +struct hda_regs; + +/** + * struct hda_codec_priv - Private data required by the HDA codec + * + * @regs: HDA registers + * @beep_nid: Node ID of beep node (>0) + */ +struct hda_codec_priv { + struct hda_regs *regs; + uint beep_nid; +}; + +/** + * hda_wait_for_ready() - Wait for the codec to indicate it is ready + * + * @regs: HDA registers + * @return 0 if OK -ETIMEDOUT if codec did not respond in time + */ +int hda_wait_for_ready(struct hda_regs *regs); + +/** + * hda_wait_for_valid() - Wait for the codec to accept the last command + * + * @regs: HDA registers + * @return 0 if OK -ETIMEDOUT if codec did not respond in time + */ +int hda_wait_for_valid(struct hda_regs *regs); + +/** + * hda_codec_detect() - Detect which codecs are present + * + * @regs: HDA registers + * @return bit mask of active codecs (0 if none) + * @return 0 if OK, -ve on error + */ +int hda_codec_detect(struct hda_regs *regs); + +/** + * hda_codecs_init() - Init all codecs + * + * @dev: Sound device + * @regs: HDA registers + * @codec_mask: Mask of codecs to init (bits 3:0) + * @return 0 if OK, -ve on error + */ +int hda_codecs_init(struct udevice *dev, struct hda_regs *regs, u32 codec_mask); + +/** + * hda_codec_start_beep() - Start beeping + * + * This tells the sound hardware to start a beep. It will continue until stopped + * by sound_stop_beep(). + * + * @dev: Sound device + * @frequency_hz: Beep frequency in hertz + * @return if OK, -ve on error + */ +int hda_codec_start_beep(struct udevice *dev, int frequency_hz); + +/** + * hda_codec_stop_beep() - Stop beeping + * + * This tells the sound hardware to stop a previously started beep. + * + * @dev: Sound device + * @return if OK, -ve on error + */ +int hda_codec_stop_beep(struct udevice *dev); + +/** + * hda_codec_init() - Set up the HDA codec base address + * + * This should be called at the start of the probe() method. + * + * @dev: Sound device + * @return 0 if OK, -ve on error + */ +int hda_codec_init(struct udevice *dev); + +/** + * hda_codec_finish_init() - Finish setting up the HDA codec base address + * + * This should be called at the end of the probe() method. + * + * @dev: Sound device + * @return 0 if OK, -ve on error + */ +int hda_codec_finish_init(struct udevice *dev); + +#endif /* __HDA_CODEC_H_ */ -- cgit v1.3.1