3
0
mirror of https://github.com/Qortal/Brooklyn.git synced 2025-01-30 14:52:17 +00:00

GPU optimizations

Clock, encoder and decoder got fixes. Now the GPU can handle voltage spikes better for Russcade.
This commit is contained in:
Scare Crowe 2021-06-26 23:04:58 +05:00
parent 54b7936165
commit 9fb43c2ca9
15 changed files with 477 additions and 175 deletions

View File

@ -359,7 +359,7 @@ hdmi0: hdmi@7ef00700 {
interrupt-names = "cec-tx", "cec-rx", "cec-low",
"wakeup", "hpd-connected", "hpd-removed";
ddc = <&ddc0>;
dmas = <&dma 10>;
dmas = <&dma (10 | (1 << 27) | (1 << 24)| (15 << 20) | (10 << 16))>;
dma-names = "audio-rx";
status = "disabled";
};
@ -406,7 +406,7 @@ hdmi1: hdmi@7ef05700 {
<9>, <10>, <11>;
interrupt-names = "cec-tx", "cec-rx", "cec-low",
"wakeup", "hpd-connected", "hpd-removed";
dmas = <&dma 17>;
dmas = <&dma (17 | (1 << 27) | (1 << 24)| (15 << 20) | (10 << 16))>;
dma-names = "audio-rx";
status = "disabled";
};

View File

@ -126,7 +126,7 @@ boardid: gpio@3a {
compatible = "nxp,pca8574";
reg = <0x3a>;
gpio-controller;
#gpio-cells = <1>;
#gpio-cells = <2>;
};
};

View File

@ -1318,6 +1318,7 @@ CONFIG_R8188EU=m
CONFIG_VT6656=m
CONFIG_STAGING_MEDIA=y
CONFIG_VIDEO_RPIVID=m
CONFIG_ASHMEM=y
CONFIG_FB_TFT=m
CONFIG_FB_TFT_AGM1264K_FL=m
CONFIG_FB_TFT_BD663474=m
@ -1384,6 +1385,9 @@ CONFIG_MAX31856=m
CONFIG_PWM_BCM2835=m
CONFIG_PWM_PCA9685=m
CONFIG_RPI_AXIPERF=m
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_BINDERFS=y
CONFIG_NVMEM_RMEM=m
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y

View File

@ -1175,6 +1175,7 @@ CONFIG_R8712U=m
CONFIG_R8188EU=m
CONFIG_VT6656=m
CONFIG_STAGING_MEDIA=y
CONFIG_ASHMEM=y
CONFIG_FB_TFT=m
CONFIG_FB_TFT_AGM1264K_FL=m
CONFIG_FB_TFT_BD663474=m
@ -1234,6 +1235,9 @@ CONFIG_MAXIM_THERMOCOUPLE=m
CONFIG_MAX31856=m
CONFIG_PWM_BCM2835=m
CONFIG_PWM_PCA9685=m
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_BINDERFS=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y

View File

@ -279,27 +279,15 @@ static u32 vc4_crtc_get_fifo_full_level_bits(struct vc4_crtc *vc4_crtc,
* allows drivers to push pixels to more than one encoder from the
* same CRTC.
*/
static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
struct drm_atomic_state *state,
struct drm_connector_state *(*get_state)(struct drm_atomic_state *state,
struct drm_connector *connector))
struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
struct drm_encoder *encoder;
drm_connector_list_iter_begin(crtc->dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter) {
struct drm_connector_state *conn_state = get_state(state, connector);
WARN_ON(hweight32(state->encoder_mask) > 1);
if (!conn_state)
continue;
if (conn_state->crtc == crtc) {
drm_connector_list_iter_end(&conn_iter);
return connector->encoder;
}
}
drm_connector_list_iter_end(&conn_iter);
drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask)
return encoder;
return NULL;
}
@ -313,12 +301,11 @@ static void vc4_crtc_pixelvalve_reset(struct drm_crtc *crtc)
CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_FIFO_CLR);
}
static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_atomic_state *state)
static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state,
drm_atomic_get_new_connector_state);
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc);
@ -535,10 +522,12 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
crtc);
struct vc4_crtc_state *old_vc4_state = to_vc4_crtc_state(old_state);
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state,
drm_atomic_get_old_connector_state);
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, old_state);
struct drm_device *dev = crtc->dev;
drm_dbg(dev, "Disabling CRTC %s (%u) connected to Encoder %s (%u)",
crtc->name, crtc->base.id, encoder->name, encoder->base.id);
require_hvs_enabled(dev);
/* Disable vblank irq handling before crtc is disabled. */
@ -563,12 +552,16 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state,
crtc);
struct drm_device *dev = crtc->dev;
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state,
drm_atomic_get_new_connector_state);
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, new_state);
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
drm_dbg(dev, "Enabling CRTC %s (%u) connected to Encoder %s (%u)",
crtc->name, crtc->base.id, encoder->name, encoder->base.id);
require_hvs_enabled(dev);
/* Enable vblank irq handling before crtc is started otherwise
@ -581,7 +574,7 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
if (vc4_encoder->pre_crtc_configure)
vc4_encoder->pre_crtc_configure(encoder, state);
vc4_crtc_config_pv(crtc, state);
vc4_crtc_config_pv(crtc, encoder, state);
CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_EN);
@ -650,12 +643,27 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
struct drm_connector *conn;
struct drm_connector_state *conn_state;
struct drm_encoder *encoder;
int ret, i;
ret = vc4_hvs_atomic_check(crtc, state);
if (ret)
return ret;
encoder = vc4_get_crtc_encoder(crtc, crtc_state);
if (encoder) {
const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
mode = &crtc_state->adjusted_mode;
if (vc4_encoder->type == VC4_ENCODER_TYPE_HDMI0) {
vc4_state->hvs_load = max(mode->clock * mode->hdisplay / mode->htotal + 1000,
mode->clock * 9 / 10) * 1000;
} else {
vc4_state->hvs_load = mode->clock * 1000;
}
}
for_each_new_connector_in_state(state, conn, conn_state,
i) {
if (conn_state->crtc != crtc)

View File

@ -7,6 +7,7 @@
#include <linux/circ_buf.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include "vc4_drv.h"
#include "vc4_regs.h"
@ -26,8 +27,10 @@ vc4_debugfs_init(struct drm_minor *minor)
struct vc4_dev *vc4 = to_vc4_dev(minor->dev);
struct vc4_debugfs_info_entry *entry;
debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
minor->debugfs_root, &vc4->load_tracker_enabled);
if (!of_device_is_compatible(vc4->hvs->pdev->dev.of_node,
"brcm,bcm2711-vc5"))
debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
minor->debugfs_root, &vc4->load_tracker_enabled);
list_for_each_entry(entry, &vc4->debugfs_list, link) {
drm_debugfs_create_files(&entry->info, 1,

View File

@ -205,9 +205,6 @@ struct vc4_dev {
int power_refcount;
/* Set to true when the load tracker is supported. */
bool load_tracker_available;
/* Set to true when the load tracker is active. */
bool load_tracker_enabled;
@ -327,6 +324,7 @@ struct vc4_hvs {
u32 __iomem *dlist;
struct clk *core_clk;
struct clk_request *core_req;
/* Memory manager for CRTCs to allocate space in the display
* list. Units are dwords.
@ -520,6 +518,9 @@ vc4_crtc_to_vc4_pv_data(const struct vc4_crtc *crtc)
return container_of(data, struct vc4_pv_data, base);
}
struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
struct drm_crtc_state *state);
struct vc4_crtc_state {
struct drm_crtc_state base;
/* Dlist area for this CRTC configuration. */
@ -535,6 +536,8 @@ struct vc4_crtc_state {
unsigned int bottom;
} margins;
unsigned long hvs_load;
/* Transitional state below, only valid during atomic commits */
bool update_muxing;
};

View File

@ -39,9 +39,11 @@ static struct vc4_ctm_state *to_vc4_ctm_state(struct drm_private_state *priv)
struct vc4_hvs_state {
struct drm_private_state base;
unsigned long core_clock_rate;
struct {
unsigned in_use: 1;
unsigned long fifo_load;
struct drm_crtc_commit *pending_commit;
} fifo_state[HVS_NUM_CHANNELS];
};
@ -342,11 +344,20 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
struct vc4_hvs *hvs = vc4->hvs;
struct drm_crtc_state *old_crtc_state;
struct drm_crtc_state *new_crtc_state;
struct vc4_hvs_state *new_hvs_state;
struct drm_crtc *crtc;
struct vc4_hvs_state *old_hvs_state;
struct clk_request *core_req;
int i;
old_hvs_state = vc4_hvs_get_old_global_state(state);
if (WARN_ON(!old_hvs_state))
return;
new_hvs_state = vc4_hvs_get_new_global_state(state);
if (WARN_ON(!new_hvs_state))
return;
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
struct vc4_crtc_state *vc4_crtc_state;
@ -357,12 +368,19 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel);
}
if (vc4->hvs && vc4->hvs->hvs5)
if (vc4->hvs && vc4->hvs->hvs5) {
/*
* Do a temporary request on the core clock during the
* modeset.
*/
core_req = clk_request_start(hvs->core_clk, 500000000);
old_hvs_state = vc4_hvs_get_old_global_state(state);
if (!old_hvs_state)
return;
/*
* And remove the previous one based on the HVS
* requirements if any.
*/
clk_request_done(hvs->core_req);
}
for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
struct vc4_crtc_state *vc4_crtc_state =
@ -413,8 +431,20 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
drm_atomic_helper_cleanup_planes(dev, state);
if (vc4->hvs && vc4->hvs->hvs5)
if (vc4->hvs && vc4->hvs->hvs5) {
drm_dbg(dev, "Running the core clock at %lu Hz\n",
new_hvs_state->core_clock_rate);
/*
* Request a clock rate based on the current HVS
* requirements.
*/
hvs->core_req = clk_request_start(hvs->core_clk,
new_hvs_state->core_clock_rate);
/* And drop the temporary request */
clk_request_done(core_req);
}
}
static int vc4_atomic_commit_setup(struct drm_atomic_state *state)
@ -576,9 +606,6 @@ static int vc4_load_tracker_atomic_check(struct drm_atomic_state *state)
struct drm_plane *plane;
int i;
if (!vc4->load_tracker_available)
return 0;
priv_state = drm_atomic_get_private_obj_state(state,
&vc4->load_tracker);
if (IS_ERR(priv_state))
@ -653,9 +680,6 @@ static void vc4_load_tracker_obj_fini(struct drm_device *dev, void *unused)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
if (!vc4->load_tracker_available)
return;
drm_atomic_private_obj_fini(&vc4->load_tracker);
}
@ -663,9 +687,6 @@ static int vc4_load_tracker_obj_init(struct vc4_dev *vc4)
{
struct vc4_load_tracker_state *load_state;
if (!vc4->load_tracker_available)
return 0;
load_state = kzalloc(sizeof(*load_state), GFP_KERNEL);
if (!load_state)
return -ENOMEM;
@ -690,9 +711,9 @@ vc4_hvs_channels_duplicate_state(struct drm_private_obj *obj)
__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
for (i = 0; i < HVS_NUM_CHANNELS; i++) {
state->fifo_state[i].in_use = old_state->fifo_state[i].in_use;
state->fifo_state[i].fifo_load = old_state->fifo_state[i].fifo_load;
if (!old_state->fifo_state[i].pending_commit)
continue;
@ -701,6 +722,9 @@ vc4_hvs_channels_duplicate_state(struct drm_private_obj *obj)
drm_crtc_commit_get(old_state->fifo_state[i].pending_commit);
}
state->core_clock_rate = old_state->core_clock_rate;
return &state->base;
}
@ -859,6 +883,76 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
return 0;
}
static int
vc4_core_clock_atomic_check(struct drm_atomic_state *state)
{
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
struct drm_private_state *priv_state;
struct vc4_hvs_state *hvs_new_state;
struct vc4_load_tracker_state *load_state;
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
struct drm_crtc *crtc;
unsigned int num_outputs;
unsigned long pixel_rate;
unsigned long cob_rate;
unsigned int i;
priv_state = drm_atomic_get_private_obj_state(state,
&vc4->load_tracker);
if (IS_ERR(priv_state))
return PTR_ERR(priv_state);
load_state = to_vc4_load_tracker_state(priv_state);
hvs_new_state = vc4_hvs_get_global_state(state);
if (!hvs_new_state)
return -EINVAL;
for_each_oldnew_crtc_in_state(state, crtc,
old_crtc_state,
new_crtc_state,
i) {
if (old_crtc_state->active) {
struct vc4_crtc_state *old_vc4_state =
to_vc4_crtc_state(old_crtc_state);
unsigned int channel = old_vc4_state->assigned_channel;
hvs_new_state->fifo_state[channel].fifo_load = 0;
}
if (new_crtc_state->active) {
struct vc4_crtc_state *new_vc4_state =
to_vc4_crtc_state(new_crtc_state);
unsigned int channel = new_vc4_state->assigned_channel;
hvs_new_state->fifo_state[channel].fifo_load =
new_vc4_state->hvs_load;
}
}
cob_rate = 0;
num_outputs = 0;
for (i = 0; i < HVS_NUM_CHANNELS; i++) {
if (!hvs_new_state->fifo_state[i].in_use)
continue;
num_outputs++;
cob_rate += hvs_new_state->fifo_state[i].fifo_load;
}
pixel_rate = load_state->hvs_load;
if (num_outputs > 1) {
pixel_rate = (pixel_rate * 40) / 100;
} else {
pixel_rate = (pixel_rate * 60) / 100;
}
hvs_new_state->core_clock_rate = max(cob_rate, pixel_rate);
return 0;
}
static int
vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
{
@ -876,7 +970,11 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
if (ret)
return ret;
return vc4_load_tracker_atomic_check(state);
ret = vc4_load_tracker_atomic_check(state);
if (ret)
return ret;
return vc4_core_clock_atomic_check(state);
}
static struct drm_mode_config_helper_funcs vc4_mode_config_helpers = {
@ -897,9 +995,12 @@ int vc4_kms_load(struct drm_device *dev)
"brcm,bcm2711-vc5");
int ret;
/*
* The limits enforced by the load tracker aren't relevant for
* the BCM2711, but the load tracker computations are used for
* the core clock rate calculation.
*/
if (!is_vc5) {
vc4->load_tracker_available = true;
/* Start with the load tracker enabled. Can be
* disabled through the debugfs load_tracker file.
*/

View File

@ -534,9 +534,6 @@ static void vc4_plane_calc_load(struct drm_plane_state *state)
struct vc4_dev *vc4;
vc4 = to_vc4_dev(state->plane->dev);
if (!vc4->load_tracker_available)
return;
vc4_state = to_vc4_plane_state(state);
crtc_state = drm_atomic_get_existing_crtc_state(state->state,
state->crtc);

View File

@ -65,12 +65,22 @@ struct hdmi_codec_ops {
/*
* Configures HDMI-encoder for audio stream.
* Mandatory
* Having either prepare or hw_params is mandatory.
*/
int (*hw_params)(struct device *dev, void *data,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms);
/*
* Configures HDMI-encoder for audio stream. Can be called
* multiple times for each setup.
*
* Having either prepare or hw_params is mandatory.
*/
int (*prepare)(struct device *dev, void *data,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms);
/*
* Shuts down the audio stream.
* Mandatory

View File

@ -4,6 +4,14 @@
#include <linux/types.h>
int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len);
int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len);
int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
u8 *cs, size_t len);
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len);

View File

@ -1,34 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/* Header file for iptables ipt_ECN target
*
* (C) 2002 by Harald Welte <laforge@gnumonks.org>
*
* This software is distributed under GNU GPL v2, 1991
*
* ipt_ECN.h,v 1.3 2002/05/29 12:17:40 laforge Exp
*/
#ifndef _IPT_ECN_TARGET_H
#define _IPT_ECN_TARGET_H
#ifndef _IPT_ECN_H
#define _IPT_ECN_H
#include <linux/types.h>
#include <linux/netfilter/xt_DSCP.h>
#include <linux/netfilter/xt_ecn.h>
#define ipt_ecn_info xt_ecn_info
#define IPT_ECN_IP_MASK (~XT_DSCP_MASK)
#define IPT_ECN_OP_SET_IP 0x01 /* set ECN bits of IPv4 header */
#define IPT_ECN_OP_SET_ECE 0x10 /* set ECE bit of TCP header */
#define IPT_ECN_OP_SET_CWR 0x20 /* set CWR bit of TCP header */
#define IPT_ECN_OP_MASK 0xce
struct ipt_ECN_info {
__u8 operation; /* bitset of operations */
__u8 ip_ect; /* ECT codepoint of IPv4 header, pre-shifted */
union {
struct {
__u8 ece:1, cwr:1; /* TCP ECT bits */
} tcp;
} proto;
enum {
IPT_ECN_IP_MASK = XT_ECN_IP_MASK,
IPT_ECN_OP_MATCH_IP = XT_ECN_OP_MATCH_IP,
IPT_ECN_OP_MATCH_ECE = XT_ECN_OP_MATCH_ECE,
IPT_ECN_OP_MATCH_CWR = XT_ECN_OP_MATCH_CWR,
IPT_ECN_OP_MATCH_MASK = XT_ECN_OP_MATCH_MASK,
};
#endif /* _IPT_ECN_TARGET_H */
#endif /* IPT_ECN_H */

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/* IP tables module for matching the value of the TTL
* (C) 2000 by Harald Welte <laforge@gnumonks.org> */
/* TTL modification module for IP tables
* (C) 2000 by Harald Welte <laforge@netfilter.org> */
#ifndef _IPT_TTL_H
#define _IPT_TTL_H
@ -8,14 +8,14 @@
#include <linux/types.h>
enum {
IPT_TTL_EQ = 0, /* equals */
IPT_TTL_NE, /* not equals */
IPT_TTL_LT, /* less than */
IPT_TTL_GT, /* greater than */
IPT_TTL_SET = 0,
IPT_TTL_INC,
IPT_TTL_DEC
};
#define IPT_TTL_MAXMODE IPT_TTL_DEC
struct ipt_ttl_info {
struct ipt_TTL_info {
__u8 mode;
__u8 ttl;
};

View File

@ -9,41 +9,68 @@
#include <sound/pcm_params.h>
#include <sound/pcm_iec958.h>
static int create_iec958_consumer(uint rate, uint sample_width,
u8 *cs, size_t len)
int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len)
{
unsigned int fs, ws;
if (len < 4)
return -EINVAL;
switch (rate) {
case 32000:
fs = IEC958_AES3_CON_FS_32000;
break;
case 44100:
fs = IEC958_AES3_CON_FS_44100;
break;
case 48000:
fs = IEC958_AES3_CON_FS_48000;
break;
case 88200:
fs = IEC958_AES3_CON_FS_88200;
break;
case 96000:
fs = IEC958_AES3_CON_FS_96000;
break;
case 176400:
fs = IEC958_AES3_CON_FS_176400;
break;
case 192000:
fs = IEC958_AES3_CON_FS_192000;
break;
default:
memset(cs, 0, len);
cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
cs[1] = IEC958_AES1_CON_GENERAL;
cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | IEC958_AES3_CON_FS_NOTID;
if (len > 4)
cs[4] = IEC958_AES4_CON_WORDLEN_NOTID;
return len;
}
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_default);
static int fill_iec958_consumer(uint rate, uint sample_width,
u8 *cs, size_t len)
{
if (len < 4)
return -EINVAL;
if ((cs[3] & IEC958_AES3_CON_FS) == IEC958_AES3_CON_FS_NOTID) {
unsigned int fs;
switch (rate) {
case 32000:
fs = IEC958_AES3_CON_FS_32000;
break;
case 44100:
fs = IEC958_AES3_CON_FS_44100;
break;
case 48000:
fs = IEC958_AES3_CON_FS_48000;
break;
case 88200:
fs = IEC958_AES3_CON_FS_88200;
break;
case 96000:
fs = IEC958_AES3_CON_FS_96000;
break;
case 176400:
fs = IEC958_AES3_CON_FS_176400;
break;
case 192000:
fs = IEC958_AES3_CON_FS_192000;
break;
default:
return -EINVAL;
}
cs[3] &= ~IEC958_AES3_CON_FS;
cs[3] |= fs;
}
if (len > 4) {
if (len > 4 &&
(cs[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) {
unsigned int ws;
switch (sample_width) {
case 16:
ws = IEC958_AES4_CON_WORDLEN_20_16;
@ -64,21 +91,30 @@ static int create_iec958_consumer(uint rate, uint sample_width,
default:
return -EINVAL;
}
cs[4] &= ~IEC958_AES4_CON_WORDLEN;
cs[4] |= ws;
}
memset(cs, 0, len);
cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
cs[1] = IEC958_AES1_CON_GENERAL;
cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | fs;
if (len > 4)
cs[4] = ws;
return len;
}
int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
u8 *cs, size_t len)
{
return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
}
EXPORT_SYMBOL(snd_pcm_fill_iec958_consumer_hw_params);
int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime,
u8 *cs, size_t len)
{
return fill_iec958_consumer(runtime->rate,
snd_pcm_format_width(runtime->format),
cs, len);
}
EXPORT_SYMBOL(snd_pcm_fill_iec958_consumer);
/**
* snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
* @runtime: pcm runtime structure with ->rate filled in
@ -95,9 +131,13 @@ static int create_iec958_consumer(uint rate, uint sample_width,
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len)
{
return create_iec958_consumer(runtime->rate,
snd_pcm_format_width(runtime->format),
cs, len);
int ret;
ret = snd_pcm_create_iec958_consumer_default(cs, len);
if (ret < 0)
return ret;
return snd_pcm_fill_iec958_consumer(runtime, cs, len);
}
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
@ -117,7 +157,12 @@ EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
u8 *cs, size_t len)
{
return create_iec958_consumer(params_rate(params), params_width(params),
cs, len);
int ret;
ret = snd_pcm_create_iec958_consumer_default(cs, len);
if (ret < 0)
return ret;
return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
}
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);

View File

@ -278,6 +278,7 @@ struct hdmi_codec_priv {
bool busy;
struct snd_soc_jack *jack;
unsigned int jack_status;
u8 iec_status[5];
};
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
@ -386,6 +387,47 @@ static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol,
return 0;
}
static int hdmi_codec_iec958_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
static int hdmi_codec_iec958_default_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
memcpy(ucontrol->value.iec958.status, hcp->iec_status,
sizeof(hcp->iec_status));
return 0;
}
static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
memcpy(hcp->iec_status, ucontrol->value.iec958.status,
sizeof(hcp->iec_status));
return 0;
}
static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
memset(ucontrol->value.iec958.status, 0xff,
sizeof_field(struct hdmi_codec_priv, iec_status));
return 0;
}
static int hdmi_codec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@ -440,6 +482,42 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
mutex_unlock(&hcp->lock);
}
static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai,
unsigned int sample_width,
unsigned int sample_rate,
unsigned int channels,
struct hdmi_codec_params *hp)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
int idx;
/* Select a channel allocation that matches with ELD and pcm channels */
idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
if (idx < 0) {
dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
idx);
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
return idx;
}
memset(hp, 0, sizeof(*hp));
hdmi_audio_infoframe_init(&hp->cea);
hp->cea.channels = channels;
hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
hp->sample_width = sample_width;
hp->sample_rate = sample_rate;
hp->channels = channels;
hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
return 0;
}
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@ -454,46 +532,73 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
.dig_subframe = { 0 },
}
};
int ret, idx;
int ret;
if (!hcp->hcd.ops->hw_params)
return 0;
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
params_width(params), params_rate(params),
params_channels(params));
ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status,
sizeof(hp.iec.status));
ret = hdmi_codec_fill_codec_params(dai,
params_width(params),
params_rate(params),
params_channels(params),
&hp);
if (ret < 0)
return ret;
memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status,
sizeof(hp.iec.status));
if (ret < 0) {
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
ret);
return ret;
}
hdmi_audio_infoframe_init(&hp.cea);
hp.cea.channels = params_channels(params);
hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
/* Select a channel allocation that matches with ELD and pcm channels */
idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels);
if (idx < 0) {
dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
idx);
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
return idx;
}
hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
hp.sample_width = params_width(params);
hp.sample_rate = params_rate(params);
hp.channels = params_channels(params);
cf->bit_fmt = params_format(params);
return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
cf, &hp);
}
static int hdmi_codec_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
struct hdmi_codec_daifmt *cf = dai->playback_dma_data;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int channels = runtime->channels;
unsigned int width = snd_pcm_format_width(runtime->format);
unsigned int rate = runtime->rate;
struct hdmi_codec_params hp;
int ret;
if (!hcp->hcd.ops->prepare)
return 0;
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
width, rate, channels);
ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp);
if (ret < 0)
return ret;
memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status,
sizeof(hp.iec.status));
if (ret < 0) {
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
ret);
return ret;
}
cf->bit_fmt = runtime->format;
return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data,
cf, &hp);
}
static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
@ -585,6 +690,7 @@ static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
.startup = hdmi_codec_startup,
.shutdown = hdmi_codec_shutdown,
.hw_params = hdmi_codec_hw_params,
.prepare = hdmi_codec_prepare,
.set_fmt = hdmi_codec_i2s_set_fmt,
.mute_stream = hdmi_codec_mute,
};
@ -621,21 +727,37 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
struct snd_kcontrol_new hdmi_codec_controls[] = {
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
.info = hdmi_codec_iec958_info,
.get = hdmi_codec_iec958_mask_get,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
.info = hdmi_codec_iec958_info,
.get = hdmi_codec_iec958_default_get,
.put = hdmi_codec_iec958_default_put,
},
{
.access = (SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE),
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "ELD",
.info = hdmi_eld_ctl_info,
.get = hdmi_eld_ctl_get,
},
};
static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai)
{
struct snd_soc_dai_driver *drv = dai->driver;
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
struct snd_kcontrol *kctl;
struct snd_kcontrol_new hdmi_eld_ctl = {
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "ELD",
.info = hdmi_eld_ctl_info,
.get = hdmi_eld_ctl_get,
.device = rtd->pcm->device,
};
unsigned int i;
int ret;
ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
@ -652,12 +774,21 @@ static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
/* add ELD ctl with the device number corresponding to the PCM stream */
kctl = snd_ctl_new1(&hdmi_eld_ctl, dai->component);
if (!kctl)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(hdmi_codec_controls); i++) {
struct snd_kcontrol *kctl;
return snd_ctl_add(rtd->card->snd_card, kctl);
/* add ELD ctl with the device number corresponding to the PCM stream */
kctl = snd_ctl_new1(&hdmi_codec_controls[i], dai->component);
if (!kctl)
return -ENOMEM;
kctl->id.device = rtd->pcm->device;
ret = snd_ctl_add(rtd->card->snd_card, kctl);
if (ret < 0)
return ret;
}
return 0;
}
static int hdmi_dai_probe(struct snd_soc_dai *dai)
@ -850,7 +981,8 @@ static int hdmi_codec_probe(struct platform_device *pdev)
}
dai_count = hcd->i2s + hcd->spdif;
if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params ||
if (dai_count < 1 || !hcd->ops ||
(!hcd->ops->hw_params && !hcd->ops->prepare) ||
!hcd->ops->audio_shutdown) {
dev_err(dev, "%s: Invalid parameters\n", __func__);
return -EINVAL;
@ -863,6 +995,11 @@ static int hdmi_codec_probe(struct platform_device *pdev)
hcp->hcd = *hcd;
mutex_init(&hcp->lock);
ret = snd_pcm_create_iec958_consumer_default(hcp->iec_status,
sizeof(hcp->iec_status));
if (ret < 0)
return ret;
daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL);
if (!daidrv)
return -ENOMEM;