Skip to content

Commit

Permalink
drm/bridge: adv7511: Add Audio support
Browse files Browse the repository at this point in the history
This patch adds support to Audio for both adv7511 and adv7533
bridge chips.

This patch was originally from [1] by Lars-Peter Clausen <lars@metafoo.de>
and was adapted by Archit Taneja <architt@codeaurora.org> and
Srinivas Kandagatla <srinivas.kandagatla@linaro.org>.

Then I heavily reworked it to use the hdmi-codec driver. And also
folded in some audio packet initialization done by Andy Green
<andy.green@linaro.org>. So credit to them, but blame to me.

[1] https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers/gpu/drm/i2c/adv7511_audio.c

Cc: David Airlie <airlied@linux.ie>
Cc: Archit Taneja <architt@codeaurora.org>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Wolfram Sang <wsa+renesas@sang-engineering.com>
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Cc: "Ville Syrjälä" <ville.syrjala@linux.intel.com>
Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
Cc: Andy Green <andy@warmcat.com>
Cc: Dave Long <dave.long@linaro.org>
Cc: Guodong Xu <guodong.xu@linaro.org>
Cc: Zhangfei Gao <zhangfei.gao@linaro.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Jose Abreu <joabreu@synopsys.com>
Cc: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Cc: dri-devel@lists.freedesktop.org
Acked-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Link: http://patchwork.freedesktop.org/patch/msgid/1480382552-28219-2-git-send-email-john.stultz@linaro.org
  • Loading branch information
John Stultz authored and Archit Taneja committed Nov 29, 2016
1 parent c45a4e4 commit 53c515b
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 0 deletions.
8 changes: 8 additions & 0 deletions drivers/gpu/drm/bridge/adv7511/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ config DRM_I2C_ADV7511
help
Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders.

config DRM_I2C_ADV7511_AUDIO
bool "ADV7511 HDMI Audio driver"
depends on DRM_I2C_ADV7511 && SND_SOC
select SND_SOC_HDMI_CODEC
help
Support the ADV7511 HDMI Audio interface. This is used in
conjunction with the AV7511 HDMI driver.

config DRM_I2C_ADV7533
bool "ADV7533 encoder"
depends on DRM_I2C_ADV7511
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/bridge/adv7511/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
adv7511-y := adv7511_drv.o
adv7511-$(CONFIG_DRM_I2C_ADV7511_AUDIO) += adv7511_audio.o
adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o
16 changes: 16 additions & 0 deletions drivers/gpu/drm/bridge/adv7511/adv7511.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ struct adv7511 {
struct drm_display_mode curr_mode;

unsigned int f_tmds;
unsigned int f_audio;
unsigned int audio_source;

unsigned int current_edid_segment;
uint8_t edid_buf[256];
Expand All @@ -334,6 +336,7 @@ struct adv7511 {
bool use_timing_gen;

enum adv7511_type type;
struct platform_device *audio_pdev;
};

#ifdef CONFIG_DRM_I2C_ADV7533
Expand Down Expand Up @@ -389,4 +392,17 @@ static inline int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv)
}
#endif

#ifdef CONFIG_DRM_I2C_ADV7511_AUDIO
int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511);
void adv7511_audio_exit(struct adv7511 *adv7511);
#else /*CONFIG_DRM_I2C_ADV7511_AUDIO */
static inline int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511)
{
return 0;
}
static inline void adv7511_audio_exit(struct adv7511 *adv7511)
{
}
#endif /* CONFIG_DRM_I2C_ADV7511_AUDIO */

#endif /* __DRM_I2C_ADV7511_H__ */
213 changes: 213 additions & 0 deletions drivers/gpu/drm/bridge/adv7511/adv7511_audio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
/*
* Analog Devices ADV7511 HDMI transmitter driver
*
* Copyright 2012 Analog Devices Inc.
* Copyright (c) 2016, Linaro Limited
*
* Licensed under the GPL-2.
*/

#include <sound/core.h>
#include <sound/hdmi-codec.h>
#include <sound/pcm.h>
#include <sound/soc.h>

#include "adv7511.h"

static void adv7511_calc_cts_n(unsigned int f_tmds, unsigned int fs,
unsigned int *cts, unsigned int *n)
{
switch (fs) {
case 32000:
*n = 4096;
break;
case 44100:
*n = 6272;
break;
case 48000:
*n = 6144;
break;
}

*cts = ((f_tmds * *n) / (128 * fs)) * 1000;
}

static int adv7511_update_cts_n(struct adv7511 *adv7511)
{
unsigned int cts = 0;
unsigned int n = 0;

adv7511_calc_cts_n(adv7511->f_tmds, adv7511->f_audio, &cts, &n);

regmap_write(adv7511->regmap, ADV7511_REG_N0, (n >> 16) & 0xf);
regmap_write(adv7511->regmap, ADV7511_REG_N1, (n >> 8) & 0xff);
regmap_write(adv7511->regmap, ADV7511_REG_N2, n & 0xff);

regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL0,
(cts >> 16) & 0xf);
regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL1,
(cts >> 8) & 0xff);
regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL2,
cts & 0xff);

return 0;
}

int adv7511_hdmi_hw_params(struct device *dev, void *data,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms)
{
struct adv7511 *adv7511 = dev_get_drvdata(dev);
unsigned int audio_source, i2s_format = 0;
unsigned int invert_clock;
unsigned int rate;
unsigned int len;

switch (hparms->sample_rate) {
case 32000:
rate = ADV7511_SAMPLE_FREQ_32000;
break;
case 44100:
rate = ADV7511_SAMPLE_FREQ_44100;
break;
case 48000:
rate = ADV7511_SAMPLE_FREQ_48000;
break;
case 88200:
rate = ADV7511_SAMPLE_FREQ_88200;
break;
case 96000:
rate = ADV7511_SAMPLE_FREQ_96000;
break;
case 176400:
rate = ADV7511_SAMPLE_FREQ_176400;
break;
case 192000:
rate = ADV7511_SAMPLE_FREQ_192000;
break;
default:
return -EINVAL;
}

switch (hparms->sample_width) {
case 16:
len = ADV7511_I2S_SAMPLE_LEN_16;
break;
case 18:
len = ADV7511_I2S_SAMPLE_LEN_18;
break;
case 20:
len = ADV7511_I2S_SAMPLE_LEN_20;
break;
case 24:
len = ADV7511_I2S_SAMPLE_LEN_24;
break;
default:
return -EINVAL;
}

switch (fmt->fmt) {
case HDMI_I2S:
audio_source = ADV7511_AUDIO_SOURCE_I2S;
i2s_format = ADV7511_I2S_FORMAT_I2S;
break;
case HDMI_RIGHT_J:
audio_source = ADV7511_AUDIO_SOURCE_I2S;
i2s_format = ADV7511_I2S_FORMAT_RIGHT_J;
break;
case HDMI_LEFT_J:
audio_source = ADV7511_AUDIO_SOURCE_I2S;
i2s_format = ADV7511_I2S_FORMAT_LEFT_J;
break;
default:
return -EINVAL;
}

invert_clock = fmt->bit_clk_inv;

regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_SOURCE, 0x70,
audio_source << 4);
regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(6),
invert_clock << 6);
regmap_update_bits(adv7511->regmap, ADV7511_REG_I2S_CONFIG, 0x03,
i2s_format);

adv7511->audio_source = audio_source;

adv7511->f_audio = hparms->sample_rate;

adv7511_update_cts_n(adv7511);

regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG3,
ADV7511_AUDIO_CFG3_LEN_MASK, len);
regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG,
ADV7511_I2C_FREQ_ID_CFG_RATE_MASK, rate << 4);
regmap_write(adv7511->regmap, 0x73, 0x1);

return 0;
}

static int audio_startup(struct device *dev, void *data)
{
struct adv7511 *adv7511 = dev_get_drvdata(dev);

regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
BIT(7), 0);

/* hide Audio infoframe updates */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
BIT(5), BIT(5));
/* enable N/CTS, enable Audio sample packets */
regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
BIT(5), BIT(5));
/* enable N/CTS */
regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
BIT(6), BIT(6));
/* not copyrighted */
regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG1,
BIT(5), BIT(5));
/* enable audio infoframes */
regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
BIT(3), BIT(3));
/* AV mute disable */
regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(0),
BIT(7) | BIT(6), BIT(7));
/* use Audio infoframe updated info */
regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(1),
BIT(5), 0);
return 0;
}

static void audio_shutdown(struct device *dev, void *data)
{
}

static const struct hdmi_codec_ops adv7511_codec_ops = {
.hw_params = adv7511_hdmi_hw_params,
.audio_shutdown = audio_shutdown,
.audio_startup = audio_startup,
};

static struct hdmi_codec_pdata codec_data = {
.ops = &adv7511_codec_ops,
.max_i2s_channels = 2,
.i2s = 1,
};

int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511)
{
adv7511->audio_pdev = platform_device_register_data(dev,
HDMI_CODEC_DRV_NAME,
PLATFORM_DEVID_AUTO,
&codec_data,
sizeof(codec_data));
return PTR_ERR_OR_ZERO(adv7511->audio_pdev);
}

void adv7511_audio_exit(struct adv7511 *adv7511)
{
if (adv7511->audio_pdev) {
platform_device_unregister(adv7511->audio_pdev);
adv7511->audio_pdev = NULL;
}
}
4 changes: 4 additions & 0 deletions drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,8 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
goto err_unregister_cec;
}

adv7511_audio_init(dev, adv7511);

return 0;

err_unregister_cec:
Expand All @@ -1058,6 +1060,8 @@ static int adv7511_remove(struct i2c_client *i2c)

drm_bridge_remove(&adv7511->bridge);

adv7511_audio_exit(adv7511);

i2c_unregister_device(adv7511->i2c_edid);

kfree(adv7511->edid);
Expand Down

0 comments on commit 53c515b

Please sign in to comment.