#include <linux/bitfield.h>
#include <linux/byteorder/generic.h>
#include <linux/cec.h>
-#include <linux/gcd.h>
#include <linux/export.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
{ 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.
*
#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 */
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;
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)
*
/* 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);