f07e572f64
bcm2708: boot tested on RPi B+ v1.2 bcm2709: boot tested on RPi 3B v1.2 and RPi 4B v1.1 4G bcm2710: boot tested on RPi 3B v1.2 bcm2711: boot tested on RPi 4B v1.1 4G Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
802 lines
23 KiB
Diff
802 lines
23 KiB
Diff
From 221b442eb7e5b4ed16151b5501f4b905a9b8455c Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?J=C3=B6rg=20Schambacher?=
|
|
<j-schambacher@users.noreply.github.com>
|
|
Date: Tue, 21 Jan 2020 15:58:39 +0100
|
|
Subject: [PATCH] sound: Add the HiFiBerry DAC+HD version
|
|
|
|
This adds the driver for the DAC+HD version supporting HiFiBerry's
|
|
PCM179x based DACs. It also adds PLL control for clock generation.
|
|
|
|
Signed-off-by: Joerg Schambacher <joerg@i2audio.com>
|
|
---
|
|
arch/arm/boot/dts/overlays/Makefile | 1 +
|
|
arch/arm/boot/dts/overlays/README | 6 +
|
|
.../overlays/hifiberry-dacplushd-overlay.dts | 106 ++++++
|
|
drivers/clk/Kconfig | 3 +
|
|
drivers/clk/Makefile | 1 +
|
|
drivers/clk/clk-hifiberry-dachd.c | 333 ++++++++++++++++++
|
|
sound/soc/bcm/Kconfig | 9 +
|
|
sound/soc/bcm/Makefile | 2 +
|
|
sound/soc/bcm/hifiberry_dacplushd.c | 238 +++++++++++++
|
|
14 files changed, 704 insertions(+)
|
|
create mode 100644 arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
|
|
create mode 100644 drivers/clk/clk-hifiberry-dachd.c
|
|
create mode 100644 sound/soc/bcm/hifiberry_dacplushd.c
|
|
|
|
--- a/arch/arm/boot/dts/overlays/Makefile
|
|
+++ b/arch/arm/boot/dts/overlays/Makefile
|
|
@@ -57,6 +57,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
|
|
hifiberry-dacplusadc.dtbo \
|
|
hifiberry-dacplusadcpro.dtbo \
|
|
hifiberry-dacplusdsp.dtbo \
|
|
+ hifiberry-dacplushd.dtbo \
|
|
hifiberry-digi.dtbo \
|
|
hifiberry-digi-pro.dtbo \
|
|
hy28a.dtbo \
|
|
--- a/arch/arm/boot/dts/overlays/README
|
|
+++ b/arch/arm/boot/dts/overlays/README
|
|
@@ -956,6 +956,12 @@ Load: dtoverlay=hifiberry-dacplusdsp
|
|
Params: <None>
|
|
|
|
|
|
+Name: hifiberry-dacplushd
|
|
+Info: Configures the HifiBerry DAC+ HD audio card
|
|
+Load: dtoverlay=hifiberry-dacplushd
|
|
+Params: <None>
|
|
+
|
|
+
|
|
Name: hifiberry-digi
|
|
Info: Configures the HifiBerry Digi and Digi+ audio card
|
|
Load: dtoverlay=hifiberry-digi
|
|
--- /dev/null
|
|
+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
|
|
@@ -0,0 +1,106 @@
|
|
+// Definitions for HiFiBerry DAC+ HD
|
|
+/dts-v1/;
|
|
+/plugin/;
|
|
+
|
|
+#include <dt-bindings/gpio/gpio.h>
|
|
+
|
|
+/ {
|
|
+ compatible = "brcm,bcm2835";
|
|
+
|
|
+ fragment@0 {
|
|
+ target-path = "/clocks";
|
|
+ __overlay__ {
|
|
+ dachd_osc: pll_dachd_osc {
|
|
+ compatible = "hifiberry,dachd-clk";
|
|
+ #clock-cells = <0>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ fragment@1 {
|
|
+ target = <&i2s>;
|
|
+ __overlay__ {
|
|
+ status = "okay";
|
|
+ };
|
|
+ };
|
|
+
|
|
+ fragment@2 {
|
|
+ target = <&i2c1>;
|
|
+ __overlay__ {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ status = "okay";
|
|
+
|
|
+ pcm1792a@4c {
|
|
+ compatible = "ti,pcm1792a";
|
|
+ #sound-dai-cells = <0>;
|
|
+ #clock-cells = <0>;
|
|
+ clocks = <&dachd_osc>;
|
|
+ reg = <0x4c>;
|
|
+ status = "okay";
|
|
+ };
|
|
+ pll: pll@62 {
|
|
+ compatible = "hifiberry,dachd-clk";
|
|
+ #clock-cells = <0>;
|
|
+ reg = <0x62>;
|
|
+ clocks = <&dachd_osc>;
|
|
+ status = "okay";
|
|
+ common_pll_regs = [
|
|
+ 02 53 03 00 07 20 0F 00
|
|
+ 10 0D 11 1D 12 0D 13 8C
|
|
+ 14 8C 15 8C 16 8C 17 8C
|
|
+ 18 2A 1C 00 1D 0F 1F 00
|
|
+ 2A 00 2C 00 2F 00 30 00
|
|
+ 31 00 32 00 34 00 37 00
|
|
+ 38 00 39 00 3A 00 3B 01
|
|
+ 3E 00 3F 00 40 00 41 00
|
|
+ 5A 00 5B 00 95 00 96 00
|
|
+ 97 00 98 00 99 00 9A 00
|
|
+ 9B 00 A2 00 A3 00 A4 00
|
|
+ B7 92 ];
|
|
+ 192k_pll_regs = [
|
|
+ 1A 0C 1B 35 1E F0 20 09
|
|
+ 21 50 2B 02 2D 10 2E 40
|
|
+ 33 01 35 22 36 80 3C 22
|
|
+ 3D 46 ];
|
|
+ 96k_pll_regs = [
|
|
+ 1A 0C 1B 35 1E F0 20 09
|
|
+ 21 50 2B 02 2D 10 2E 40
|
|
+ 33 01 35 47 36 00 3C 32
|
|
+ 3D 46 ];
|
|
+ 48k_pll_regs = [
|
|
+ 1A 0C 1B 35 1E F0 20 09
|
|
+ 21 50 2B 02 2D 10 2E 40
|
|
+ 33 01 35 90 36 00 3C 42
|
|
+ 3D 46 ];
|
|
+ 176k4_pll_regs = [
|
|
+ 1A 3D 1B 09 1E F3 20 13
|
|
+ 21 75 2B 04 2D 11 2E E0
|
|
+ 33 02 35 25 36 C0 3C 22
|
|
+ 3D 7A ];
|
|
+ 88k2_pll_regs = [
|
|
+ 1A 3D 1B 09 1E F3 20 13
|
|
+ 21 75 2B 04 2D 11 2E E0
|
|
+ 33 01 35 4D 36 80 3C 32
|
|
+ 3D 7A ];
|
|
+ 44k1_pll_regs = [
|
|
+ 1A 3D 1B 09 1E F3 20 13
|
|
+ 21 75 2B 04 2D 11 2E E0
|
|
+ 33 01 35 9D 36 00 3C 42
|
|
+ 3D 7A ];
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ fragment@3 {
|
|
+ target = <&sound>;
|
|
+ __overlay__ {
|
|
+ compatible = "hifiberry,hifiberry-dacplushd";
|
|
+ i2s-controller = <&i2s>;
|
|
+ clocks = <&pll 0>;
|
|
+ reset-gpio = <&gpio 16 GPIO_ACTIVE_LOW>;
|
|
+ status = "okay";
|
|
+ };
|
|
+ };
|
|
+
|
|
+};
|
|
--- a/drivers/clk/Kconfig
|
|
+++ b/drivers/clk/Kconfig
|
|
@@ -70,6 +70,9 @@ config COMMON_CLK_HI655X
|
|
multi-function device has one fixed-rate oscillator, clocked
|
|
at 32KHz.
|
|
|
|
+config COMMON_CLK_HIFIBERRY_DACPLUSHD
|
|
+ tristate
|
|
+
|
|
config COMMON_CLK_HIFIBERRY_DACPRO
|
|
tristate
|
|
|
|
--- a/drivers/clk/Makefile
|
|
+++ b/drivers/clk/Makefile
|
|
@@ -36,6 +36,7 @@ obj-$(CONFIG_ARCH_HIGHBANK) += clk-high
|
|
obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o
|
|
obj-$(CONFIG_COMMON_CLK_LOCHNAGAR) += clk-lochnagar.o
|
|
obj-$(CONFIG_COMMON_CLK_HIFIBERRY_DACPRO) += clk-hifiberry-dacpro.o
|
|
+obj-$(CONFIG_COMMON_CLK_HIFIBERRY_DACPLUSHD) += clk-hifiberry-dachd.o
|
|
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
|
|
obj-$(CONFIG_COMMON_CLK_MAX9485) += clk-max9485.o
|
|
obj-$(CONFIG_ARCH_MILBEAUT_M10V) += clk-milbeaut.o
|
|
--- /dev/null
|
|
+++ b/drivers/clk/clk-hifiberry-dachd.c
|
|
@@ -0,0 +1,333 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Clock Driver for HiFiBerry DAC+ HD
|
|
+ *
|
|
+ * Author: Joerg Schambacher, i2Audio GmbH for HiFiBerry
|
|
+ * Copyright 2020
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License
|
|
+ * version 2 as published by the Free Software Foundation.
|
|
+ *
|
|
+ * 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/clk-provider.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/regmap.h>
|
|
+
|
|
+#define NO_PLL_RESET 0
|
|
+#define PLL_RESET 1
|
|
+#define HIFIBERRY_PLL_MAX_REGISTER 256
|
|
+#define DEFAULT_RATE 44100
|
|
+
|
|
+static struct reg_default hifiberry_pll_reg_defaults[] = {
|
|
+ {0x02, 0x53}, {0x03, 0x00}, {0x07, 0x20}, {0x0F, 0x00},
|
|
+ {0x10, 0x0D}, {0x11, 0x1D}, {0x12, 0x0D}, {0x13, 0x8C},
|
|
+ {0x14, 0x8C}, {0x15, 0x8C}, {0x16, 0x8C}, {0x17, 0x8C},
|
|
+ {0x18, 0x2A}, {0x1C, 0x00}, {0x1D, 0x0F}, {0x1F, 0x00},
|
|
+ {0x2A, 0x00}, {0x2C, 0x00}, {0x2F, 0x00}, {0x30, 0x00},
|
|
+ {0x31, 0x00}, {0x32, 0x00}, {0x34, 0x00}, {0x37, 0x00},
|
|
+ {0x38, 0x00}, {0x39, 0x00}, {0x3A, 0x00}, {0x3B, 0x01},
|
|
+ {0x3E, 0x00}, {0x3F, 0x00}, {0x40, 0x00}, {0x41, 0x00},
|
|
+ {0x5A, 0x00}, {0x5B, 0x00}, {0x95, 0x00}, {0x96, 0x00},
|
|
+ {0x97, 0x00}, {0x98, 0x00}, {0x99, 0x00}, {0x9A, 0x00},
|
|
+ {0x9B, 0x00}, {0xA2, 0x00}, {0xA3, 0x00}, {0xA4, 0x00},
|
|
+ {0xB7, 0x92},
|
|
+ {0x1A, 0x3D}, {0x1B, 0x09}, {0x1E, 0xF3}, {0x20, 0x13},
|
|
+ {0x21, 0x75}, {0x2B, 0x04}, {0x2D, 0x11}, {0x2E, 0xE0},
|
|
+ {0x3D, 0x7A},
|
|
+ {0x35, 0x9D}, {0x36, 0x00}, {0x3C, 0x42},
|
|
+ { 177, 0xAC},
|
|
+};
|
|
+static struct reg_default common_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
|
|
+static int num_common_pll_regs;
|
|
+static struct reg_default dedicated_192k_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
|
|
+static int num_dedicated_192k_pll_regs;
|
|
+static struct reg_default dedicated_96k_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
|
|
+static int num_dedicated_96k_pll_regs;
|
|
+static struct reg_default dedicated_48k_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
|
|
+static int num_dedicated_48k_pll_regs;
|
|
+static struct reg_default dedicated_176k4_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
|
|
+static int num_dedicated_176k4_pll_regs;
|
|
+static struct reg_default dedicated_88k2_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
|
|
+static int num_dedicated_88k2_pll_regs;
|
|
+static struct reg_default dedicated_44k1_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
|
|
+static int num_dedicated_44k1_pll_regs;
|
|
+
|
|
+/**
|
|
+ * struct clk_hifiberry_drvdata - Common struct to the HiFiBerry DAC HD Clk
|
|
+ * @hw: clk_hw for the common clk framework
|
|
+ */
|
|
+struct clk_hifiberry_drvdata {
|
|
+ struct regmap *regmap;
|
|
+ struct clk *clk;
|
|
+ struct clk_hw hw;
|
|
+ unsigned long rate;
|
|
+};
|
|
+
|
|
+#define to_hifiberry_clk(_hw) \
|
|
+ container_of(_hw, struct clk_hifiberry_drvdata, hw)
|
|
+
|
|
+static int clk_hifiberry_dachd_write_pll_regs(struct regmap *regmap,
|
|
+ struct reg_default *regs,
|
|
+ int num, int do_pll_reset)
|
|
+{
|
|
+ int i;
|
|
+ int ret = 0;
|
|
+ char pll_soft_reset[] = { 177, 0xAC, };
|
|
+
|
|
+ for (i = 0; i < num; i++) {
|
|
+ ret |= regmap_write(regmap, regs[i].reg, regs[i].def);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+ if (do_pll_reset) {
|
|
+ ret |= regmap_write(regmap, pll_soft_reset[0],
|
|
+ pll_soft_reset[1]);
|
|
+ mdelay(10);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static unsigned long clk_hifiberry_dachd_recalc_rate(struct clk_hw *hw,
|
|
+ unsigned long parent_rate)
|
|
+{
|
|
+ return to_hifiberry_clk(hw)->rate;
|
|
+}
|
|
+
|
|
+static long clk_hifiberry_dachd_round_rate(struct clk_hw *hw,
|
|
+ unsigned long rate, unsigned long *parent_rate)
|
|
+{
|
|
+ return rate;
|
|
+}
|
|
+
|
|
+static int clk_hifiberry_dachd_set_rate(struct clk_hw *hw,
|
|
+ unsigned long rate, unsigned long parent_rate)
|
|
+{
|
|
+ int ret;
|
|
+ struct clk_hifiberry_drvdata *drvdata = to_hifiberry_clk(hw);
|
|
+
|
|
+ switch (rate) {
|
|
+ case 44100:
|
|
+ ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
|
|
+ dedicated_44k1_pll_regs, num_dedicated_44k1_pll_regs,
|
|
+ PLL_RESET);
|
|
+ break;
|
|
+ case 88200:
|
|
+ ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
|
|
+ dedicated_88k2_pll_regs, num_dedicated_88k2_pll_regs,
|
|
+ PLL_RESET);
|
|
+ break;
|
|
+ case 176400:
|
|
+ ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
|
|
+ dedicated_176k4_pll_regs, num_dedicated_176k4_pll_regs,
|
|
+ PLL_RESET);
|
|
+ break;
|
|
+ case 48000:
|
|
+ ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
|
|
+ dedicated_48k_pll_regs, num_dedicated_48k_pll_regs,
|
|
+ PLL_RESET);
|
|
+ break;
|
|
+ case 96000:
|
|
+ ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
|
|
+ dedicated_96k_pll_regs, num_dedicated_96k_pll_regs,
|
|
+ PLL_RESET);
|
|
+ break;
|
|
+ case 192000:
|
|
+ ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
|
|
+ dedicated_192k_pll_regs, num_dedicated_192k_pll_regs,
|
|
+ PLL_RESET);
|
|
+ break;
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ to_hifiberry_clk(hw)->rate = rate;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+const struct clk_ops clk_hifiberry_dachd_rate_ops = {
|
|
+ .recalc_rate = clk_hifiberry_dachd_recalc_rate,
|
|
+ .round_rate = clk_hifiberry_dachd_round_rate,
|
|
+ .set_rate = clk_hifiberry_dachd_set_rate,
|
|
+};
|
|
+
|
|
+static int clk_hifiberry_get_prop_values(struct device *dev,
|
|
+ char *prop_name,
|
|
+ struct reg_default *regs)
|
|
+{
|
|
+ int ret;
|
|
+ int i;
|
|
+ u8 tmp[2 * HIFIBERRY_PLL_MAX_REGISTER];
|
|
+
|
|
+ ret = of_property_read_variable_u8_array(dev->of_node, prop_name,
|
|
+ tmp, 0, 2 * HIFIBERRY_PLL_MAX_REGISTER);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ if (ret & 1) {
|
|
+ dev_err(dev,
|
|
+ "%s <%s> -> #%i odd number of bytes for reg/val pairs!",
|
|
+ __func__,
|
|
+ prop_name,
|
|
+ ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ret /= 2;
|
|
+ for (i = 0; i < ret; i++) {
|
|
+ regs[i].reg = (u32)tmp[2 * i];
|
|
+ regs[i].def = (u32)tmp[2 * i + 1];
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static int clk_hifiberry_dachd_dt_parse(struct device *dev)
|
|
+{
|
|
+ num_common_pll_regs = clk_hifiberry_get_prop_values(dev,
|
|
+ "common_pll_regs", common_pll_regs);
|
|
+ num_dedicated_44k1_pll_regs = clk_hifiberry_get_prop_values(dev,
|
|
+ "44k1_pll_regs", dedicated_44k1_pll_regs);
|
|
+ num_dedicated_88k2_pll_regs = clk_hifiberry_get_prop_values(dev,
|
|
+ "88k2_pll_regs", dedicated_88k2_pll_regs);
|
|
+ num_dedicated_176k4_pll_regs = clk_hifiberry_get_prop_values(dev,
|
|
+ "176k4_pll_regs", dedicated_176k4_pll_regs);
|
|
+ num_dedicated_48k_pll_regs = clk_hifiberry_get_prop_values(dev,
|
|
+ "48k_pll_regs", dedicated_48k_pll_regs);
|
|
+ num_dedicated_96k_pll_regs = clk_hifiberry_get_prop_values(dev,
|
|
+ "96k_pll_regs", dedicated_96k_pll_regs);
|
|
+ num_dedicated_192k_pll_regs = clk_hifiberry_get_prop_values(dev,
|
|
+ "192k_pll_regs", dedicated_192k_pll_regs);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int clk_hifiberry_dachd_remove(struct device *dev)
|
|
+{
|
|
+ of_clk_del_provider(dev->of_node);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+const struct regmap_config hifiberry_pll_regmap = {
|
|
+ .reg_bits = 8,
|
|
+ .val_bits = 8,
|
|
+ .max_register = HIFIBERRY_PLL_MAX_REGISTER,
|
|
+ .reg_defaults = hifiberry_pll_reg_defaults,
|
|
+ .num_reg_defaults = ARRAY_SIZE(hifiberry_pll_reg_defaults),
|
|
+ .cache_type = REGCACHE_RBTREE,
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(hifiberry_pll_regmap);
|
|
+
|
|
+
|
|
+static int clk_hifiberry_dachd_i2c_probe(struct i2c_client *i2c,
|
|
+ const struct i2c_device_id *id)
|
|
+{
|
|
+ struct clk_hifiberry_drvdata *hdclk;
|
|
+ int ret = 0;
|
|
+ struct clk_init_data init;
|
|
+ struct device *dev = &i2c->dev;
|
|
+ struct device_node *dev_node = dev->of_node;
|
|
+ struct regmap_config config = hifiberry_pll_regmap;
|
|
+
|
|
+ hdclk = devm_kzalloc(&i2c->dev,
|
|
+ sizeof(struct clk_hifiberry_drvdata), GFP_KERNEL);
|
|
+ if (!hdclk)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ i2c_set_clientdata(i2c, hdclk);
|
|
+
|
|
+ hdclk->regmap = devm_regmap_init_i2c(i2c, &config);
|
|
+
|
|
+ if (IS_ERR(hdclk->regmap))
|
|
+ return PTR_ERR(hdclk->regmap);
|
|
+
|
|
+ /* start PLL to allow detection of DAC */
|
|
+ ret = clk_hifiberry_dachd_write_pll_regs(hdclk->regmap,
|
|
+ hifiberry_pll_reg_defaults,
|
|
+ ARRAY_SIZE(hifiberry_pll_reg_defaults),
|
|
+ PLL_RESET);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ clk_hifiberry_dachd_dt_parse(dev);
|
|
+
|
|
+ /* restart PLL with configs from DTB */
|
|
+ ret = clk_hifiberry_dachd_write_pll_regs(hdclk->regmap, common_pll_regs,
|
|
+ num_common_pll_regs, PLL_RESET);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ init.name = "clk-hifiberry-dachd";
|
|
+ init.ops = &clk_hifiberry_dachd_rate_ops;
|
|
+ init.flags = 0;
|
|
+ init.parent_names = NULL;
|
|
+ init.num_parents = 0;
|
|
+
|
|
+ hdclk->hw.init = &init;
|
|
+
|
|
+ hdclk->clk = devm_clk_register(dev, &hdclk->hw);
|
|
+ if (IS_ERR(hdclk->clk)) {
|
|
+ dev_err(dev, "unable to register %s\n", init.name);
|
|
+ return PTR_ERR(hdclk->clk);
|
|
+ }
|
|
+
|
|
+ ret = of_clk_add_provider(dev_node, of_clk_src_simple_get, hdclk->clk);
|
|
+ if (ret != 0) {
|
|
+ dev_err(dev, "Cannot of_clk_add_provider");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = clk_set_rate(hdclk->hw.clk, DEFAULT_RATE);
|
|
+ if (ret != 0) {
|
|
+ dev_err(dev, "Cannot set rate : %d\n", ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int clk_hifiberry_dachd_i2c_remove(struct i2c_client *i2c)
|
|
+{
|
|
+ clk_hifiberry_dachd_remove(&i2c->dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct i2c_device_id clk_hifiberry_dachd_i2c_id[] = {
|
|
+ { "dachd-clk", },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(i2c, clk_hifiberry_dachd_i2c_id);
|
|
+
|
|
+static const struct of_device_id clk_hifiberry_dachd_of_match[] = {
|
|
+ { .compatible = "hifiberry,dachd-clk", },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, clk_hifiberry_dachd_of_match);
|
|
+
|
|
+static struct i2c_driver clk_hifiberry_dachd_i2c_driver = {
|
|
+ .probe = clk_hifiberry_dachd_i2c_probe,
|
|
+ .remove = clk_hifiberry_dachd_i2c_remove,
|
|
+ .id_table = clk_hifiberry_dachd_i2c_id,
|
|
+ .driver = {
|
|
+ .name = "dachd-clk",
|
|
+ .of_match_table = of_match_ptr(clk_hifiberry_dachd_of_match),
|
|
+ },
|
|
+};
|
|
+
|
|
+module_i2c_driver(clk_hifiberry_dachd_i2c_driver);
|
|
+
|
|
+
|
|
+MODULE_DESCRIPTION("HiFiBerry DAC+ HD clock driver");
|
|
+MODULE_AUTHOR("Joerg Schambacher <joerg@i2audio.com>");
|
|
+MODULE_LICENSE("GPL v2");
|
|
+MODULE_ALIAS("platform:clk-hifiberry-dachd");
|
|
--- a/sound/soc/bcm/Kconfig
|
|
+++ b/sound/soc/bcm/Kconfig
|
|
@@ -42,6 +42,14 @@ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
|
|
help
|
|
Say Y or M if you want to add support for HifiBerry DAC+.
|
|
|
|
+config SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD
|
|
+ tristate "Support for HifiBerry DAC+ HD"
|
|
+ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
|
|
+ select SND_SOC_PCM179X_I2C
|
|
+ select COMMON_CLK_HIFIBERRY_DACPLUSHD
|
|
+ help
|
|
+ Say Y or M if you want to add support for HifiBerry DAC+ HD.
|
|
+
|
|
config SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC
|
|
tristate "Support for HifiBerry DAC+ADC"
|
|
depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
|
|
@@ -56,6 +64,7 @@ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
|
|
depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
|
|
select SND_SOC_PCM512x_I2C
|
|
select SND_SOC_PCM186X_I2C
|
|
+ select COMMON_CLK_HIFIBERRY_DACPRO
|
|
help
|
|
Say Y or M if you want to add support for HifiBerry DAC+ADC PRO.
|
|
|
|
--- a/sound/soc/bcm/Makefile
|
|
+++ b/sound/soc/bcm/Makefile
|
|
@@ -14,6 +14,7 @@ snd-soc-googlevoicehat-codec-objs := goo
|
|
|
|
# BCM2708 Machine Support
|
|
snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o
|
|
+snd-soc-hifiberry-dacplushd-objs := hifiberry_dacplushd.o
|
|
snd-soc-hifiberry-dacplusadc-objs := hifiberry_dacplusadc.o
|
|
snd-soc-hifiberry-dacplusadcpro-objs := hifiberry_dacplusadcpro.o
|
|
snd-soc-hifiberry-dacplusdsp-objs := hifiberry_dacplusdsp.o
|
|
@@ -41,6 +42,7 @@ snd-soc-rpi-wm8804-soundcard-objs := rpi
|
|
|
|
obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD) += snd-soc-googlevoicehat-codec.o
|
|
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o
|
|
+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD) += snd-soc-hifiberry-dacplushd.o
|
|
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC) += snd-soc-hifiberry-dacplusadc.o
|
|
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADCPRO) += snd-soc-hifiberry-dacplusadcpro.o
|
|
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSDSP) += snd-soc-hifiberry-dacplusdsp.o
|
|
--- /dev/null
|
|
+++ b/sound/soc/bcm/hifiberry_dacplushd.c
|
|
@@ -0,0 +1,238 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * ASoC Driver for HiFiBerry DAC+ HD
|
|
+ *
|
|
+ * Author: Joerg Schambacher, i2Audio GmbH for HiFiBerry
|
|
+ * Copyright 2020
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License
|
|
+ * version 2 as published by the Free Software Foundation.
|
|
+ *
|
|
+ * 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/module.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
+#include <sound/core.h>
|
|
+#include <sound/pcm.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/soc.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/clk.h>
|
|
+
|
|
+#include "../codecs/pcm179x.h"
|
|
+
|
|
+#define DEFAULT_RATE 44100
|
|
+
|
|
+struct brd_drv_data {
|
|
+ struct regmap *regmap;
|
|
+ struct clk *sclk;
|
|
+};
|
|
+
|
|
+static struct brd_drv_data drvdata;
|
|
+static struct gpio_desc *reset_gpio;
|
|
+static const unsigned int hb_dacplushd_rates[] = {
|
|
+ 192000, 96000, 48000, 176400, 88200, 44100,
|
|
+};
|
|
+
|
|
+static struct snd_pcm_hw_constraint_list hb_dacplushd_constraints = {
|
|
+ .list = hb_dacplushd_rates,
|
|
+ .count = ARRAY_SIZE(hb_dacplushd_rates),
|
|
+};
|
|
+
|
|
+static int snd_rpi_hb_dacplushd_startup(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ /* constraints for standard sample rates */
|
|
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
|
|
+ SNDRV_PCM_HW_PARAM_RATE,
|
|
+ &hb_dacplushd_constraints);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void snd_rpi_hifiberry_dacplushd_set_sclk(
|
|
+ struct snd_soc_component *component,
|
|
+ int sample_rate)
|
|
+{
|
|
+ if (!IS_ERR(drvdata.sclk))
|
|
+ clk_set_rate(drvdata.sclk, sample_rate);
|
|
+}
|
|
+
|
|
+static int snd_rpi_hifiberry_dacplushd_init(struct snd_soc_pcm_runtime *rtd)
|
|
+{
|
|
+ struct snd_soc_dai_link *dai = rtd->dai_link;
|
|
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
+
|
|
+ dai->name = "HiFiBerry DAC+ HD";
|
|
+ dai->stream_name = "HiFiBerry DAC+ HD HiFi";
|
|
+ dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
|
|
+ | SND_SOC_DAIFMT_CBM_CFM;
|
|
+
|
|
+ /* allow only fixed 32 clock counts per channel */
|
|
+ snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int snd_rpi_hifiberry_dacplushd_hw_params(
|
|
+ struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
|
|
+{
|
|
+ int ret = 0;
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+
|
|
+ struct snd_soc_component *component = rtd->codec_dai->component;
|
|
+
|
|
+ snd_rpi_hifiberry_dacplushd_set_sclk(component, params_rate(params));
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* machine stream operations */
|
|
+static struct snd_soc_ops snd_rpi_hifiberry_dacplushd_ops = {
|
|
+ .startup = snd_rpi_hb_dacplushd_startup,
|
|
+ .hw_params = snd_rpi_hifiberry_dacplushd_hw_params,
|
|
+};
|
|
+
|
|
+SND_SOC_DAILINK_DEFS(hifi,
|
|
+ DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
|
|
+ DAILINK_COMP_ARRAY(COMP_CODEC("pcm179x.1-004c", "pcm179x-hifi")),
|
|
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
|
|
+
|
|
+
|
|
+static struct snd_soc_dai_link snd_rpi_hifiberry_dacplushd_dai[] = {
|
|
+{
|
|
+ .name = "HiFiBerry DAC+ HD",
|
|
+ .stream_name = "HiFiBerry DAC+ HD HiFi",
|
|
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
|
+ SND_SOC_DAIFMT_CBS_CFS,
|
|
+ .ops = &snd_rpi_hifiberry_dacplushd_ops,
|
|
+ .init = snd_rpi_hifiberry_dacplushd_init,
|
|
+ SND_SOC_DAILINK_REG(hifi),
|
|
+},
|
|
+};
|
|
+
|
|
+/* audio machine driver */
|
|
+static struct snd_soc_card snd_rpi_hifiberry_dacplushd = {
|
|
+ .name = "snd_rpi_hifiberry_dacplushd",
|
|
+ .driver_name = "HifiberryDacplusHD",
|
|
+ .owner = THIS_MODULE,
|
|
+ .dai_link = snd_rpi_hifiberry_dacplushd_dai,
|
|
+ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dacplushd_dai),
|
|
+};
|
|
+
|
|
+static int snd_rpi_hifiberry_dacplushd_probe(struct platform_device *pdev)
|
|
+{
|
|
+ int ret = 0;
|
|
+ static int dac_reset_done;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct device_node *dev_node = dev->of_node;
|
|
+
|
|
+ snd_rpi_hifiberry_dacplushd.dev = &pdev->dev;
|
|
+
|
|
+ /* get GPIO and release DAC from RESET */
|
|
+ if (!dac_reset_done) {
|
|
+ reset_gpio = gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
|
|
+ if (IS_ERR(reset_gpio)) {
|
|
+ dev_err(&pdev->dev, "gpiod_get() failed\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ dac_reset_done = 1;
|
|
+ }
|
|
+ if (!IS_ERR(reset_gpio))
|
|
+ gpiod_set_value(reset_gpio, 0);
|
|
+ msleep(1);
|
|
+ if (!IS_ERR(reset_gpio))
|
|
+ gpiod_set_value(reset_gpio, 1);
|
|
+ msleep(1);
|
|
+ if (!IS_ERR(reset_gpio))
|
|
+ gpiod_set_value(reset_gpio, 0);
|
|
+
|
|
+ if (pdev->dev.of_node) {
|
|
+ struct device_node *i2s_node;
|
|
+ struct snd_soc_dai_link *dai;
|
|
+
|
|
+ dai = &snd_rpi_hifiberry_dacplushd_dai[0];
|
|
+ i2s_node = of_parse_phandle(pdev->dev.of_node,
|
|
+ "i2s-controller", 0);
|
|
+
|
|
+ if (i2s_node) {
|
|
+ dai->cpus->of_node = i2s_node;
|
|
+ dai->platforms->of_node = i2s_node;
|
|
+ dai->cpus->dai_name = NULL;
|
|
+ dai->platforms->name = NULL;
|
|
+ } else {
|
|
+ return -EPROBE_DEFER;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ ret = devm_snd_soc_register_card(&pdev->dev,
|
|
+ &snd_rpi_hifiberry_dacplushd);
|
|
+ if (ret && ret != -EPROBE_DEFER) {
|
|
+ dev_err(&pdev->dev,
|
|
+ "snd_soc_register_card() failed: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ if (ret == -EPROBE_DEFER)
|
|
+ return ret;
|
|
+
|
|
+ dev_set_drvdata(dev, &drvdata);
|
|
+ if (dev_node == NULL) {
|
|
+ dev_err(&pdev->dev, "Device tree node not found\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ drvdata.sclk = devm_clk_get(dev, NULL);
|
|
+ if (IS_ERR(drvdata.sclk)) {
|
|
+ drvdata.sclk = ERR_PTR(-ENOENT);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ clk_set_rate(drvdata.sclk, DEFAULT_RATE);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int snd_rpi_hifiberry_dacplushd_remove(struct platform_device *pdev)
|
|
+{
|
|
+ if (IS_ERR(reset_gpio))
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* put DAC into RESET and release GPIO */
|
|
+ gpiod_set_value(reset_gpio, 0);
|
|
+ gpiod_put(reset_gpio);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id snd_rpi_hifiberry_dacplushd_of_match[] = {
|
|
+ { .compatible = "hifiberry,hifiberry-dacplushd", },
|
|
+ {},
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_dacplushd_of_match);
|
|
+
|
|
+static struct platform_driver snd_rpi_hifiberry_dacplushd_driver = {
|
|
+ .driver = {
|
|
+ .name = "snd-rpi-hifiberry-dacplushd",
|
|
+ .owner = THIS_MODULE,
|
|
+ .of_match_table = snd_rpi_hifiberry_dacplushd_of_match,
|
|
+ },
|
|
+ .probe = snd_rpi_hifiberry_dacplushd_probe,
|
|
+ .remove = snd_rpi_hifiberry_dacplushd_remove,
|
|
+};
|
|
+
|
|
+module_platform_driver(snd_rpi_hifiberry_dacplushd_driver);
|
|
+
|
|
+MODULE_AUTHOR("Joerg Schambacher <joerg@i2audio.com>");
|
|
+MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+ HD");
|
|
+MODULE_LICENSE("GPL v2");
|