blob: 05cfe89cafe213eb6f881d2763fc320e26f053b2 [file] [log] [blame]
/*
* Copyright 2018 NXP
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/component.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/irq.h>
#include <linux/of_device.h>
#include <sound/hdmi-codec.h>
#include "imx-hdp.h"
#include "imx-hdmi.h"
#include "imx-dp.h"
#include "../imx-drm.h"
static u32 TMDS_rate_table[7] = {
25200, 27000, 54000, 74250, 148500, 297000, 594000,
};
static u32 N_table_32k[8] = {
/*25200, 27000, 54000, 74250, 148500, 297000, 594000,*/
4096, 4096, 4096, 4096, 4096, 3072, 3072, 4096,
};
static u32 N_table_44k[8] = {
6272, 6272, 6272, 6272, 6272, 4704, 9408, 6272,
};
static u32 N_table_48k[8] = {
6144, 6144, 6144, 6144, 6144, 5120, 6144, 6144,
};
static int select_N_index(u32 pclk)
{
int i = 0;
for (i = 0; i < 7; i++) {
if (pclk == TMDS_rate_table[i])
break;
}
if (i == 7)
DRM_WARN("pclkc %d is not supported!\n", pclk);
return i;
}
static u32 imx_hdp_audio(struct imx_hdp *hdmi, AUDIO_TYPE type, u32 sample_rate, u32 channels, u32 width)
{
AUDIO_FREQ freq;
AUDIO_WIDTH bits;
int ncts_n;
state_struct *state = &hdmi->state;
int idx_n = select_N_index(hdmi->video.cur_mode.clock);
switch (sample_rate) {
case 32000:
freq = AUDIO_FREQ_32;
ncts_n = N_table_32k[idx_n];
break;
case 44100:
freq = AUDIO_FREQ_44_1;
ncts_n = N_table_44k[idx_n];
break;
case 48000:
freq = AUDIO_FREQ_48;
ncts_n = N_table_48k[idx_n];
break;
case 88200:
freq = AUDIO_FREQ_88_2;
ncts_n = N_table_44k[idx_n] * 2;
break;
case 96000:
freq = AUDIO_FREQ_96;
ncts_n = N_table_48k[idx_n] * 2;
break;
case 176400:
freq = AUDIO_FREQ_176_4;
ncts_n = N_table_44k[idx_n] * 4;
break;
case 192000:
freq = AUDIO_FREQ_192;
ncts_n = N_table_48k[idx_n] * 4;
break;
default:
return -EINVAL;
}
switch (width) {
case 16:
bits = AUDIO_WIDTH_16;
break;
case 24:
bits = AUDIO_WIDTH_24;
break;
case 32:
bits = AUDIO_WIDTH_32;
break;
default:
return -EINVAL;
}
CDN_API_AudioOff_blocking(state, type);
CDN_API_AudioAutoConfig_blocking(state,
type,
channels,
freq,
0,
bits,
hdmi->audio_type,
ncts_n,
AUDIO_MUTE_MODE_UNMUTE);
return 0;
}
/*
* HDMI audio codec callbacks
*/
static int imx_hdp_audio_hw_params(struct device *dev, void *data,
struct hdmi_codec_daifmt *daifmt,
struct hdmi_codec_params *params)
{
struct imx_hdp *hdmi = dev_get_drvdata(dev);
unsigned int chan = params->cea.channels;
dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
params->sample_rate, params->sample_width, chan);
if (!hdmi->bridge.encoder)
return -ENODEV;
if (daifmt->fmt == HDMI_I2S)
imx_hdp_audio(hdmi,
AUDIO_TYPE_I2S,
params->sample_rate,
chan,
params->sample_width);
else if (daifmt->fmt == HDMI_SPDIF)
imx_hdp_audio(hdmi,
AUDIO_TYPE_SPIDIF_EXTERNAL,
params->sample_rate,
chan,
params->sample_width);
else
return -EINVAL;
return 0;
}
static void imx_hdp_audio_shutdown(struct device *dev, void *data)
{
}
static int imx_hdp_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len)
{
struct imx_hdp *hdmi = dev_get_drvdata(dev);
memcpy(buf, hdmi->connector.eld, min(sizeof(hdmi->connector.eld), len));
return 0;
}
static const struct hdmi_codec_ops imx_hdp_audio_codec_ops = {
.hw_params = imx_hdp_audio_hw_params,
.audio_shutdown = imx_hdp_audio_shutdown,
.get_eld = imx_hdp_audio_get_eld,
};
void imx_hdp_register_audio_driver(struct device *dev)
{
struct hdmi_codec_pdata codec_data = {
.ops = &imx_hdp_audio_codec_ops,
.max_i2s_channels = 8,
.i2s = 1,
};
struct platform_device *pdev;
pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
PLATFORM_DEVID_NONE, &codec_data,
sizeof(codec_data));
if (IS_ERR(pdev))
return;
DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME);
}