Commit cc95bb6c authored by Stephen Rothwell's avatar Stephen Rothwell

Merge remote-tracking branch 'drm-tegra/drm/tegra/for-next'

parents 79d79c32 db5adf4d
......@@ -10,6 +10,7 @@ tegra-drm-y := \
dc.o \
output.o \
rgb.o \
hda.o \
hdmi.o \
mipi-phy.o \
dsi.o \
......
......@@ -124,7 +124,7 @@ struct tegra_output {
struct drm_panel *panel;
struct i2c_adapter *ddc;
const struct edid *edid;
struct cec_notifier *notifier;
struct cec_notifier *cec;
unsigned int hpd_irq;
int hpd_gpio;
enum of_gpio_flags hpd_gpio_flags;
......
// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2019 NVIDIA Corporation
*/
#include <linux/bug.h>
#include <sound/hda_verbs.h>
#include "hda.h"
void tegra_hda_parse_format(unsigned int format, struct tegra_hda_format *fmt)
{
unsigned int mul, div, bits, channels;
if (format & AC_FMT_TYPE_NON_PCM)
fmt->pcm = false;
else
fmt->pcm = true;
if (format & AC_FMT_BASE_44K)
fmt->sample_rate = 44100;
else
fmt->sample_rate = 48000;
mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT;
div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT;
fmt->sample_rate *= (mul + 1) / (div + 1);
switch (format & AC_FMT_BITS_MASK) {
case AC_FMT_BITS_8:
fmt->bits = 8;
break;
case AC_FMT_BITS_16:
fmt->bits = 16;
break;
case AC_FMT_BITS_20:
fmt->bits = 20;
break;
case AC_FMT_BITS_24:
fmt->bits = 24;
break;
case AC_FMT_BITS_32:
fmt->bits = 32;
break;
default:
bits = (format & AC_FMT_BITS_MASK) >> AC_FMT_BITS_SHIFT;
WARN(1, "invalid number of bits: %#x\n", bits);
fmt->bits = 8;
break;
}
channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT;
/* channels are encoded as n - 1 */
fmt->channels = channels + 1;
}
// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2019 NVIDIA Corporation
*/
#ifndef DRM_TEGRA_HDA_H
#define DRM_TEGRA_HDA_H 1
#include <linux/types.h>
struct tegra_hda_format {
unsigned int sample_rate;
unsigned int channels;
unsigned int bits;
bool pcm;
};
void tegra_hda_parse_format(unsigned int format, struct tegra_hda_format *fmt);
#endif
......@@ -11,6 +11,7 @@
#include <linux/debugfs.h>
#include <linux/gpio.h>
#include <linux/hdmi.h>
#include <linux/math64.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
......@@ -20,10 +21,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <sound/hda_verbs.h>
#include <media/cec-notifier.h>
#include "hda.h"
#include "hdmi.h"
#include "drm.h"
#include "dc.h"
......@@ -71,8 +69,7 @@ struct tegra_hdmi {
const struct tegra_hdmi_config *config;
unsigned int audio_source;
unsigned int audio_sample_rate;
unsigned int audio_channels;
struct tegra_hda_format format;
unsigned int pixel_clock;
bool stereo;
......@@ -119,68 +116,11 @@ static inline void tegra_hdmi_writel(struct tegra_hdmi *hdmi, u32 value,
}
struct tegra_hdmi_audio_config {
unsigned int pclk;
unsigned int n;
unsigned int cts;
unsigned int aval;
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_32k[] = {
{ 25200000, 4096, 25200, 24000 },
{ 27000000, 4096, 27000, 24000 },
{ 74250000, 4096, 74250, 24000 },
{ 148500000, 4096, 148500, 24000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_44_1k[] = {
{ 25200000, 5880, 26250, 25000 },
{ 27000000, 5880, 28125, 25000 },
{ 74250000, 4704, 61875, 20000 },
{ 148500000, 4704, 123750, 20000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_48k[] = {
{ 25200000, 6144, 25200, 24000 },
{ 27000000, 6144, 27000, 24000 },
{ 74250000, 6144, 74250, 24000 },
{ 148500000, 6144, 148500, 24000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_88_2k[] = {
{ 25200000, 11760, 26250, 25000 },
{ 27000000, 11760, 28125, 25000 },
{ 74250000, 9408, 61875, 20000 },
{ 148500000, 9408, 123750, 20000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_96k[] = {
{ 25200000, 12288, 25200, 24000 },
{ 27000000, 12288, 27000, 24000 },
{ 74250000, 12288, 74250, 24000 },
{ 148500000, 12288, 148500, 24000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_176_4k[] = {
{ 25200000, 23520, 26250, 25000 },
{ 27000000, 23520, 28125, 25000 },
{ 74250000, 18816, 61875, 20000 },
{ 148500000, 18816, 123750, 20000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_192k[] = {
{ 25200000, 24576, 25200, 24000 },
{ 27000000, 24576, 27000, 24000 },
{ 74250000, 24576, 74250, 24000 },
{ 148500000, 24576, 148500, 24000 },
{ 0, 0, 0, 0 },
};
static const struct tmds_config tegra20_tmds_config[] = {
{ /* slow pixel clock modes */
.pclk = 27000000,
......@@ -418,52 +358,53 @@ static const struct tmds_config tegra124_tmds_config[] = {
},
};
static const struct tegra_hdmi_audio_config *
tegra_hdmi_get_audio_config(unsigned int sample_rate, unsigned int pclk)
static int
tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pix_clock,
struct tegra_hdmi_audio_config *config)
{
const struct tegra_hdmi_audio_config *table;
switch (sample_rate) {
case 32000:
table = tegra_hdmi_audio_32k;
break;
case 44100:
table = tegra_hdmi_audio_44_1k;
break;
case 48000:
table = tegra_hdmi_audio_48k;
break;
case 88200:
table = tegra_hdmi_audio_88_2k;
break;
case 96000:
table = tegra_hdmi_audio_96k;
break;
case 176400:
table = tegra_hdmi_audio_176_4k;
break;
case 192000:
table = tegra_hdmi_audio_192k;
break;
default:
return NULL;
}
while (table->pclk) {
if (table->pclk == pclk)
return table;
table++;
const unsigned int afreq = 128 * audio_freq;
const unsigned int min_n = afreq / 1500;
const unsigned int max_n = afreq / 300;
const unsigned int ideal_n = afreq / 1000;
int64_t min_err = (uint64_t)-1 >> 1;
unsigned int min_delta = -1;
int n;
memset(config, 0, sizeof(*config));
config->n = -1;
for (n = min_n; n <= max_n; n++) {
uint64_t cts_f, aval_f;
unsigned int delta;
int64_t cts, err;
/* compute aval in 48.16 fixed point */
aval_f = ((int64_t)24000000 << 16) * n;
do_div(aval_f, afreq);
/* It should round without any rest */
if (aval_f & 0xFFFF)
continue;
/* Compute cts in 48.16 fixed point */
cts_f = ((int64_t)pix_clock << 16) * n;
do_div(cts_f, afreq);
/* Round it to the nearest integer */
cts = (cts_f & ~0xFFFF) + ((cts_f & BIT(15)) << 1);
delta = abs(n - ideal_n);
/* Compute the absolute error */
err = abs((int64_t)cts_f - cts);
if (err < min_err || (err == min_err && delta < min_delta)) {
config->n = n;
config->cts = cts >> 16;
config->aval = aval_f >> 16;
min_delta = delta;
min_err = err;
}
}
return NULL;
return config->n != -1 ? 0 : -EINVAL;
}
static void tegra_hdmi_setup_audio_fs_tables(struct tegra_hdmi *hdmi)
......@@ -510,7 +451,7 @@ static void tegra_hdmi_write_aval(struct tegra_hdmi *hdmi, u32 value)
unsigned int i;
for (i = 0; i < ARRAY_SIZE(regs); i++) {
if (regs[i].sample_rate == hdmi->audio_sample_rate) {
if (regs[i].sample_rate == hdmi->format.sample_rate) {
tegra_hdmi_writel(hdmi, value, regs[i].offset);
break;
}
......@@ -519,8 +460,9 @@ static void tegra_hdmi_write_aval(struct tegra_hdmi *hdmi, u32 value)
static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi)
{
const struct tegra_hdmi_audio_config *config;
struct tegra_hdmi_audio_config config;
u32 source, value;
int err;
switch (hdmi->audio_source) {
case HDA:
......@@ -564,7 +506,7 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi)
* play back system startup sounds early. It is possibly not
* needed on Linux at all.
*/
if (hdmi->audio_channels == 2)
if (hdmi->format.channels == 2)
value = SOR_AUDIO_CNTRL0_INJECT_NULLSMPL;
else
value = 0;
......@@ -595,25 +537,28 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi)
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_SPARE0);
}
config = tegra_hdmi_get_audio_config(hdmi->audio_sample_rate,
hdmi->pixel_clock);
if (!config) {
err = tegra_hdmi_get_audio_config(hdmi->format.sample_rate,
hdmi->pixel_clock, &config);
if (err < 0) {
dev_err(hdmi->dev,
"cannot set audio to %u Hz at %u Hz pixel clock\n",
hdmi->audio_sample_rate, hdmi->pixel_clock);
return -EINVAL;
hdmi->format.sample_rate, hdmi->pixel_clock);
return err;
}
dev_dbg(hdmi->dev, "audio: pixclk=%u, n=%u, cts=%u, aval=%u\n",
hdmi->pixel_clock, config.n, config.cts, config.aval);
tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_HDMI_ACR_CTRL);
value = AUDIO_N_RESETF | AUDIO_N_GENERATE_ALTERNATE |
AUDIO_N_VALUE(config->n - 1);
AUDIO_N_VALUE(config.n - 1);
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N);
tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config->n) | ACR_ENABLE,
tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config.n) | ACR_ENABLE,
HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH);
tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config->cts),
tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config.cts),
HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW);
value = SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1);
......@@ -624,7 +569,7 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi)
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N);
if (hdmi->config->has_hda)
tegra_hdmi_write_aval(hdmi, config->aval);
tegra_hdmi_write_aval(hdmi, config.aval);
tegra_hdmi_setup_audio_fs_tables(hdmi);
......@@ -787,7 +732,7 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
return;
}
frame.channels = hdmi->audio_channels;
frame.channels = hdmi->format.channels;
err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
if (err < 0) {
......@@ -1589,24 +1534,6 @@ static const struct of_device_id tegra_hdmi_of_match[] = {
};
MODULE_DEVICE_TABLE(of, tegra_hdmi_of_match);
static void hda_format_parse(unsigned int format, unsigned int *rate,
unsigned int *channels)
{
unsigned int mul, div;
if (format & AC_FMT_BASE_44K)
*rate = 44100;
else
*rate = 48000;
mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT;
div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT;
*rate = *rate * (mul + 1) / (div + 1);
*channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT;
}
static irqreturn_t tegra_hdmi_irq(int irq, void *data)
{
struct tegra_hdmi *hdmi = data;
......@@ -1623,14 +1550,9 @@ static irqreturn_t tegra_hdmi_irq(int irq, void *data)
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0);
if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) {
unsigned int sample_rate, channels;
format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK;
hda_format_parse(format, &sample_rate, &channels);
hdmi->audio_sample_rate = sample_rate;
hdmi->audio_channels = channels;
tegra_hda_parse_format(format, &hdmi->format);
err = tegra_hdmi_setup_audio(hdmi);
if (err < 0) {
......@@ -1664,8 +1586,6 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
hdmi->dev = &pdev->dev;
hdmi->audio_source = AUTO;
hdmi->audio_sample_rate = 48000;
hdmi->audio_channels = 2;
hdmi->stereo = false;
hdmi->dvi = false;
......@@ -1709,10 +1629,6 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
return PTR_ERR(hdmi->vdd);
}
hdmi->output.notifier = cec_notifier_get(&pdev->dev);
if (hdmi->output.notifier == NULL)
return -ENOMEM;
hdmi->output.dev = &pdev->dev;
err = tegra_output_probe(&hdmi->output);
......@@ -1771,9 +1687,6 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
tegra_output_remove(&hdmi->output);
if (hdmi->output.notifier)
cec_notifier_put(hdmi->output.notifier);
return 0;
}
......
......@@ -36,7 +36,7 @@ int tegra_output_connector_get_modes(struct drm_connector *connector)
else if (output->ddc)
edid = drm_get_edid(connector, output->ddc);
cec_notifier_set_phys_addr_from_edid(output->notifier, edid);
cec_notifier_set_phys_addr_from_edid(output->cec, edid);
drm_connector_update_edid_property(connector, edid);
if (edid) {
......@@ -73,7 +73,7 @@ tegra_output_connector_detect(struct drm_connector *connector, bool force)
}
if (status != connector_status_connected)
cec_notifier_phys_addr_invalidate(output->notifier);
cec_notifier_phys_addr_invalidate(output->cec);
return status;
}
......@@ -174,11 +174,18 @@ int tegra_output_probe(struct tegra_output *output)
disable_irq(output->hpd_irq);
}
output->cec = cec_notifier_get(output->dev);
if (!output->cec)
return -ENOMEM;
return 0;
}
void tegra_output_remove(struct tegra_output *output)
{
if (output->cec)
cec_notifier_put(output->cec);
if (gpio_is_valid(output->hpd_gpio)) {
free_irq(output->hpd_irq, output);
gpio_free(output->hpd_gpio);
......
......@@ -19,8 +19,6 @@
#include <soc/tegra/pmc.h>
#include <sound/hda_verbs.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_panel.h>
......@@ -28,6 +26,7 @@
#include "dc.h"
#include "drm.h"
#include "hda.h"
#include "sor.h"
#include "trace.h"
......@@ -429,10 +428,7 @@ struct tegra_sor {
struct delayed_work scdc;
bool scdc_enabled;
struct {
unsigned int sample_rate;
unsigned int channels;
} audio;
struct tegra_hda_format format;
};
struct tegra_sor_state {
......@@ -2185,7 +2181,7 @@ static int tegra_sor_hdmi_enable_audio_infoframe(struct tegra_sor *sor)
return err;
}
frame.channels = sor->audio.channels;
frame.channels = sor->format.channels;
err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
if (err < 0) {
......@@ -2214,7 +2210,7 @@ static void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor)
value |= SOR_AUDIO_CNTRL_SOURCE_SELECT(SOURCE_SELECT_HDA);
/* inject null samples */
if (sor->audio.channels != 2)
if (sor->format.channels != 2)
value &= ~SOR_AUDIO_CNTRL_INJECT_NULLSMPL;
else
value |= SOR_AUDIO_CNTRL_INJECT_NULLSMPL;
......@@ -2245,7 +2241,7 @@ static void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor)
value = SOR_HDMI_AUDIO_N_RESET | SOR_HDMI_AUDIO_N_LOOKUP;
tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_N);
value = (24000 * 4096) / (128 * sor->audio.sample_rate / 1000);
value = (24000 * 4096) / (128 * sor->format.sample_rate / 1000);
tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0320);
tegra_sor_writel(sor, 4096, SOR_AUDIO_NVAL_0320);
......@@ -2258,15 +2254,15 @@ static void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor)
tegra_sor_writel(sor, 20000, SOR_AUDIO_AVAL_1764);
tegra_sor_writel(sor, 18816, SOR_AUDIO_NVAL_1764);
value = (24000 * 6144) / (128 * sor->audio.sample_rate / 1000);
value = (24000 * 6144) / (128 * sor->format.sample_rate / 1000);
tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0480);
tegra_sor_writel(sor, 6144, SOR_AUDIO_NVAL_0480);
value = (24000 * 12288) / (128 * sor->audio.sample_rate / 1000);
value = (24000 * 12288) / (128 * sor->format.sample_rate / 1000);
tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0960);
tegra_sor_writel(sor, 12288, SOR_AUDIO_NVAL_0960);
value = (24000 * 24576) / (128 * sor->audio.sample_rate / 1000);
value = (24000 * 24576) / (128 * sor->format.sample_rate / 1000);
tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_1920);
tegra_sor_writel(sor, 24576, SOR_AUDIO_NVAL_1920);
......@@ -3195,24 +3191,6 @@ static int tegra_sor_parse_dt(struct tegra_sor *sor)
return 0;
}
static void tegra_hda_parse_format(unsigned int format, unsigned int *rate,
unsigned int *channels)
{
unsigned int mul, div;
if (format & AC_FMT_BASE_44K)
*rate = 44100;
else
*rate = 48000;
mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT;
div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT;
*rate = *rate * (mul + 1) / (div + 1);
*channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT;
}
static irqreturn_t tegra_sor_irq(int irq, void *data)
{
struct tegra_sor *sor = data;
......@@ -3225,14 +3203,11 @@ static irqreturn_t tegra_sor_irq(int irq, void *data)
value = tegra_sor_readl(sor, SOR_AUDIO_HDA_CODEC_SCRATCH0);
if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) {
unsigned int format, sample_rate, channels;
unsigned int format;
format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK;
tegra_hda_parse_format(format, &sample_rate, &channels);
sor->audio.sample_rate = sample_rate;
sor->audio.channels = channels;
tegra_hda_parse_format(format, &sor->format);
tegra_sor_hdmi_audio_enable(sor);
} else {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment