LXQT for Sinclair progressive mode line support

This commit is contained in:
Scare Crowe 2021-11-09 16:56:05 +05:00
parent c26fcb19b5
commit 4fc17d7ce7
7 changed files with 445 additions and 75 deletions

View File

@ -2140,10 +2140,6 @@ static void __execlists_unhold(struct i915_request *rq)
if (p->flags & I915_DEPENDENCY_WEAK)
continue;
/* Propagate any change in error status */
if (rq->fence.error)
i915_request_set_error_once(w, rq->fence.error);
if (w->engine != rq->engine)
continue;

View File

@ -294,6 +294,23 @@ struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
return NULL;
}
#define drm_for_each_connector_mask(connector, dev, connector_mask) \
list_for_each_entry((connector), &(dev)->mode_config.connector_list, head) \
for_each_if ((connector_mask) & drm_connector_mask(connector))
struct drm_connector *vc4_get_crtc_connector(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct drm_connector *connector;
WARN_ON(hweight32(state->connector_mask) > 1);
drm_for_each_connector_mask(connector, crtc->dev, state->connector_mask)
return connector;
return NULL;
}
static void vc4_crtc_pixelvalve_reset(struct drm_crtc *crtc)
{
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
@ -393,6 +410,7 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode
CRTC_WRITE(PV_V_CONTROL,
PV_VCONTROL_CONTINUOUS |
(is_dsi ? PV_VCONTROL_DSI : 0));
CRTC_WRITE(PV_VSYNCD_EVEN, 0);
}
CRTC_WRITE(PV_VERTA,
@ -1162,19 +1180,42 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
if (!vc4->hvs->hvs5) {
drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
} else {
/* This is a lie for hvs5 which uses a 16 point PWL, but it
* allows for something smarter than just 16 linearly spaced
* segments. Conversion is done in vc5_hvs_update_gamma_lut.
*/
drm_mode_crtc_set_gamma_size(crtc, 256);
}
drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
if (!vc4->hvs->hvs5) {
/* We support CTM, but only for one CRTC at a time. It's therefore
* implemented as private driver state in vc4_kms, not here.
*/
drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
}
for (i = 0; i < crtc->gamma_size; i++) {
vc4_crtc->lut_r[i] = i;
vc4_crtc->lut_g[i] = i;
vc4_crtc->lut_b[i] = i;
/* Initialize the VC4 gamma LUTs */
for (i = 0; i < crtc->gamma_size; i++) {
vc4_crtc->lut_r[i] = i;
vc4_crtc->lut_g[i] = i;
vc4_crtc->lut_b[i] = i;
}
} else {
/* Initialize the VC5 gamma PWL entries. Assume 12-bit pipeline,
* evenly spread over full range.
*/
for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++) {
vc4_crtc->pwl_r[i] =
VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
vc4_crtc->pwl_g[i] =
VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
vc4_crtc->pwl_b[i] =
VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
vc4_crtc->pwl_a[i] =
VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
}
}
return 0;

View File

@ -19,6 +19,7 @@
#include <drm/drm_modeset_lock.h>
#include "uapi/drm/vc4_drm.h"
#include "vc4_regs.h"
struct drm_device;
struct drm_gem_object;
@ -325,7 +326,6 @@ 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.
@ -481,6 +481,17 @@ struct vc4_pv_data {
};
struct vc5_gamma_entry {
u32 x_c_terms;
u32 grad_term;
};
#define VC5_HVS_SET_GAMMA_ENTRY(x, c, g) (struct vc5_gamma_entry){ \
.x_c_terms = VC4_SET_FIELD((x), SCALER5_DSPGAMMA_OFF_X) | \
VC4_SET_FIELD((c), SCALER5_DSPGAMMA_OFF_C), \
.grad_term = (g) \
}
struct vc4_crtc {
struct drm_crtc base;
struct platform_device *pdev;
@ -490,9 +501,19 @@ struct vc4_crtc {
/* Timestamp at start of vblank irq - unaffected by lock delays. */
ktime_t t_vblank;
u8 lut_r[256];
u8 lut_g[256];
u8 lut_b[256];
union {
struct { /* VC4 gamma LUT */
u8 lut_r[256];
u8 lut_g[256];
u8 lut_b[256];
};
struct { /* VC5 gamma PWL entries */
struct vc5_gamma_entry pwl_r[SCALER5_DSPGAMMA_NUM_POINTS];
struct vc5_gamma_entry pwl_g[SCALER5_DSPGAMMA_NUM_POINTS];
struct vc5_gamma_entry pwl_b[SCALER5_DSPGAMMA_NUM_POINTS];
struct vc5_gamma_entry pwl_a[SCALER5_DSPGAMMA_NUM_POINTS];
};
};
struct drm_pending_vblank_event *event;
@ -546,6 +567,9 @@ vc4_crtc_to_vc4_pv_data(const struct vc4_crtc *crtc)
return container_of(data, struct vc4_pv_data, base);
}
struct drm_connector *vc4_get_crtc_connector(struct drm_crtc *crtc,
struct drm_crtc_state *state);
struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
struct drm_crtc_state *state);

View File

@ -134,6 +134,84 @@ static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data)
return 0;
}
static int vc5_hvs_debugfs_gamma(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct drm_printer p = drm_seq_file_printer(m);
unsigned int i, chan;
u32 dispstat, dispbkgndx;
for (chan = 0; chan < SCALER_CHANNELS_COUNT; chan++) {
u32 x_c, grad;
u32 offset = SCALER5_DSPGAMMA_START +
chan * SCALER5_DSPGAMMA_CHAN_OFFSET;
dispstat = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)),
SCALER_DISPSTATX_MODE);
if (dispstat == SCALER_DISPSTATX_MODE_DISABLED ||
dispstat == SCALER_DISPSTATX_MODE_EOF) {
drm_printf(&p, "HVS channel %u: Channel disabled\n", chan);
continue;
}
dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan));
if (!(dispbkgndx & SCALER_DISPBKGND_GAMMA)) {
drm_printf(&p, "HVS channel %u: Gamma disabled\n", chan);
continue;
}
drm_printf(&p, "HVS channel %u:\n", chan);
drm_printf(&p, " red:\n");
for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) {
x_c = HVS_READ(offset);
grad = HVS_READ(offset + 4);
drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n",
x_c, grad,
VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X),
VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C),
grad);
}
drm_printf(&p, " green:\n");
for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) {
x_c = HVS_READ(offset);
grad = HVS_READ(offset + 4);
drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n",
x_c, grad,
VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X),
VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C),
grad);
}
drm_printf(&p, " blue:\n");
for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) {
x_c = HVS_READ(offset);
grad = HVS_READ(offset + 4);
drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n",
x_c, grad,
VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X),
VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C),
grad);
}
/* Alpha only valid on channel 2 */
if (chan != 2)
continue;
drm_printf(&p, " alpha:\n");
for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) {
x_c = HVS_READ(offset);
grad = HVS_READ(offset + 4);
drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n",
x_c, grad,
VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X),
VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C),
grad);
}
}
return 0;
}
/* The filter kernel is composed of dwords each containing 3 9-bit
* signed integers packed next to each other.
*/
@ -236,6 +314,80 @@ static void vc4_hvs_update_gamma_lut(struct drm_crtc *crtc)
vc4_hvs_lut_load(crtc);
}
static void vc5_hvs_write_gamma_entry(struct vc4_dev *vc4,
u32 offset,
struct vc5_gamma_entry *gamma)
{
HVS_WRITE(offset, gamma->x_c_terms);
HVS_WRITE(offset + 4, gamma->grad_term);
}
static void vc5_hvs_lut_load(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
u32 i;
u32 offset = SCALER5_DSPGAMMA_START +
vc4_state->assigned_channel * SCALER5_DSPGAMMA_CHAN_OFFSET;
for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8)
vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_r[i]);
for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8)
vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_g[i]);
for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8)
vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_b[i]);
if (vc4_state->assigned_channel == 2) {
/* Alpha only valid on channel 2 */
for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8)
vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_a[i]);
}
}
static void vc5_hvs_update_gamma_lut(struct drm_crtc *crtc)
{
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct drm_color_lut *lut = crtc->state->gamma_lut->data;
unsigned int step, i;
u32 start, end;
#define VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl, chan) \
start = drm_color_lut_extract(lut[i * step].chan, 12); \
end = drm_color_lut_extract(lut[(i + 1) * step - 1].chan, 12); \
\
/* Negative gradients not permitted by the hardware, so \
* flatten such points out. \
*/ \
if (end < start) \
end = start; \
\
/* Assume 12bit pipeline. \
* X evenly spread over full range (12 bit). \
* C as U12.4 format. \
* Gradient as U4.8 format. \
*/ \
vc4_crtc->pwl[i] = \
VC5_HVS_SET_GAMMA_ENTRY(i << 8, start << 4, \
((end - start) << 4) / (step - 1))
/* HVS5 has a 16 point piecewise linear function for each colour
* channel (including alpha on channel 2) on each display channel.
*
* Currently take a crude subsample of the gamma LUT, but this could
* be improved to implement curve fitting.
*/
step = crtc->gamma_size / SCALER5_DSPGAMMA_NUM_POINTS;
for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++) {
VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_r, red);
VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_g, green);
VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_b, blue);
}
vc5_hvs_lut_load(crtc);
}
int vc4_hvs_get_fifo_from_output(struct drm_device *dev, unsigned int output)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
@ -329,14 +481,16 @@ static int vc4_hvs_init_channel(struct vc4_dev *vc4, struct drm_crtc *crtc,
dispbkgndx &= ~SCALER_DISPBKGND_INTERLACE;
HVS_WRITE(SCALER_DISPBKGNDX(chan), dispbkgndx |
SCALER_DISPBKGND_AUTOHS |
((!vc4->hvs->hvs5) ? SCALER_DISPBKGND_GAMMA : 0) |
SCALER_DISPBKGND_AUTOHS | SCALER_DISPBKGND_GAMMA |
(interlace ? SCALER_DISPBKGND_INTERLACE : 0));
/* Reload the LUT, since the SRAMs would have been disabled if
* all CRTCs had SCALER_DISPBKGND_GAMMA unset at once.
*/
vc4_hvs_lut_load(crtc);
if (!vc4->hvs->hvs5)
vc4_hvs_lut_load(crtc);
else
vc5_hvs_lut_load(crtc);
return 0;
}
@ -365,6 +519,36 @@ void vc4_hvs_stop_channel(struct drm_device *dev, unsigned int chan)
SCALER_DISPSTATX_EMPTY);
}
static int vc4_hvs_gamma_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
struct drm_connector_state *conn_state;
struct drm_connector *connector;
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
if (!vc4->hvs->hvs5)
return 0;
if (!crtc_state->color_mgmt_changed)
return 0;
connector = vc4_get_crtc_connector(crtc, crtc_state);
if (!connector)
return -EINVAL;
if (!(connector->connector_type == DRM_MODE_CONNECTOR_HDMIA))
return 0;
conn_state = drm_atomic_get_connector_state(state, connector);
if (!conn_state)
return -EINVAL;
crtc_state->mode_changed = true;
return 0;
}
int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
@ -395,7 +579,7 @@ int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
if (ret)
return ret;
return 0;
return vc4_hvs_gamma_check(crtc, state);
}
static void vc4_hvs_update_dlist(struct drm_crtc *crtc)
@ -534,7 +718,10 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel));
if (crtc->state->gamma_lut) {
vc4_hvs_update_gamma_lut(crtc);
if (!vc4->hvs->hvs5)
vc4_hvs_update_gamma_lut(crtc);
else
vc5_hvs_update_gamma_lut(crtc);
dispbkgndx |= SCALER_DISPBKGND_GAMMA;
} else {
/* Unsetting DISPBKGND_GAMMA skips the gamma lut step
@ -730,6 +917,9 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
NULL);
vc4_debugfs_add_file(drm, "hvs_dlists", vc4_hvs_debugfs_dlist,
NULL);
if (hvs->hvs5)
vc4_debugfs_add_file(drm, "hvs_gamma", vc5_hvs_debugfs_gamma,
NULL);
return 0;
}

View File

@ -40,6 +40,7 @@ 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 clk_request *core_req;
struct {
unsigned in_use: 1;
@ -343,20 +344,20 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
struct drm_device *dev = state->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
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;
unsigned int channel;
int i;
old_hvs_state = vc4_hvs_get_old_global_state(state);
if (WARN_ON(!old_hvs_state))
if (WARN_ON(IS_ERR(old_hvs_state)))
return;
new_hvs_state = vc4_hvs_get_new_global_state(state);
if (WARN_ON(!new_hvs_state))
if (WARN_ON(IS_ERR(new_hvs_state)))
return;
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
@ -369,16 +370,10 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel);
}
for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
struct vc4_crtc_state *vc4_crtc_state =
to_vc4_crtc_state(old_crtc_state);
for (channel = 0; channel < HVS_NUM_CHANNELS; channel++) {
struct drm_crtc_commit *commit;
unsigned int channel = vc4_crtc_state->assigned_channel;
int ret;
if (channel == VC4_HVS_CHANNEL_DISABLED)
continue;
if (!old_hvs_state->fifo_state[channel].in_use)
continue;
@ -389,6 +384,9 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
ret = drm_crtc_commit_wait(commit);
if (ret)
drm_err(dev, "Timed out waiting for commit\n");
drm_crtc_commit_put(commit);
old_hvs_state->fifo_state[channel].pending_commit = NULL;
}
if (vc4->hvs && vc4->hvs->hvs5) {
@ -408,7 +406,8 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
* And remove the previous one based on the HVS
* requirements if any.
*/
clk_request_done(hvs->core_req);
clk_request_done(old_hvs_state->core_req);
old_hvs_state->core_req = NULL;
}
drm_atomic_helper_commit_modeset_disables(dev, state);
@ -442,8 +441,8 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
* 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);
new_hvs_state->core_req = clk_request_start(hvs->core_clk,
new_hvs_state->core_clock_rate);
/* And drop the temporary request */
clk_request_done(core_req);
@ -468,8 +467,8 @@ static int vc4_atomic_commit_setup(struct drm_atomic_state *state)
state->legacy_cursor_update = false;
hvs_state = vc4_hvs_get_new_global_state(state);
if (!hvs_state)
return -EINVAL;
if (WARN_ON(IS_ERR(hvs_state)))
return PTR_ERR(hvs_state);
for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
struct vc4_crtc_state *vc4_crtc_state =
@ -717,12 +716,6 @@ vc4_hvs_channels_duplicate_state(struct drm_private_obj *obj)
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;
state->fifo_state[i].pending_commit =
drm_crtc_commit_get(old_state->fifo_state[i].pending_commit);
}
state->core_clock_rate = old_state->core_clock_rate;
@ -814,8 +807,8 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
unsigned int i;
hvs_new_state = vc4_hvs_get_global_state(state);
if (!hvs_new_state)
return -EINVAL;
if (IS_ERR(hvs_new_state))
return PTR_ERR(hvs_new_state);
for (i = 0; i < ARRAY_SIZE(hvs_new_state->fifo_state); i++)
if (!hvs_new_state->fifo_state[i].in_use)
@ -907,8 +900,8 @@ vc4_core_clock_atomic_check(struct drm_atomic_state *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;
if (IS_ERR(hvs_new_state))
return PTR_ERR(hvs_new_state);
for_each_oldnew_crtc_in_state(state, crtc,
old_crtc_state,

View File

@ -491,6 +491,28 @@
#define SCALER_DLIST_START 0x00002000
#define SCALER_DLIST_SIZE 0x00004000
/* Gamma PWL for each channel. 16 points for each of 4 colour channels (alpha
* only on channel 2). 8 bytes per entry, offsets first, then gradient:
* Y = GRAD * X + C
*
* Values for X and C are left justified, and vary depending on the width of
* the HVS channel:
* 8-bit pipeline: X uses [31:24], C is U8.8 format, and GRAD is U4.8.
* 12-bit pipeline: X uses [31:20], C is U12.4 format, and GRAD is U4.8.
*
* The 3 HVS channels start at 0x400 offsets (ie chan 1 starts at 0x2400, and
* chan 2 at 0x2800).
*/
#define SCALER5_DSPGAMMA_NUM_POINTS 16
#define SCALER5_DSPGAMMA_START 0x00002000
#define SCALER5_DSPGAMMA_CHAN_OFFSET 0x400
# define SCALER5_DSPGAMMA_OFF_X_MASK VC4_MASK(31, 20)
# define SCALER5_DSPGAMMA_OFF_X_SHIFT 20
# define SCALER5_DSPGAMMA_OFF_C_MASK VC4_MASK(15, 0)
# define SCALER5_DSPGAMMA_OFF_C_SHIFT 0
# define SCALER5_DSPGAMMA_GRAD_MASK VC4_MASK(11, 0)
# define SCALER5_DSPGAMMA_GRAD_SHIFT 0
#define SCALER5_DLIST_START 0x00004000
# define VC4_HDMI_SW_RESET_FORMAT_DETECT BIT(1)

View File

@ -245,7 +245,8 @@ enum vc4_vec_tv_mode_id {
};
struct vc4_vec_tv_mode {
const struct drm_display_mode *mode;
const struct drm_display_mode *interlaced_mode;
const struct drm_display_mode *progressive_mode;
u32 config0;
u32 config1;
u32 custom_freq;
@ -279,61 +280,81 @@ static const struct debugfs_reg32 vec_regs[] = {
};
static const struct drm_display_mode drm_mode_480i = {
DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,
DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,
480, 480 + 7, 480 + 7 + 6, 525, 0,
DRM_MODE_FLAG_INTERLACE)
};
static const struct drm_display_mode drm_mode_240p = {
DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500,
720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,
240, 240 + 3, 240 + 3 + 3, 262, 0, 0)
};
static const struct drm_display_mode drm_mode_576i = {
DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,
DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
576, 576 + 4, 576 + 4 + 6, 625, 0,
DRM_MODE_FLAG_INTERLACE)
};
static const struct drm_display_mode drm_mode_288p = {
DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500,
720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
288, 288 + 2, 288 + 2 + 3, 312, 0, 0)
};
static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {
[VC4_VEC_TV_MODE_NTSC] = {
.mode = &drm_mode_480i,
.interlaced_mode = &drm_mode_480i,
.progressive_mode = &drm_mode_240p,
.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
[VC4_VEC_TV_MODE_NTSC_J] = {
.mode = &drm_mode_480i,
.interlaced_mode = &drm_mode_480i,
.progressive_mode = &drm_mode_240p,
.config0 = VEC_CONFIG0_NTSC_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
[VC4_VEC_TV_MODE_NTSC_443] = {
/* NTSC with PAL chroma frequency */
.mode = &drm_mode_480i,
.interlaced_mode = &drm_mode_480i,
.progressive_mode = &drm_mode_240p,
.config0 = VEC_CONFIG0_NTSC_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
.custom_freq = 0x2a098acb,
},
[VC4_VEC_TV_MODE_PAL] = {
.mode = &drm_mode_576i,
.interlaced_mode = &drm_mode_576i,
.progressive_mode = &drm_mode_288p,
.config0 = VEC_CONFIG0_PAL_BDGHI_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
[VC4_VEC_TV_MODE_PAL_M] = {
.mode = &drm_mode_480i,
.interlaced_mode = &drm_mode_480i,
.progressive_mode = &drm_mode_240p,
.config0 = VEC_CONFIG0_PAL_M_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
[VC4_VEC_TV_MODE_PAL_N] = {
.mode = &drm_mode_576i,
.interlaced_mode = &drm_mode_576i,
.progressive_mode = &drm_mode_288p,
.config0 = VEC_CONFIG0_PAL_N_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
[VC4_VEC_TV_MODE_PAL60] = {
/* PAL-M with chroma frequency of regular PAL */
.mode = &drm_mode_480i,
.interlaced_mode = &drm_mode_480i,
.progressive_mode = &drm_mode_240p,
.config0 = VEC_CONFIG0_PAL_M_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
.custom_freq = 0x2a098acb,
},
[VC4_VEC_TV_MODE_SECAM] = {
.mode = &drm_mode_576i,
.interlaced_mode = &drm_mode_576i,
.progressive_mode = &drm_mode_288p,
.config0 = VEC_CONFIG0_SECAM_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
.custom_freq = 0x29c71c72,
@ -393,16 +414,32 @@ static void vc4_vec_connector_destroy(struct drm_connector *connector)
static int vc4_vec_connector_get_modes(struct drm_connector *connector)
{
struct drm_connector_state *state = connector->state;
struct drm_display_mode *mode;
struct drm_display_mode *interlaced_mode, *progressive_mode;
mode = drm_mode_duplicate(connector->dev,
vc4_vec_tv_modes[state->tv.mode].mode);
if (!mode) {
interlaced_mode =
drm_mode_duplicate(connector->dev,
vc4_vec_tv_modes[state->tv.mode].interlaced_mode);
progressive_mode =
drm_mode_duplicate(connector->dev,
vc4_vec_tv_modes[state->tv.mode].progressive_mode);
if (!interlaced_mode || !progressive_mode) {
DRM_ERROR("Failed to create a new display mode\n");
drm_mode_destroy(connector->dev, interlaced_mode);
drm_mode_destroy(connector->dev, progressive_mode);
return -ENOMEM;
}
drm_mode_probed_add(connector, mode);
if (connector->cmdline_mode.specified &&
connector->cmdline_mode.refresh_specified &&
!connector->cmdline_mode.interlace)
/* progressive mode set at boot, let's make it preferred */
progressive_mode->type |= DRM_MODE_TYPE_PREFERRED;
else
/* otherwise, interlaced mode is preferred */
interlaced_mode->type |= DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, interlaced_mode);
drm_mode_probed_add(connector, progressive_mode);
return 1;
}
@ -423,18 +460,11 @@ static int vc4_vec_connector_atomic_check(struct drm_connector *conn,
struct drm_connector_state *new_state =
drm_atomic_get_new_connector_state(state, conn);
const struct vc4_vec_tv_mode *vec_mode =
&vc4_vec_tv_modes[new_state->tv.mode];
if (new_state->crtc) {
if (new_state->crtc && old_state->tv.mode != new_state->tv.mode) {
struct drm_crtc_state *crtc_state =
drm_atomic_get_new_crtc_state(state, new_state->crtc);
if (!drm_mode_equal(vec_mode->mode, &crtc_state->mode))
return -EINVAL;
if (old_state->tv.mode != new_state->tv.mode)
crtc_state->mode_changed = true;
crtc_state->mode_changed = true;
}
return 0;
@ -559,7 +589,10 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
VEC_WRITE(VEC_CLMP0_START, 0xac);
VEC_WRITE(VEC_CLMP0_END, 0xec);
VEC_WRITE(VEC_CONFIG2,
VEC_CONFIG2_UV_DIG_DIS | VEC_CONFIG2_RGB_DIG_DIS);
VEC_CONFIG2_UV_DIG_DIS |
VEC_CONFIG2_RGB_DIG_DIS |
((encoder->crtc->state->adjusted_mode.flags &
DRM_MODE_FLAG_INTERLACE) ? 0 : VEC_CONFIG2_PROG_SCAN));
VEC_WRITE(VEC_CONFIG3, VEC_CONFIG3_HORIZ_LEN_STD);
VEC_WRITE(VEC_DAC_CONFIG, vec->variant->dac_config);
@ -582,17 +615,88 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
}
static bool vc4_vec_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
return true;
const struct drm_display_mode *reference_mode =
vc4_vec_tv_modes[conn_state->tv.mode].interlaced_mode;
if (crtc_state->adjusted_mode.crtc_clock != reference_mode->clock ||
crtc_state->adjusted_mode.crtc_htotal != reference_mode->htotal ||
crtc_state->adjusted_mode.crtc_hdisplay % 4 != 0 ||
crtc_state->adjusted_mode.crtc_hsync_end -
crtc_state->adjusted_mode.crtc_hsync_start < 1)
return -EINVAL;
switch (reference_mode->vtotal) {
case 525:
if (crtc_state->adjusted_mode.crtc_vdisplay < 1 ||
crtc_state->adjusted_mode.crtc_vdisplay > 253 ||
crtc_state->adjusted_mode.crtc_vsync_start -
crtc_state->adjusted_mode.crtc_vdisplay < 1 ||
crtc_state->adjusted_mode.crtc_vsync_end -
crtc_state->adjusted_mode.crtc_vsync_start != 3 ||
crtc_state->adjusted_mode.crtc_vtotal -
crtc_state->adjusted_mode.crtc_vsync_end < 4 ||
crtc_state->adjusted_mode.crtc_vtotal > 262)
return -EINVAL;
if ((crtc_state->adjusted_mode.flags &
DRM_MODE_FLAG_INTERLACE) &&
(crtc_state->adjusted_mode.vdisplay % 2 != 0 ||
crtc_state->adjusted_mode.vsync_start % 2 != 1 ||
crtc_state->adjusted_mode.vsync_end % 2 != 1 ||
crtc_state->adjusted_mode.vtotal % 2 != 1))
return -EINVAL;
/* progressive mode is hard-wired to 262 total lines */
if (!(crtc_state->adjusted_mode.flags &
DRM_MODE_FLAG_INTERLACE) &&
crtc_state->adjusted_mode.crtc_vtotal != 262)
return -EINVAL;
break;
case 625:
if (crtc_state->adjusted_mode.crtc_vdisplay < 1 ||
crtc_state->adjusted_mode.crtc_vdisplay > 305 ||
crtc_state->adjusted_mode.crtc_vsync_start -
crtc_state->adjusted_mode.crtc_vdisplay < 1 ||
crtc_state->adjusted_mode.crtc_vsync_end -
crtc_state->adjusted_mode.crtc_vsync_start != 3 ||
crtc_state->adjusted_mode.crtc_vtotal -
crtc_state->adjusted_mode.crtc_vsync_end < 2 ||
crtc_state->adjusted_mode.crtc_vtotal > 312)
return -EINVAL;
if ((crtc_state->adjusted_mode.flags &
DRM_MODE_FLAG_INTERLACE) &&
(crtc_state->adjusted_mode.vdisplay % 2 != 0 ||
crtc_state->adjusted_mode.vsync_start % 2 != 0 ||
crtc_state->adjusted_mode.vsync_end % 2 != 0 ||
crtc_state->adjusted_mode.vtotal % 2 != 1))
return -EINVAL;
/* progressive mode is hard-wired to 312 total lines */
if (!(crtc_state->adjusted_mode.flags &
DRM_MODE_FLAG_INTERLACE) &&
crtc_state->adjusted_mode.crtc_vtotal != 312)
return -EINVAL;
break;
default:
return -EINVAL;
}
return 0;
}
static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = {
.disable = vc4_vec_encoder_disable,
.enable = vc4_vec_encoder_enable,
.mode_fixup = vc4_vec_encoder_mode_fixup,
.atomic_check = vc4_vec_encoder_atomic_check,
};
static const struct vc4_vec_variant bcm2835_vec_variant = {