drm/msm/dsi-staging: update coral gamma tables
Based on testing, the s6e3hc2 display has as significant difference
between 60hz and 90hz on the G13+G22 bands. By performing some swaps on
the calibrated gamma bands, the difference is reduced.
Bug: 141569052
Change-Id: Ifbb2c0981cf2a0eb0bac9aaa1c9e8da1e310f717
Signed-off-by: Adrian Salido <salidoa@google.com>
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel_switch.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel_switch.c
index cf3bfad..ff84010 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel_switch.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel_switch.c
@@ -662,6 +662,34 @@
.perform_switch = panel_switch_cmd_set_transfer,
};
+struct gamma_color {
+ s16 r;
+ s16 g;
+ s16 b;
+};
+
+#define pr_debug_color(fmt, col, ...) \
+ pr_debug(fmt "RGB: (%03X %03X %03X)\n", ##__VA_ARGS__, \
+ (col)->r, (col)->g, (col)->b)
+
+static void gamma_color_sub(struct gamma_color *result,
+ const struct gamma_color *c1,
+ const struct gamma_color *c2)
+{
+ result->r = c1->r - c2->r;
+ result->g = c1->g - c2->g;
+ result->b = c1->b - c2->b;
+}
+
+static void gamma_color_add(struct gamma_color *result,
+ const struct gamma_color *c1,
+ const struct gamma_color *c2)
+{
+ result->r = c1->r + c2->r;
+ result->g = c1->g + c2->g;
+ result->b = c1->b + c2->b;
+}
+
enum s6e3hc2_gamma_state {
GAMMA_STATE_UNINITIALIZED,
GAMMA_STATE_EMPTY,
@@ -674,11 +702,17 @@
enum s6e3hc2_gamma_state gamma_state;
struct kthread_work gamma_work;
+ bool skip_swap;
};
#define S6E3HC2_GAMMA_BAND_LEN 45
#define S6E3HC2_GAMMA_BAND_G0_OFFSET 39
+enum s6e3hc2_gamma_bands_indices {
+ S6E3HC2_GAMMA_BAND_G22 = 7,
+ S6E3HC2_GAMMA_BAND_G13 = 8,
+};
+
enum s6e3hc2_gamma_flags {
/*
* Some s6e3hc2 panels have incorrectly programmed gamma bands,
@@ -955,6 +989,137 @@
return 0;
}
+/*
+ * Gamma bands are represented in groups of 4 x 10-bit per color (3) component
+ * 4 * 3 color components * 10 bit = 15 bytes
+ *
+ * The MSB (upper) 2 bits of each color are all found packed at the beginning of
+ * the group (2 bits * 12 = 3 bytes) in big endian memory layout.
+ * This is followed by the LSB (lower) 8 bits.
+ */
+static void s6e3hc2_gamma_band_offsets(int band_idx, u8 color, int *lsb_idx,
+ int *msb_idx, int *msb_shift,
+ u8 *msb_mask)
+{
+ const int index = ((band_idx % 4) * 3) + color;
+ const int group_offset = 15 * (band_idx / 4);
+
+ /* one byte per color LSB, skipping 3 bytes within group for MSB */
+ *lsb_idx = group_offset + 3 + index;
+
+ /* each byte can hold 4 colors MSB, 2 bits each */
+ *msb_idx = group_offset + (index / 4);
+ *msb_shift = 2 * ((index % 4) + 1);
+ *msb_mask = 0x3 << (BITS_PER_BYTE - *msb_shift);
+}
+
+static u16 s6e3hc2_gamma_get_color(u8 *buf, int band_idx, u8 color)
+{
+ int msb_idx, lsb_idx, msb_shift;
+ u8 msb_mask;
+
+ s6e3hc2_gamma_band_offsets(band_idx, color, &lsb_idx, &msb_idx,
+ &msb_shift, &msb_mask);
+
+ return ((buf[msb_idx] & msb_mask) << msb_shift) | buf[lsb_idx];
+}
+
+static void s6e3hc2_gamma_set_color(u8 *buf, int band_idx, u8 color, u16 val)
+{
+ int msb_idx, lsb_idx, msb_shift;
+ u8 msb_mask;
+
+ s6e3hc2_gamma_band_offsets(band_idx, color, &lsb_idx, &msb_idx,
+ &msb_shift, &msb_mask);
+
+ buf[lsb_idx] = val & 0xFF;
+ buf[msb_idx] &= ~msb_mask;
+ buf[msb_idx] |= (val >> msb_shift) & msb_mask;
+}
+
+static void s6e3hc2_gamma_get_rgb(void *buf, int band_idx,
+ struct gamma_color *color)
+{
+ /* buf should point at the beginning of a 45 byte gamma band */
+ color->r = s6e3hc2_gamma_get_color(buf, band_idx, 0);
+ color->g = s6e3hc2_gamma_get_color(buf, band_idx, 1);
+ color->b = s6e3hc2_gamma_get_color(buf, band_idx, 2);
+}
+
+static int s6e3hc2_gamma_set_rgb(void *buf, int band_idx,
+ struct gamma_color *color)
+{
+ if (color->r < 0 || color->g < 0 || color->b < 0) {
+ pr_err("Invalid color value %03X %03X %03X for band=%d\n",
+ color->r, color->g, color->b, band_idx);
+ return -EINVAL;
+ }
+
+ /* buf should point at the beginning of a 45 byte gamma band */
+ s6e3hc2_gamma_set_color(buf, band_idx, 0, color->r);
+ s6e3hc2_gamma_set_color(buf, band_idx, 1, color->g);
+ s6e3hc2_gamma_set_color(buf, band_idx, 2, color->b);
+
+ return 0;
+}
+
+static void *s6e3hc2_gamma_get_offset(struct s6e3hc2_panel_data *data,
+ u32 table_idx, u32 gamma_idx)
+{
+ const int gamma_offset = gamma_idx * S6E3HC2_GAMMA_BAND_LEN;
+
+ if (unlikely(table_idx >= ARRAY_SIZE(s6e3hc2_gamma_tables))) {
+ return NULL;
+ } else {
+ const int len = s6e3hc2_gamma_tables[table_idx].len;
+
+ if (unlikely(gamma_offset + S6E3HC2_GAMMA_BAND_LEN > len))
+ return NULL;
+ }
+
+ /* skip 1 byte for cmd */
+ return data->gamma_data[table_idx] + gamma_offset + 1;
+}
+
+static void s6e3hc2_gamma_swap_offsets(struct s6e3hc2_panel_data *low,
+ struct s6e3hc2_panel_data *high)
+{
+ struct gamma_color g13_60hz, g13_90hz, g13_delta;
+ struct gamma_color g22_60hz, g22_90hz, g22_delta;
+ void *buf_60hz, *buf_90hz;
+
+ buf_60hz = s6e3hc2_gamma_get_offset(low, 0, 1);
+ buf_90hz = s6e3hc2_gamma_get_offset(high, 0, 1);
+
+ if (unlikely(!buf_60hz || !buf_90hz))
+ return;
+
+ s6e3hc2_gamma_get_rgb(buf_60hz, S6E3HC2_GAMMA_BAND_G22, &g22_60hz);
+ pr_debug_color("60Hz-22", &g22_60hz);
+
+ s6e3hc2_gamma_get_rgb(buf_60hz, S6E3HC2_GAMMA_BAND_G13, &g13_60hz);
+ pr_debug_color("60Hz-13", &g13_60hz);
+
+ s6e3hc2_gamma_get_rgb(buf_90hz, S6E3HC2_GAMMA_BAND_G22, &g22_90hz);
+ pr_debug_color("90Hz-22", &g22_90hz);
+
+ s6e3hc2_gamma_get_rgb(buf_90hz, S6E3HC2_GAMMA_BAND_G13, &g13_90hz);
+ pr_debug_color("90Hz-13", &g13_90hz);
+
+ gamma_color_sub(&g13_delta, &g13_90hz, &g13_60hz);
+ gamma_color_sub(&g22_delta, &g22_90hz, &g22_60hz);
+
+ /* keep the highest delta at g13 */
+ if (g22_delta.g < g13_delta.g)
+ return;
+
+ gamma_color_add(&g22_90hz, &g22_60hz, &g13_delta);
+ s6e3hc2_gamma_set_rgb(buf_90hz, S6E3HC2_GAMMA_BAND_G22, &g22_90hz);
+
+ gamma_color_add(&g13_90hz, &g13_60hz, &g22_delta);
+ s6e3hc2_gamma_set_rgb(buf_90hz, S6E3HC2_GAMMA_BAND_G13, &g13_90hz);
+}
+
static int s6e3hc2_gamma_read_mode(struct panel_switch_data *pdata,
const struct dsi_display_mode *mode)
{
@@ -983,13 +1148,13 @@
return rc;
}
-static int find_gamma_data_for_refresh_rate(struct dsi_panel *panel,
- u32 refresh_rate, u8 ***gamma_data)
+static int find_switch_data_for_refresh_rate(struct dsi_panel *panel,
+ u32 refresh_rate, struct s6e3hc2_panel_data **data)
{
struct dsi_display_mode *mode;
int i;
- if (!gamma_data)
+ if (!data)
return -EINVAL;
for_each_display_mode(i, mode, panel)
@@ -1000,7 +1165,7 @@
if (unlikely(!priv_data))
return -ENODATA;
- *gamma_data = priv_data->gamma_data;
+ *data = priv_data;
return 0;
}
@@ -1022,32 +1187,10 @@
* In other words, when we write gamma data to registers, we do not modify
* prefix data; we only modify gamma data.
*/
-static int s6e3hc2_gamma_set_prefixes(struct panel_switch_data *pdata)
+static void s6e3hc2_gamma_set_prefixes(u8 **gamma_data_otp,
+ u8 **gamma_data_flash)
{
int i;
- int rc = 0;
- u8 **gamma_data_otp;
- u8 **gamma_data_flash;
-
- /*
- * For s6e3hc2, 60Hz gamma curves are read from OTP and 90Hz
- * gamma curves are read from flash.
- */
- rc = find_gamma_data_for_refresh_rate(pdata->panel, 60,
- &gamma_data_otp);
- if (rc) {
- pr_err("Error setting gamma prefix: no matching OTP mode, err %d\n",
- rc);
- return rc;
- }
-
- rc = find_gamma_data_for_refresh_rate(pdata->panel, 90,
- &gamma_data_flash);
- if (rc) {
- pr_err("Error setting gamma prefix: no matching flash mode, err %d\n",
- rc);
- return rc;
- }
for (i = 0; i < S6E3HC2_NUM_GAMMA_TABLES; i++) {
const struct s6e3hc2_gamma_info *gamma_info =
@@ -1065,6 +1208,41 @@
memcpy(gamma_curve_flash, gamma_curve_otp,
gamma_info->prefix_len);
}
+}
+static int s6e3hc2_gamma_post_process(struct panel_switch_data *pdata)
+{
+ const struct s6e3hc2_switch_data *sdata;
+ int rc = 0;
+ struct s6e3hc2_panel_data *otp_data;
+ struct s6e3hc2_panel_data *flash_data;
+
+ sdata = container_of(pdata, struct s6e3hc2_switch_data, base);
+
+ /*
+ * For s6e3hc2, 60Hz gamma curves are read from OTP and 90Hz
+ * gamma curves are read from flash.
+ */
+ rc = find_switch_data_for_refresh_rate(pdata->panel, 60,
+ &otp_data);
+ if (rc) {
+ pr_err("Error setting gamma prefix: no matching OTP mode, err %d\n",
+ rc);
+ return rc;
+ }
+
+ rc = find_switch_data_for_refresh_rate(pdata->panel, 90,
+ &flash_data);
+ if (rc) {
+ pr_err("Error setting gamma prefix: no matching flash mode, err %d\n",
+ rc);
+ return rc;
+ }
+
+ s6e3hc2_gamma_set_prefixes(otp_data->gamma_data,
+ flash_data->gamma_data);
+
+ if (!sdata->skip_swap)
+ s6e3hc2_gamma_swap_offsets(otp_data, flash_data);
return rc;
}
@@ -1102,7 +1280,7 @@
}
}
- rc = s6e3hc2_gamma_set_prefixes(pdata);
+ rc = s6e3hc2_gamma_post_process(pdata);
if (rc) {
pr_err("Unable to set gamma prefix\n");
goto abort;
@@ -1276,6 +1454,8 @@
kthread_init_work(&sdata->gamma_work, s6e3hc2_gamma_work);
debugfs_create_file("gamma", 0600, sdata->base.debug_root,
&sdata->base, &s6e3hc2_read_gamma_fops);
+ debugfs_create_bool("skip_swap", 0600, sdata->base.debug_root,
+ &sdata->skip_swap);
return &sdata->base;
}