Re: [PATCH] drm/edid: add CTA Video Format Data Block support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 16 Jan 2025, Alex Deucher <alexdeucher@xxxxxxxxx> wrote:
> On Tue, Nov 5, 2024 at 9:14 PM Hamza Mahfooz <hamza.mahfooz@xxxxxxx> wrote:
>>
>> Video Format Data Blocks (VFDBs) contain the necessary information that
>> needs to be fed to the Optimized Video Timings (OVT) Algorithm.
>> Also, we require OVT support to cover modes that aren't supported by
>> earlier standards (e.g. CVT). So, parse all of the relevant VFDB data
>> and feed it to the OVT Algorithm, to extract all of the missing OVT
>> modes.
>>
>> Cc: Ian Forbes <ian.forbes@xxxxxxxxxxxx>
>> Cc: Jani Nikula <jani.nikula@xxxxxxxxxxxxxxx>
>> Suggested-by: Karol Herbst <kherbst@xxxxxxxxxx>
>> Signed-off-by: Hamza Mahfooz <hamza.mahfooz@xxxxxxx>
>> ---
>> v3: move ovt stuff above add_cea_modes() and break up
>>     calculate_ovt_mode() to make it easier to read.
>>
>> v4: fix 32 bit build and constify read-only vars.
>>
>> v5: Implement suggestions from:
>>     https://lore.kernel.org/dri-devel/87plpbk92h.fsf@xxxxxxxxx/
>>     and export drm_ovt_mode().
>
> Any concerns with this patch?  Can we land this?

Has someone properly reviewed it against the specs?

I've provided some comments to earlier versions, and I think the end
result is better, but alas I haven't had the time to go through all the
calculations etc. And someone definitely should do that before
merging.


BR,
Jani.



>
> Alex
>
>> ---
>>  drivers/gpu/drm/drm_edid.c | 465 +++++++++++++++++++++++++++++++++++++
>>  include/drm/drm_edid.h     |   3 +
>>  2 files changed, 468 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
>> index 855beafb76ff..e5e34397be00 100644
>> --- a/drivers/gpu/drm/drm_edid.c
>> +++ b/drivers/gpu/drm/drm_edid.c
>> @@ -31,6 +31,7 @@
>>  #include <linux/bitfield.h>
>>  #include <linux/byteorder/generic.h>
>>  #include <linux/cec.h>
>> +#include <linux/gcd.h>
>>  #include <linux/hdmi.h>
>>  #include <linux/i2c.h>
>>  #include <linux/kernel.h>
>> @@ -741,6 +742,87 @@ static const struct minimode extra_modes[] = {
>>         { 2048, 1536, 60, 0 },
>>  };
>>
>> +struct cta_rid {
>> +       u16 hactive;
>> +       u16 vactive;
>> +       u8 hratio;
>> +       u8 vratio;
>> +};
>> +
>> +/* CTA-861-I Table 11 - Resolution Identification (RID) */
>> +static const struct cta_rid rids[] = {
>> +       [0]  = { 0, 0, 0, 0 },
>> +       [1]  = { 1280, 720, 16, 9 },
>> +       [2]  = { 1280, 720, 64, 27 },
>> +       [3]  = { 1680, 720, 64, 27 },
>> +       [4]  = { 1920, 1080, 16, 9 },
>> +       [5]  = { 1920, 1080, 64, 27 },
>> +       [6]  = { 2560, 1080, 64, 27 },
>> +       [7]  = { 3840, 1080, 32, 9 },
>> +       [8]  = { 2560, 1440, 16, 9 },
>> +       [9]  = { 3440, 1440, 64, 27 },
>> +       [10] = { 5120, 1440, 32, 9 },
>> +       [11] = { 3840, 2160, 16, 9 },
>> +       [12] = { 3840, 2160, 64, 27 },
>> +       [13] = { 5120, 2160, 64, 27 },
>> +       [14] = { 7680, 2160, 32, 9 },
>> +       [15] = { 5120, 2880, 16, 9 },
>> +       [16] = { 5120, 2880, 64, 27 },
>> +       [17] = { 6880, 2880, 64, 27 },
>> +       [18] = { 10240, 2880, 32, 9 },
>> +       [19] = { 7680, 4320, 16, 9 },
>> +       [20] = { 7680, 4320, 64, 27 },
>> +       [21] = { 10240, 4320, 64, 27 },
>> +       [22] = { 15360, 4320, 32, 9 },
>> +       [23] = { 11520, 6480, 16, 9 },
>> +       [24] = { 11520, 6480, 64, 27 },
>> +       [25] = { 15360, 6480, 64, 27 },
>> +       [26] = { 15360, 8640, 16, 9 },
>> +       [27] = { 15360, 8640, 64, 27 },
>> +       [28] = { 20480, 8640, 64, 27 },
>> +};
>> +
>> +/* CTA-861-I Table 12 - AVI InfoFrame Video Format Frame Rate */
>> +static const u16 video_format_frame_rates[] = {
>> +       /* Frame Rate 0-7 */
>> +       0, 24, 25, 30, 48, 50, 60, 100,
>> +       /* Frame Rate 8-15 */
>> +       120, 144, 200, 240, 300, 360, 400, 480,
>> +};
>> +
>> +/* CTA-861-I Table 13 - RID To VIC Mapping */
>> +static const u8 rid_to_vic[][8] = {
>> +       [0]  = {},
>> +       [1]  = { 60, 61, 62, 108, 19, 4, 41, 47 },
>> +       [2]  = { 65, 66, 67, 109, 68, 69, 70, 71 },
>> +       [3]  = { 79, 80, 81, 110, 82, 83, 84, 85 },
>> +       [4]  = { 32, 33, 34, 111, 31, 16, 64, 63 },
>> +       [5]  = { 72, 73, 74, 112, 75, 76, 77, 78 },
>> +       [6]  = { 86, 87, 88, 113, 89, 90, 91, 92 },
>> +       [7]  = {},
>> +       [8]  = {},
>> +       [9]  = {},
>> +       [10] = {},
>> +       [11] = { 93, 94, 95, 114, 96, 97, 117, 118 },
>> +       [12] = { 103, 104, 105, 116, 106, 107, 119, 120 },
>> +       [13] = { 121, 122, 123, 124, 125, 126, 127, 193 },
>> +       [14] = {},
>> +       [15] = {},
>> +       [16] = {},
>> +       [17] = {},
>> +       [18] = {},
>> +       [19] = { 194, 195, 196, 197, 198, 199, 200, 201 },
>> +       [20] = { 202, 203, 204, 205, 206, 207, 208, 209 },
>> +       [21] = { 210, 211, 212, 213, 214, 215, 216, 217 },
>> +       [22] = {},
>> +       [23] = {},
>> +       [24] = {},
>> +       [25] = {},
>> +       [26] = {},
>> +       [27] = {},
>> +       [28] = {},
>> +};
>> +
>>  /*
>>   * From CEA/CTA-861 spec.
>>   *
>> @@ -4131,6 +4213,7 @@ static int add_detailed_modes(struct drm_connector *connector,
>>  #define CTA_DB_VIDEO                   2
>>  #define CTA_DB_VENDOR                  3
>>  #define CTA_DB_SPEAKER                 4
>> +#define CTA_DB_VIDEO_FORMAT            6
>>  #define CTA_DB_EXTENDED_TAG            7
>>
>>  /* CTA-861-H Table 62 - CTA Extended Tag Codes */
>> @@ -4972,6 +5055,16 @@ struct cea_db {
>>         u8 data[];
>>  } __packed;
>>
>> +struct cta_vfd {
>> +       u8 rid;
>> +       u8 fr_fact;
>> +       bool bfr50;
>> +       bool fr24;
>> +       bool bfr60;
>> +       bool fr144;
>> +       bool fr48;
>> +};
>> +
>>  static int cea_db_tag(const struct cea_db *db)
>>  {
>>         return db->tag_length >> 5;
>> @@ -5250,6 +5343,376 @@ static int edid_hfeeodb_extension_block_count(const struct edid *edid)
>>         return cta[4 + 2];
>>  }
>>
>> +/* CTA-861 Video Format Descriptor (CTA VFD) */
>> +static void parse_cta_vfd(struct cta_vfd *vfd, const u8 *data, int vfd_len)
>> +{
>> +       vfd->rid = data[0] & 0x3f;
>> +       vfd->bfr50 = data[0] & 0x80;
>> +       vfd->fr24 = data[0] & 0x40;
>> +       vfd->bfr60 = vfd_len > 1 ? (data[1] & 0x80) : true;
>> +       vfd->fr144 = vfd_len > 1 ? (data[1] & 0x40) : false;
>> +       vfd->fr_fact = vfd_len > 1 ? (data[1] & 0x3f) : 0x3;
>> +       vfd->fr48 = vfd_len > 2 ? (data[2] & 0x1) : false;
>> +}
>> +
>> +static bool vfd_has_fr(const struct cta_vfd *vfd, int rate)
>> +{
>> +       static const u8 factors[] = {
>> +               1, 2, 4, 8, 12, 16
>> +       };
>> +       int factor = 0;
>> +       int i;
>> +
>> +       switch (rate) {
>> +       case 24:
>> +               return vfd->fr24;
>> +       case 48:
>> +               return vfd->fr48;
>> +       case 144:
>> +               return vfd->fr144;
>> +       }
>> +
>> +       if (!(rate % 25)) {
>> +               if (!vfd->bfr50)
>> +                       return false;
>> +
>> +               factor = rate / 25;
>> +       } else if (!(rate % 30)) {
>> +               if (!vfd->bfr60)
>> +                       return false;
>> +
>> +               factor = rate / 30;
>> +       }
>> +
>> +       for (i = 0; i < ARRAY_SIZE(factors); i++)
>> +               if (factor == factors[i] && (vfd->fr_fact & (1 << i)))
>> +                       return true;
>> +
>> +       return false;
>> +}
>> +
>> +#define OVT_PIXEL_CLOCK_GRANULARITY    1000            /* Hz */
>> +#define OVT_MIN_HTOTAL_GRANULARITY     8               /* pixels */
>> +#define OVT_MIN_VBLANK_DURATION        460000000       /* ps */
>> +#define OVT_MIN_VBLANK_LINES           20
>> +#define OVT_MIN_VSYNC_LEADING_EDGE     400             /* us */
>> +#define OVT_MIN_VSYNC_LE_LINES         14
>> +#define OVT_MIN_CLOCK_RATE_420         590000000       /* Hz */
>> +#define OVT_PIXEL_FACTOR_420           2
>> +#define OVT_MIN_HBLANK_444             80              /* pixels */
>> +#define OVT_MIN_HBLANK_420             128             /* pixels */
>> +#define OVT_MAX_CHUNK_RATE             650000000       /* Hz */
>> +#define OVT_AUDIO_PACKET_RATE          195000          /* Hz */
>> +#define OVT_AUDIO_PACKET_SIZE          32
>> +#define OVT_LINE_OVERHEAD              32
>> +#define OVT_HSYNC_WIDTH                32
>> +#define OVT_VSYNC_WIDTH                8
>> +
>> +static u32 calculate_ovt_min_vtotal(const struct cta_rid *rid, u64 max_vrate,
>> +                                   u32 vtotal_granularity)
>> +{
>> +       u64 max_active_time;
>> +       u32 min_line_time;
>> +       u32 min_vblank;
>> +       u32 min_vtotal;
>> +
>> +       /* step 2 */
>> +       max_active_time = div64_u64(1000000000000, max_vrate) -
>> +               (u64)OVT_MIN_VBLANK_DURATION;
>> +
>> +       min_line_time = div_u64(max_active_time, rid->vactive);
>> +
>> +       min_vblank = max_t(u64, (u64)OVT_MIN_VBLANK_LINES,
>> +                          DIV64_U64_ROUND_UP(OVT_MIN_VBLANK_DURATION,
>> +                                             min_line_time));
>> +
>> +       min_vtotal = rid->vactive + min_vblank;
>> +
>> +       if (min_vtotal % vtotal_granularity)
>> +               min_vtotal += vtotal_granularity - (min_vtotal %
>> +                                                   vtotal_granularity);
>> +
>> +       return min_vtotal;
>> +}
>> +
>> +static u32 calculate_ovt_min_htotal(const struct cta_rid *rid,
>> +                                   const u32 max_vrate,
>> +                                   const u32 min_vtotal,
>> +                                   u32 *min_hblank,
>> +                                   u32 *htotal_granularity)
>> +{
>> +       u32 max_audio_packets_per_line;
>> +       u32 htotal_granularity_chunk;
>> +       u64 min_pixel_clock_rate;
>> +       u32 min_line_rate;
>> +       u32 min_htotal;
>> +
>> +       /* step 3 */
>> +       min_line_rate = max_vrate * min_vtotal;
>> +
>> +       max_audio_packets_per_line = DIV_ROUND_UP(OVT_AUDIO_PACKET_RATE,
>> +                                                 min_line_rate);
>> +
>> +       /* step 4 */
>> +       *min_hblank = OVT_LINE_OVERHEAD + OVT_AUDIO_PACKET_SIZE *
>> +               max_audio_packets_per_line;
>> +
>> +       min_htotal = rid->hactive + max(OVT_MIN_HBLANK_444, *min_hblank);
>> +
>> +       min_pixel_clock_rate = max_vrate * min_htotal * min_vtotal;
>> +
>> +       htotal_granularity_chunk =
>> +               roundup_pow_of_two(DIV64_U64_ROUND_UP(min_pixel_clock_rate,
>> +                                                     OVT_MAX_CHUNK_RATE));
>> +
>> +       *htotal_granularity = max(OVT_MIN_HTOTAL_GRANULARITY,
>> +                                 htotal_granularity_chunk);
>> +
>> +       if (min_htotal % *htotal_granularity)
>> +               min_htotal += *htotal_granularity - (min_htotal %
>> +                                                    *htotal_granularity);
>> +
>> +       return min_htotal;
>> +}
>> +
>> +static u64 calculate_ovt_pixel_clock_rate(const struct cta_rid *rid,
>> +                                         const u32 max_vrate,
>> +                                         const u32 min_hblank,
>> +                                         u32 min_htotal,
>> +                                         u32 min_vtotal,
>> +                                         const u32 htotal_granularity,
>> +                                         const u32 vtotal_granularity,
>> +                                         u32 *htotal, u32 *vtotal)
>> +{
>> +       u32 resolution_granularity;
>> +       u64 pixel_clock_rate;
>> +       u64 min_resolution;
>> +       u64 rem;
>> +       u32 h;
>> +       u64 r;
>> +       u32 v;
>> +
>> +       resolution_granularity = OVT_PIXEL_CLOCK_GRANULARITY /
>> +               gcd(OVT_PIXEL_CLOCK_GRANULARITY, max_vrate);
>> +
>> +       do {
>> +               /* step 5 */
>> +               min_resolution = 0;
>> +               v = min_vtotal;
>> +
>> +               goto loop_end;
>> +
>> +               while (!min_resolution || r <= min_resolution) {
>> +                       goto inner_loop_end;
>> +
>> +                       while (rem || div64_u64(max_vrate * r, (h & ~(h - 1))) >
>> +                              OVT_MAX_CHUNK_RATE) {
>> +                               h += htotal_granularity;
>> +                               r = (u64)h * (u64)v;
>> +inner_loop_end:
>> +                               div64_u64_rem(r, resolution_granularity, &rem);
>> +                       }
>> +
>> +                       if (!min_resolution || r < min_resolution) {
>> +                               *htotal = h;
>> +                               *vtotal = v;
>> +                               min_resolution = r;
>> +                       }
>> +
>> +                       v += vtotal_granularity;
>> +
>> +loop_end:
>> +                       h = min_htotal;
>> +                       r = (u64)h * (u64)v;
>> +               }
>> +
>> +               pixel_clock_rate = max_vrate * min_resolution;
>> +
>> +               /* step 6 */
>> +               min_htotal = rid->hactive + max(OVT_MIN_HBLANK_420,
>> +                                               OVT_PIXEL_FACTOR_420 *
>> +                                               min_hblank);
>> +
>> +       } while (pixel_clock_rate >= OVT_MIN_CLOCK_RATE_420 &&
>> +                *htotal < min_htotal);
>> +
>> +       return pixel_clock_rate;
>> +}
>> +
>> +static const struct cta_rid *find_rid(u8 rid)
>> +{
>> +       if (!rid || rid >= ARRAY_SIZE(rids))
>> +               return NULL;
>> +
>> +       return &rids[rid];
>> +}
>> +
>> +/* OVT Algorthim as specified in CTA-861-I */
>> +struct drm_display_mode *drm_ovt_mode(struct drm_device *dev, int r_id,
>> +                                     int vrefresh)
>> +{
>> +       const struct cta_rid *rid = find_rid(r_id);
>> +       struct drm_display_mode *mode;
>> +       u32 vtotal_granularity = 1;
>> +       u32 htotal_granularity;
>> +       u32 max_vrate = vrefresh;
>> +       u64 pixel_clock_rate;
>> +       u32 vsync_position;
>> +       u32 min_hblank;
>> +       u32 min_htotal;
>> +       u32 min_vtotal;
>> +       u32 htotal;
>> +       u32 vtotal;
>> +
>> +       if (!rid)
>> +               return NULL;
>> +
>> +       /* step 1 */
>> +       switch (vrefresh) {
>> +       case 24:
>> +       case 25:
>> +               max_vrate = 30;
>> +               fallthrough;
>> +       case 30:
>> +               vtotal_granularity = 20;
>> +               break;
>> +       case 48:
>> +       case 50:
>> +               max_vrate = 60;
>> +               fallthrough;
>> +       case 60:
>> +               vtotal_granularity = 20;
>> +               break;
>> +       case 100:
>> +               max_vrate = 120;
>> +               fallthrough;
>> +       case 120:
>> +               vtotal_granularity = 5;
>> +               break;
>> +       case 200:
>> +               max_vrate = 240;
>> +               fallthrough;
>> +       case 240:
>> +               vtotal_granularity = 5;
>> +               break;
>> +       case 300:
>> +               max_vrate = 360;
>> +               fallthrough;
>> +       case 360:
>> +               vtotal_granularity = 5;
>> +               break;
>> +       case 400:
>> +               max_vrate = 480;
>> +               fallthrough;
>> +       case 480:
>> +               vtotal_granularity = 5;
>> +               break;
>> +       }
>> +
>> +       min_vtotal = calculate_ovt_min_vtotal(rid, max_vrate,
>> +                                             vtotal_granularity);
>> +
>> +       min_htotal = calculate_ovt_min_htotal(rid, max_vrate, min_vtotal,
>> +                                             &min_hblank, &htotal_granularity);
>> +
>> +       pixel_clock_rate = calculate_ovt_pixel_clock_rate(rid, max_vrate,
>> +                                                         min_hblank,
>> +                                                         min_htotal,
>> +                                                         min_vtotal,
>> +                                                         htotal_granularity,
>> +                                                         vtotal_granularity,
>> +                                                         &htotal, &vtotal);
>> +
>> +       /* step 7 */
>> +       vtotal = vtotal * max_vrate / (u32)vrefresh;
>> +
>> +       /* step 8 */
>> +       vsync_position = max(OVT_MIN_VSYNC_LE_LINES,
>> +                            DIV64_U64_ROUND_UP((u64)OVT_MIN_VSYNC_LE_LINES *
>> +                                               pixel_clock_rate,
>> +                                               (u64)htotal * (u64)1000000));
>> +
>> +       mode = drm_mode_create(dev);
>> +
>> +       if (!mode)
>> +               return NULL;
>> +
>> +       /* step 10 */
>> +       mode->clock = div_u64(pixel_clock_rate, 1000);
>> +       mode->hdisplay = rid->hactive;
>> +       mode->hsync_start = htotal - OVT_HSYNC_WIDTH * 2;
>> +       mode->hsync_end = mode->hsync_start + OVT_HSYNC_WIDTH;
>> +       mode->htotal = htotal;
>> +
>> +       mode->vdisplay = rid->vactive;
>> +       mode->vsync_start = vtotal - vsync_position;
>> +       mode->vsync_end = mode->vsync_start + OVT_VSYNC_WIDTH;
>> +       mode->vtotal = vtotal;
>> +
>> +       return mode;
>> +}
>> +
>> +static u8 find_vic(u8 rid, int rate_idx)
>> +{
>> +       if (video_format_frame_rates[rate_idx] > 120 || !find_rid(rid))
>> +               return 0;
>> +
>> +       return rid_to_vic[rid][rate_idx - 1];
>> +}
>> +
>> +/* CTA-861 Video Format Data Block (CTA VFDB) */
>> +static int add_modes_from_vfdb(struct drm_connector *connector,
>> +                              const struct cea_db *db)
>> +{
>> +       const struct drm_display_info *info = &connector->display_info;
>> +       int vfdb_len = cea_db_payload_len(db);
>> +       struct drm_display_mode *mode;
>> +       struct cta_vfd vfd;
>> +       int num_modes = 0;
>> +       int rate_idx;
>> +       int vfd_len;
>> +       int rate;
>> +       int i;
>> +
>> +       if (!vfdb_len)
>> +               return 0;
>> +
>> +       vfd_len = (db->data[0] & 0x3);
>> +
>> +       if (!vfd_len)
>> +               return 0;
>> +
>> +       vfd_len++;
>> +       vfdb_len--;
>> +       vfdb_len -= (vfdb_len % vfd_len);
>> +
>> +       for (i = 0; i < vfdb_len; i += vfd_len) {
>> +               parse_cta_vfd(&vfd, &db->data[i + 1], vfd_len);
>> +
>> +               for (rate_idx = 1; rate_idx <
>> +                    ARRAY_SIZE(video_format_frame_rates); rate_idx++) {
>> +                       rate = video_format_frame_rates[rate_idx];
>> +
>> +                       if (!vfd_has_fr(&vfd, rate) || find_vic(vfd.rid,
>> +                                                               rate_idx))
>> +                               continue;
>> +
>> +                       mode = drm_ovt_mode(connector->dev, vfd.rid, rate);
>> +
>> +                       if (!mode)
>> +                               continue;
>> +
>> +                       mode->height_mm = info->height_mm;
>> +                       mode->width_mm = info->width_mm;
>> +
>> +                       drm_mode_probed_add(connector, mode);
>> +                       num_modes++;
>> +               }
>> +       }
>> +
>> +       return num_modes;
>> +}
>> +
>>  /*
>>   * CTA-861 YCbCr 4:2:0 Capability Map Data Block (CTA Y420CMDB)
>>   *
>> @@ -5318,6 +5781,8 @@ static int add_cea_modes(struct drm_connector *connector,
>>                         /* Add 4:2:0(only) modes present in EDID */
>>                         modes += do_y420vdb_modes(connector, vdb420,
>>                                                   cea_db_payload_len(db) - 1);
>> +               } else if (cea_db_tag(db) == CTA_DB_VIDEO_FORMAT) {
>> +                       modes += add_modes_from_vfdb(connector, db);
>>                 }
>>         }
>>         cea_db_iter_end(&iter);
>> diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
>> index eaac5e665892..78831207ec65 100644
>> --- a/include/drm/drm_edid.h
>> +++ b/include/drm/drm_edid.h
>> @@ -450,6 +450,9 @@ struct drm_display_mode *
>>  drm_display_mode_from_cea_vic(struct drm_device *dev,
>>                               u8 video_code);
>>
>> +struct drm_display_mode *drm_ovt_mode(struct drm_device *dev, int rid,
>> +                                     int vrefresh);
>> +
>>  /* Interface based on struct drm_edid */
>>  const struct drm_edid *drm_edid_alloc(const void *edid, size_t size);
>>  const struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid);
>> --
>> 2.46.1
>>

-- 
Jani Nikula, Intel




[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux