12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498 |
- From 0396d0bd56a5c2941c02f858599de3b0cbc93c75 Mon Sep 17 00:00:00 2001
- From: Dave Stevenson <[email protected]>
- Date: Mon, 7 Sep 2020 17:32:27 +0100
- Subject: [PATCH] drm/vc4: Add firmware-kms mode
- This is a squash of all firmware-kms related patches from previous
- branches, up to and including
- "drm/vc4: Set the possible crtcs mask correctly for planes with FKMS"
- plus a couple of minor fixups for the 5.9 branch.
- Please refer to earlier branches for full history.
- This patch includes work by Eric Anholt, James Hughes, Phil Elwell,
- Dave Stevenson, Dom Cobley, and Jonathon Bell.
- Signed-off-by: Dave Stevenson <[email protected]>
- drm/vc4: Fixup firmware-kms after "drm/atomic: Pass the full state to CRTC atomic enable/disable"
- Prototype for those calls changed, so amend fkms (which isn't
- upstream) to match.
- Signed-off-by: Dave Stevenson <[email protected]>
- drm/vc4: Fixup fkms for API change
- Atomic flush and check changed API, so fix up the downstream-only
- FKMS driver.
- Signed-off-by: Dave Stevenson <[email protected]>
- drm/vc4: Make normalize_zpos conditional on using fkms
- Eric's view was that there was no point in having zpos
- support on vc4 as all the planes had the same functionality.
- Can be later squashed into (and fixes):
- drm/vc4: Add firmware-kms mode
- Signed-off-by: Dom Cobley <[email protected]>
- drm/vc4: FKMS: Change of Broadcast RGB mode needs a mode change
- The Broadcast RGB (aka HDMI limited/full range) property is only
- notified to the firmware on mode change, so this needs to be
- signalled when set.
- https://github.com/raspberrypi/firmware/issues/1580
- Signed-off-by: Dave Stevenson <[email protected]>
- vc4/drv: Only notify firmware of display done with kms
- fkms driver still wants firmware display to be active
- Signed-off-by: Dom Cobley <[email protected]>
- ydrm/vc4: fkms: Fix margin calculations for the right/bottom edges
- The calculations clipped the right/bottom edge of the clipped
- range based on the left/top margins.
- https://github.com/raspberrypi/linux/issues/4447
- Signed-off-by: Dave Stevenson <[email protected]>
- drm/vc4: fkms: Use new devm_rpi_firmware_get api
- drm/kms: Add allow_fb_modifiers
- Signed-off-by: Dom Cobley <[email protected]>
- ---
- drivers/gpu/drm/vc4/Makefile | 1 +
- drivers/gpu/drm/vc4/vc4_debugfs.c | 2 +-
- drivers/gpu/drm/vc4/vc4_drv.c | 29 +-
- drivers/gpu/drm/vc4/vc4_drv.h | 7 +
- drivers/gpu/drm/vc4/vc4_firmware_kms.c | 1994 ++++++++++++++++++++
- drivers/gpu/drm/vc4/vc4_kms.c | 35 +-
- drivers/gpu/drm/vc4/vc_image_types.h | 175 ++
- include/soc/bcm2835/raspberrypi-firmware.h | 6 +
- 8 files changed, 2235 insertions(+), 14 deletions(-)
- create mode 100644 drivers/gpu/drm/vc4/vc4_firmware_kms.c
- create mode 100644 drivers/gpu/drm/vc4/vc_image_types.h
- --- a/drivers/gpu/drm/vc4/Makefile
- +++ b/drivers/gpu/drm/vc4/Makefile
- @@ -9,6 +9,7 @@ vc4-y := \
- vc4_dpi.o \
- vc4_dsi.o \
- vc4_fence.o \
- + vc4_firmware_kms.o \
- vc4_kms.o \
- vc4_gem.o \
- vc4_hdmi.o \
- --- a/drivers/gpu/drm/vc4/vc4_debugfs.c
- +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
- @@ -27,7 +27,7 @@ vc4_debugfs_init(struct drm_minor *minor
- struct vc4_dev *vc4 = to_vc4_dev(minor->dev);
- struct vc4_debugfs_info_entry *entry;
-
- - if (!of_device_is_compatible(vc4->hvs->pdev->dev.of_node,
- + if (vc4->hvs && !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);
- --- a/drivers/gpu/drm/vc4/vc4_drv.c
- +++ b/drivers/gpu/drm/vc4/vc4_drv.c
- @@ -235,6 +235,18 @@ const struct of_device_id vc4_dma_range_
- {}
- };
-
- +/*
- + * we need this helper function for determining presence of fkms
- + * before it's been bound
- + */
- +static bool firmware_kms(void)
- +{
- + return of_device_is_available(of_find_compatible_node(NULL, NULL,
- + "raspberrypi,rpi-firmware-kms")) ||
- + of_device_is_available(of_find_compatible_node(NULL, NULL,
- + "raspberrypi,rpi-firmware-kms-2711"));
- +}
- +
- static int vc4_drm_bind(struct device *dev)
- {
- struct platform_device *pdev = to_platform_device(dev);
- @@ -308,7 +320,7 @@ static int vc4_drm_bind(struct device *d
- if (ret)
- return ret;
-
- - if (firmware) {
- + if (firmware && !firmware_kms()) {
- ret = rpi_firmware_property(firmware,
- RPI_FIRMWARE_NOTIFY_DISPLAY_DONE,
- NULL, 0);
- @@ -322,16 +334,20 @@ static int vc4_drm_bind(struct device *d
- if (ret)
- return ret;
-
- - ret = vc4_plane_create_additional_planes(drm);
- - if (ret)
- - goto unbind_all;
- + if (!vc4->firmware_kms) {
- + ret = vc4_plane_create_additional_planes(drm);
- + if (ret)
- + goto unbind_all;
- + }
-
- ret = vc4_kms_load(drm);
- if (ret < 0)
- goto unbind_all;
-
- - drm_for_each_crtc(crtc, drm)
- - vc4_crtc_disable_at_boot(crtc);
- + if (!vc4->firmware_kms) {
- + drm_for_each_crtc(crtc, drm)
- + vc4_crtc_disable_at_boot(crtc);
- + }
-
- ret = drm_dev_register(drm, 0);
- if (ret < 0)
- @@ -378,6 +394,7 @@ static struct platform_driver *const com
- &vc4_dsi_driver,
- &vc4_txp_driver,
- &vc4_crtc_driver,
- + &vc4_firmware_kms_driver,
- &vc4_v3d_driver,
- };
-
- --- a/drivers/gpu/drm/vc4/vc4_drv.h
- +++ b/drivers/gpu/drm/vc4/vc4_drv.h
- @@ -76,11 +76,15 @@ struct vc4_dev {
-
- unsigned int irq;
-
- + bool firmware_kms;
- + struct rpi_firmware *firmware;
- +
- struct vc4_hvs *hvs;
- struct vc4_v3d *v3d;
- struct vc4_dpi *dpi;
- struct vc4_vec *vec;
- struct vc4_txp *txp;
- + struct vc4_fkms *fkms;
-
- struct vc4_hang_state *hang_state;
-
- @@ -896,6 +900,9 @@ extern struct platform_driver vc4_dsi_dr
- /* vc4_fence.c */
- extern const struct dma_fence_ops vc4_fence_ops;
-
- +/* vc4_firmware_kms.c */
- +extern struct platform_driver vc4_firmware_kms_driver;
- +
- /* vc4_gem.c */
- int vc4_gem_init(struct drm_device *dev);
- int vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
- --- /dev/null
- +++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
- @@ -0,0 +1,1994 @@
- +// SPDX-License-Identifier: GPL-2.0-only
- +/*
- + * Copyright (C) 2016 Broadcom
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License version 2 as
- + * published by the Free Software Foundation.
- + */
- +
- +/**
- + * DOC: VC4 firmware KMS module.
- + *
- + * As a hack to get us from the current closed source driver world
- + * toward a totally open stack, implement KMS on top of the Raspberry
- + * Pi's firmware display stack.
- + */
- +
- +#include <drm/drm_atomic_helper.h>
- +#include <drm/drm_crtc_helper.h>
- +#include <drm/drm_drv.h>
- +#include <drm/drm_fb_cma_helper.h>
- +#include <drm/drm_fourcc.h>
- +#include <drm/drm_gem_atomic_helper.h>
- +#include <drm/drm_plane_helper.h>
- +#include <drm/drm_probe_helper.h>
- +#include <drm/drm_vblank.h>
- +
- +#include <linux/component.h>
- +#include <linux/clk.h>
- +#include <linux/debugfs.h>
- +#include <linux/module.h>
- +
- +#include <soc/bcm2835/raspberrypi-firmware.h>
- +
- +#include "vc4_drv.h"
- +#include "vc4_regs.h"
- +#include "vc_image_types.h"
- +
- +int fkms_max_refresh_rate = 85;
- +module_param(fkms_max_refresh_rate, int, 0644);
- +MODULE_PARM_DESC(fkms_max_refresh_rate, "Max supported refresh rate");
- +
- +struct get_display_cfg {
- + u32 max_pixel_clock[2]; //Max pixel clock for each display
- +};
- +
- +struct vc4_fkms {
- + struct get_display_cfg cfg;
- + bool bcm2711;
- +};
- +
- +#define PLANES_PER_CRTC 8
- +
- +struct set_plane {
- + u8 display;
- + u8 plane_id;
- + u8 vc_image_type;
- + s8 layer;
- +
- + u16 width;
- + u16 height;
- +
- + u16 pitch;
- + u16 vpitch;
- +
- + u32 src_x; /* 16p16 */
- + u32 src_y; /* 16p16 */
- +
- + u32 src_w; /* 16p16 */
- + u32 src_h; /* 16p16 */
- +
- + s16 dst_x;
- + s16 dst_y;
- +
- + u16 dst_w;
- + u16 dst_h;
- +
- + u8 alpha;
- + u8 num_planes;
- + u8 is_vu;
- + u8 color_encoding;
- +
- + u32 planes[4]; /* DMA address of each plane */
- +
- + u32 transform;
- +};
- +
- +/* Values for the transform field */
- +#define TRANSFORM_NO_ROTATE 0
- +#define TRANSFORM_ROTATE_180 BIT(1)
- +#define TRANSFORM_FLIP_HRIZ BIT(16)
- +#define TRANSFORM_FLIP_VERT BIT(17)
- +
- +struct mailbox_set_plane {
- + struct rpi_firmware_property_tag_header tag;
- + struct set_plane plane;
- +};
- +
- +struct mailbox_blank_display {
- + struct rpi_firmware_property_tag_header tag1;
- + u32 display;
- + struct rpi_firmware_property_tag_header tag2;
- + u32 blank;
- +};
- +
- +struct mailbox_display_pwr {
- + struct rpi_firmware_property_tag_header tag1;
- + u32 display;
- + u32 state;
- +};
- +
- +struct mailbox_get_edid {
- + struct rpi_firmware_property_tag_header tag1;
- + u32 block;
- + u32 display_number;
- + u8 edid[128];
- +};
- +
- +struct set_timings {
- + u8 display;
- + u8 padding;
- + u16 video_id_code;
- +
- + u32 clock; /* in kHz */
- +
- + u16 hdisplay;
- + u16 hsync_start;
- +
- + u16 hsync_end;
- + u16 htotal;
- +
- + u16 hskew;
- + u16 vdisplay;
- +
- + u16 vsync_start;
- + u16 vsync_end;
- +
- + u16 vtotal;
- + u16 vscan;
- +
- + u16 vrefresh;
- + u16 padding2;
- +
- + u32 flags;
- +#define TIMINGS_FLAGS_H_SYNC_POS BIT(0)
- +#define TIMINGS_FLAGS_H_SYNC_NEG 0
- +#define TIMINGS_FLAGS_V_SYNC_POS BIT(1)
- +#define TIMINGS_FLAGS_V_SYNC_NEG 0
- +#define TIMINGS_FLAGS_INTERLACE BIT(2)
- +
- +#define TIMINGS_FLAGS_ASPECT_MASK GENMASK(7, 4)
- +#define TIMINGS_FLAGS_ASPECT_NONE (0 << 4)
- +#define TIMINGS_FLAGS_ASPECT_4_3 (1 << 4)
- +#define TIMINGS_FLAGS_ASPECT_16_9 (2 << 4)
- +#define TIMINGS_FLAGS_ASPECT_64_27 (3 << 4)
- +#define TIMINGS_FLAGS_ASPECT_256_135 (4 << 4)
- +
- +/* Limited range RGB flag. Not set corresponds to full range. */
- +#define TIMINGS_FLAGS_RGB_LIMITED BIT(8)
- +/* DVI monitor, therefore disable infoframes. Not set corresponds to HDMI. */
- +#define TIMINGS_FLAGS_DVI BIT(9)
- +/* Double clock */
- +#define TIMINGS_FLAGS_DBL_CLK BIT(10)
- +};
- +
- +struct mailbox_set_mode {
- + struct rpi_firmware_property_tag_header tag1;
- + struct set_timings timings;
- +};
- +
- +static const struct vc_image_format {
- + u32 drm; /* DRM_FORMAT_* */
- + u32 vc_image; /* VC_IMAGE_* */
- + u32 is_vu;
- +} vc_image_formats[] = {
- + {
- + .drm = DRM_FORMAT_XRGB8888,
- + .vc_image = VC_IMAGE_XRGB8888,
- + },
- + {
- + .drm = DRM_FORMAT_ARGB8888,
- + .vc_image = VC_IMAGE_ARGB8888,
- + },
- +/*
- + * FIXME: Need to resolve which DRM format goes to which vc_image format
- + * for the remaining RGBA and RGBX formats.
- + * {
- + * .drm = DRM_FORMAT_ABGR8888,
- + * .vc_image = VC_IMAGE_RGBA8888,
- + * },
- + * {
- + * .drm = DRM_FORMAT_XBGR8888,
- + * .vc_image = VC_IMAGE_RGBA8888,
- + * },
- + */
- + {
- + .drm = DRM_FORMAT_RGB565,
- + .vc_image = VC_IMAGE_RGB565,
- + },
- + {
- + .drm = DRM_FORMAT_RGB888,
- + .vc_image = VC_IMAGE_BGR888,
- + },
- + {
- + .drm = DRM_FORMAT_BGR888,
- + .vc_image = VC_IMAGE_RGB888,
- + },
- + {
- + .drm = DRM_FORMAT_YUV422,
- + .vc_image = VC_IMAGE_YUV422PLANAR,
- + },
- + {
- + .drm = DRM_FORMAT_YUV420,
- + .vc_image = VC_IMAGE_YUV420,
- + },
- + {
- + .drm = DRM_FORMAT_YVU420,
- + .vc_image = VC_IMAGE_YUV420,
- + .is_vu = 1,
- + },
- + {
- + .drm = DRM_FORMAT_NV12,
- + .vc_image = VC_IMAGE_YUV420SP,
- + },
- + {
- + .drm = DRM_FORMAT_NV21,
- + .vc_image = VC_IMAGE_YUV420SP,
- + .is_vu = 1,
- + },
- + {
- + .drm = DRM_FORMAT_P030,
- + .vc_image = VC_IMAGE_YUV10COL,
- + },
- +};
- +
- +static const struct vc_image_format *vc4_get_vc_image_fmt(u32 drm_format)
- +{
- + unsigned int i;
- +
- + for (i = 0; i < ARRAY_SIZE(vc_image_formats); i++) {
- + if (vc_image_formats[i].drm == drm_format)
- + return &vc_image_formats[i];
- + }
- +
- + return NULL;
- +}
- +
- +/* The firmware delivers a vblank interrupt to us through the SMI
- + * hardware, which has only this one register.
- + */
- +#define SMICS 0x0
- +#define SMIDSW0 0x14
- +#define SMIDSW1 0x1C
- +#define SMICS_INTERRUPTS (BIT(9) | BIT(10) | BIT(11))
- +
- +/* Flag to denote that the firmware is giving multiple display callbacks */
- +#define SMI_NEW 0xabcd0000
- +
- +#define vc4_crtc vc4_kms_crtc
- +#define to_vc4_crtc to_vc4_kms_crtc
- +struct vc4_crtc {
- + struct drm_crtc base;
- + struct drm_encoder *encoder;
- + struct drm_connector *connector;
- + void __iomem *regs;
- +
- + struct drm_pending_vblank_event *event;
- + bool vblank_enabled;
- + u32 display_number;
- + u32 display_type;
- +};
- +
- +static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
- +{
- + return container_of(crtc, struct vc4_crtc, base);
- +}
- +
- +struct vc4_fkms_encoder {
- + struct drm_encoder base;
- + bool hdmi_monitor;
- + bool rgb_range_selectable;
- + int display_num;
- +};
- +
- +static inline struct vc4_fkms_encoder *
- +to_vc4_fkms_encoder(struct drm_encoder *encoder)
- +{
- + return container_of(encoder, struct vc4_fkms_encoder, base);
- +}
- +
- +/* "Broadcast RGB" property.
- + * Allows overriding of HDMI full or limited range RGB
- + */
- +#define VC4_BROADCAST_RGB_AUTO 0
- +#define VC4_BROADCAST_RGB_FULL 1
- +#define VC4_BROADCAST_RGB_LIMITED 2
- +
- +/* VC4 FKMS connector KMS struct */
- +struct vc4_fkms_connector {
- + struct drm_connector base;
- +
- + /* Since the connector is attached to just the one encoder,
- + * this is the reference to it so we can do the best_encoder()
- + * hook.
- + */
- + struct drm_encoder *encoder;
- + struct vc4_dev *vc4_dev;
- + u32 display_number;
- + u32 display_type;
- +
- + struct drm_property *broadcast_rgb_property;
- +};
- +
- +static inline struct vc4_fkms_connector *
- +to_vc4_fkms_connector(struct drm_connector *connector)
- +{
- + return container_of(connector, struct vc4_fkms_connector, base);
- +}
- +
- +/* VC4 FKMS connector state */
- +struct vc4_fkms_connector_state {
- + struct drm_connector_state base;
- +
- + int broadcast_rgb;
- +};
- +
- +#define to_vc4_fkms_connector_state(x) \
- + container_of(x, struct vc4_fkms_connector_state, base)
- +
- +static u32 vc4_get_display_type(u32 display_number)
- +{
- + const u32 display_types[] = {
- + /* The firmware display (DispmanX) IDs map to specific types in
- + * a fixed manner.
- + */
- + DRM_MODE_ENCODER_DSI, /* MAIN_LCD - DSI or DPI */
- + DRM_MODE_ENCODER_DSI, /* AUX_LCD */
- + DRM_MODE_ENCODER_TMDS, /* HDMI0 */
- + DRM_MODE_ENCODER_TVDAC, /* VEC */
- + DRM_MODE_ENCODER_NONE, /* FORCE_LCD */
- + DRM_MODE_ENCODER_NONE, /* FORCE_TV */
- + DRM_MODE_ENCODER_NONE, /* FORCE_OTHER */
- + DRM_MODE_ENCODER_TMDS, /* HDMI1 */
- + DRM_MODE_ENCODER_NONE, /* FORCE_TV2 */
- + };
- + return display_number > ARRAY_SIZE(display_types) - 1 ?
- + DRM_MODE_ENCODER_NONE : display_types[display_number];
- +}
- +
- +/* Firmware's structure for making an FB mbox call. */
- +struct fbinfo_s {
- + u32 xres, yres, xres_virtual, yres_virtual;
- + u32 pitch, bpp;
- + u32 xoffset, yoffset;
- + u32 base;
- + u32 screen_size;
- + u16 cmap[256];
- +};
- +
- +struct vc4_fkms_plane {
- + struct drm_plane base;
- + struct fbinfo_s *fbinfo;
- + dma_addr_t fbinfo_bus_addr;
- + u32 pitch;
- + struct mailbox_set_plane mb;
- +};
- +
- +static inline struct vc4_fkms_plane *to_vc4_fkms_plane(struct drm_plane *plane)
- +{
- + return (struct vc4_fkms_plane *)plane;
- +}
- +
- +static int vc4_plane_set_blank(struct drm_plane *plane, bool blank)
- +{
- + struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
- + struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
- + struct mailbox_set_plane blank_mb = {
- + .tag = { RPI_FIRMWARE_SET_PLANE, sizeof(struct set_plane), 0 },
- + .plane = {
- + .display = vc4_plane->mb.plane.display,
- + .plane_id = vc4_plane->mb.plane.plane_id,
- + }
- + };
- + static const char * const plane_types[] = {
- + "overlay",
- + "primary",
- + "cursor"
- + };
- + int ret;
- +
- + DRM_DEBUG_ATOMIC("[PLANE:%d:%s] %s plane %s",
- + plane->base.id, plane->name, plane_types[plane->type],
- + blank ? "blank" : "unblank");
- +
- + if (blank)
- + ret = rpi_firmware_property_list(vc4->firmware, &blank_mb,
- + sizeof(blank_mb));
- + else
- + ret = rpi_firmware_property_list(vc4->firmware, &vc4_plane->mb,
- + sizeof(vc4_plane->mb));
- +
- + WARN_ONCE(ret, "%s: firmware call failed. Please update your firmware",
- + __func__);
- + return ret;
- +}
- +
- +static void vc4_fkms_crtc_get_margins(struct drm_crtc_state *state,
- + unsigned int *left, unsigned int *right,
- + unsigned int *top, unsigned int *bottom)
- +{
- + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
- + struct drm_connector_state *conn_state;
- + struct drm_connector *conn;
- + int i;
- +
- + *left = vc4_state->margins.left;
- + *right = vc4_state->margins.right;
- + *top = vc4_state->margins.top;
- + *bottom = vc4_state->margins.bottom;
- +
- + /* We have to interate over all new connector states because
- + * vc4_fkms_crtc_get_margins() might be called before
- + * vc4_fkms_crtc_atomic_check() which means margins info in
- + * vc4_crtc_state might be outdated.
- + */
- + for_each_new_connector_in_state(state->state, conn, conn_state, i) {
- + if (conn_state->crtc != state->crtc)
- + continue;
- +
- + *left = conn_state->tv.margins.left;
- + *right = conn_state->tv.margins.right;
- + *top = conn_state->tv.margins.top;
- + *bottom = conn_state->tv.margins.bottom;
- + break;
- + }
- +}
- +
- +static int vc4_fkms_margins_adj(struct drm_plane_state *pstate,
- + struct set_plane *plane)
- +{
- + unsigned int left, right, top, bottom;
- + int adjhdisplay, adjvdisplay;
- + struct drm_crtc_state *crtc_state;
- +
- + crtc_state = drm_atomic_get_new_crtc_state(pstate->state,
- + pstate->crtc);
- +
- + vc4_fkms_crtc_get_margins(crtc_state, &left, &right, &top, &bottom);
- +
- + if (!left && !right && !top && !bottom)
- + return 0;
- +
- + if (left + right >= crtc_state->mode.hdisplay ||
- + top + bottom >= crtc_state->mode.vdisplay)
- + return -EINVAL;
- +
- + adjhdisplay = crtc_state->mode.hdisplay - (left + right);
- + plane->dst_x = DIV_ROUND_CLOSEST(plane->dst_x * adjhdisplay,
- + (int)crtc_state->mode.hdisplay);
- + plane->dst_x += left;
- + if (plane->dst_x > (int)(crtc_state->mode.hdisplay - right))
- + plane->dst_x = crtc_state->mode.hdisplay - right;
- +
- + adjvdisplay = crtc_state->mode.vdisplay - (top + bottom);
- + plane->dst_y = DIV_ROUND_CLOSEST(plane->dst_y * adjvdisplay,
- + (int)crtc_state->mode.vdisplay);
- + plane->dst_y += top;
- + if (plane->dst_y > (int)(crtc_state->mode.vdisplay - bottom))
- + plane->dst_y = crtc_state->mode.vdisplay - bottom;
- +
- + plane->dst_w = DIV_ROUND_CLOSEST(plane->dst_w * adjhdisplay,
- + crtc_state->mode.hdisplay);
- + plane->dst_h = DIV_ROUND_CLOSEST(plane->dst_h * adjvdisplay,
- + crtc_state->mode.vdisplay);
- +
- + if (!plane->dst_w || !plane->dst_h)
- + return -EINVAL;
- +
- + return 0;
- +}
- +
- +static void vc4_plane_atomic_update(struct drm_plane *plane,
- + struct drm_atomic_state *old_state)
- +{
- + struct drm_plane_state *state = plane->state;
- +
- + /*
- + * Do NOT set now, as we haven't checked if the crtc is active or not.
- + * Set from vc4_plane_set_blank instead.
- + *
- + * If the CRTC is on (or going to be on) and we're enabled,
- + * then unblank. Otherwise, stay blank until CRTC enable.
- + */
- + if (state->crtc->state->active)
- + vc4_plane_set_blank(plane, false);
- +}
- +
- +static void vc4_plane_atomic_disable(struct drm_plane *plane,
- + struct drm_atomic_state *old_state)
- +{
- + struct drm_plane_state *state = plane->state;
- + struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
- +
- + DRM_DEBUG_ATOMIC("[PLANE:%d:%s] plane disable %dx%d@%d +%d,%d\n",
- + plane->base.id, plane->name,
- + state->crtc_w,
- + state->crtc_h,
- + vc4_plane->mb.plane.vc_image_type,
- + state->crtc_x,
- + state->crtc_y);
- + vc4_plane_set_blank(plane, true);
- +}
- +
- +static bool plane_enabled(struct drm_plane_state *state)
- +{
- + return state->fb && state->crtc;
- +}
- +
- +static int vc4_plane_to_mb(struct drm_plane *plane,
- + struct mailbox_set_plane *mb,
- + struct drm_plane_state *state)
- +{
- + struct drm_framebuffer *fb = state->fb;
- + struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
- + const struct drm_format_info *drm_fmt = fb->format;
- + const struct vc_image_format *vc_fmt =
- + vc4_get_vc_image_fmt(drm_fmt->format);
- + int num_planes = fb->format->num_planes;
- + unsigned int rotation;
- +
- + mb->plane.vc_image_type = vc_fmt->vc_image;
- + mb->plane.width = fb->width;
- + mb->plane.height = fb->height;
- + mb->plane.pitch = fb->pitches[0];
- + mb->plane.src_w = state->src_w;
- + mb->plane.src_h = state->src_h;
- + mb->plane.src_x = state->src_x;
- + mb->plane.src_y = state->src_y;
- + mb->plane.dst_w = state->crtc_w;
- + mb->plane.dst_h = state->crtc_h;
- + mb->plane.dst_x = state->crtc_x;
- + mb->plane.dst_y = state->crtc_y;
- + mb->plane.alpha = state->alpha >> 8;
- + mb->plane.layer = state->normalized_zpos ?
- + state->normalized_zpos : -127;
- + mb->plane.num_planes = num_planes;
- + mb->plane.is_vu = vc_fmt->is_vu;
- + mb->plane.planes[0] = bo->paddr + fb->offsets[0];
- +
- + rotation = drm_rotation_simplify(state->rotation,
- + DRM_MODE_ROTATE_0 |
- + DRM_MODE_REFLECT_X |
- + DRM_MODE_REFLECT_Y);
- +
- + mb->plane.transform = TRANSFORM_NO_ROTATE;
- + if (rotation & DRM_MODE_REFLECT_X)
- + mb->plane.transform |= TRANSFORM_FLIP_HRIZ;
- + if (rotation & DRM_MODE_REFLECT_Y)
- + mb->plane.transform |= TRANSFORM_FLIP_VERT;
- +
- + vc4_fkms_margins_adj(state, &mb->plane);
- +
- + if (num_planes > 1) {
- + /* Assume this must be YUV */
- + /* Makes assumptions on the stride for the chroma planes as we
- + * can't easily plumb in non-standard pitches.
- + */
- + mb->plane.planes[1] = bo->paddr + fb->offsets[1];
- + if (num_planes > 2)
- + mb->plane.planes[2] = bo->paddr + fb->offsets[2];
- + else
- + mb->plane.planes[2] = 0;
- +
- + /* Special case the YUV420 with U and V as line interleaved
- + * planes as we have special handling for that case.
- + */
- + if (num_planes == 3 &&
- + (fb->offsets[2] - fb->offsets[1]) == fb->pitches[1])
- + mb->plane.vc_image_type = VC_IMAGE_YUV420_S;
- +
- + switch (state->color_encoding) {
- + default:
- + case DRM_COLOR_YCBCR_BT601:
- + if (state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
- + mb->plane.color_encoding =
- + VC_IMAGE_YUVINFO_CSC_ITUR_BT601;
- + else
- + mb->plane.color_encoding =
- + VC_IMAGE_YUVINFO_CSC_JPEG_JFIF;
- + break;
- + case DRM_COLOR_YCBCR_BT709:
- + /* Currently no support for a full range BT709 */
- + mb->plane.color_encoding =
- + VC_IMAGE_YUVINFO_CSC_ITUR_BT709;
- + break;
- + case DRM_COLOR_YCBCR_BT2020:
- + /* Currently no support for a full range BT2020 */
- + mb->plane.color_encoding =
- + VC_IMAGE_YUVINFO_CSC_REC_2020;
- + break;
- + }
- + } else {
- + mb->plane.planes[1] = 0;
- + mb->plane.planes[2] = 0;
- + }
- + mb->plane.planes[3] = 0;
- +
- + switch (fourcc_mod_broadcom_mod(fb->modifier)) {
- + case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
- + switch (mb->plane.vc_image_type) {
- + case VC_IMAGE_XRGB8888:
- + mb->plane.vc_image_type = VC_IMAGE_TF_RGBX32;
- + break;
- + case VC_IMAGE_ARGB8888:
- + mb->plane.vc_image_type = VC_IMAGE_TF_RGBA32;
- + break;
- + case VC_IMAGE_RGB565:
- + mb->plane.vc_image_type = VC_IMAGE_TF_RGB565;
- + break;
- + }
- + break;
- + case DRM_FORMAT_MOD_BROADCOM_SAND128:
- + switch (mb->plane.vc_image_type) {
- + case VC_IMAGE_YUV420SP:
- + mb->plane.vc_image_type = VC_IMAGE_YUV_UV;
- + break;
- + /* VC_IMAGE_YUV10COL could be included in here, but it is only
- + * valid as a SAND128 format, so the table at the top will have
- + * already set the correct format.
- + */
- + }
- + /* Note that the column pitch is passed across in lines, not
- + * bytes.
- + */
- + mb->plane.pitch = fourcc_mod_broadcom_param(fb->modifier);
- + break;
- + }
- +
- + DRM_DEBUG_ATOMIC("[PLANE:%d:%s] plane update %dx%d@%d +dst(%d,%d, %d,%d) +src(%d,%d, %d,%d) 0x%08x/%08x/%08x/%d, alpha %u zpos %u\n",
- + plane->base.id, plane->name,
- + mb->plane.width,
- + mb->plane.height,
- + mb->plane.vc_image_type,
- + state->crtc_x,
- + state->crtc_y,
- + state->crtc_w,
- + state->crtc_h,
- + mb->plane.src_x,
- + mb->plane.src_y,
- + mb->plane.src_w,
- + mb->plane.src_h,
- + mb->plane.planes[0],
- + mb->plane.planes[1],
- + mb->plane.planes[2],
- + fb->pitches[0],
- + state->alpha,
- + state->normalized_zpos);
- +
- + return 0;
- +}
- +
- +static int vc4_plane_atomic_check(struct drm_plane *plane,
- + struct drm_atomic_state *state)
- +{
- + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
- + plane);
- + struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
- +
- + if (!plane_enabled(new_plane_state))
- + return 0;
- +
- + return vc4_plane_to_mb(plane, &vc4_plane->mb, new_plane_state);
- +}
- +
- +/* Called during init to allocate the plane's atomic state. */
- +static void vc4_plane_reset(struct drm_plane *plane)
- +{
- + struct vc4_plane_state *vc4_state;
- +
- + WARN_ON(plane->state);
- +
- + vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
- + if (!vc4_state)
- + return;
- +
- + __drm_atomic_helper_plane_reset(plane, &vc4_state->base);
- +}
- +
- +static void vc4_plane_destroy(struct drm_plane *plane)
- +{
- + drm_plane_cleanup(plane);
- +}
- +
- +static bool vc4_fkms_format_mod_supported(struct drm_plane *plane,
- + uint32_t format,
- + uint64_t modifier)
- +{
- + /* Support T_TILING for RGB formats only. */
- + switch (format) {
- + case DRM_FORMAT_XRGB8888:
- + case DRM_FORMAT_ARGB8888:
- + case DRM_FORMAT_RGB565:
- + switch (modifier) {
- + case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
- + case DRM_FORMAT_MOD_LINEAR:
- + return true;
- + default:
- + return false;
- + }
- + case DRM_FORMAT_NV12:
- + switch (fourcc_mod_broadcom_mod(modifier)) {
- + case DRM_FORMAT_MOD_LINEAR:
- + case DRM_FORMAT_MOD_BROADCOM_SAND128:
- + return true;
- + default:
- + return false;
- + }
- + case DRM_FORMAT_P030:
- + switch (fourcc_mod_broadcom_mod(modifier)) {
- + case DRM_FORMAT_MOD_BROADCOM_SAND128:
- + return true;
- + default:
- + return false;
- + }
- + case DRM_FORMAT_NV21:
- + case DRM_FORMAT_RGB888:
- + case DRM_FORMAT_BGR888:
- + case DRM_FORMAT_YUV422:
- + case DRM_FORMAT_YUV420:
- + case DRM_FORMAT_YVU420:
- + default:
- + return (modifier == DRM_FORMAT_MOD_LINEAR);
- + }
- +}
- +
- +static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
- +{
- + struct vc4_plane_state *vc4_state;
- +
- + if (WARN_ON(!plane->state))
- + return NULL;
- +
- + vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
- + if (!vc4_state)
- + return NULL;
- +
- + __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
- +
- + return &vc4_state->base;
- +}
- +
- +static const struct drm_plane_funcs vc4_plane_funcs = {
- + .update_plane = drm_atomic_helper_update_plane,
- + .disable_plane = drm_atomic_helper_disable_plane,
- + .destroy = vc4_plane_destroy,
- + .set_property = NULL,
- + .reset = vc4_plane_reset,
- + .atomic_duplicate_state = vc4_plane_duplicate_state,
- + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
- + .format_mod_supported = vc4_fkms_format_mod_supported,
- +};
- +
- +static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
- + .prepare_fb = drm_gem_plane_helper_prepare_fb,
- + .cleanup_fb = NULL,
- + .atomic_check = vc4_plane_atomic_check,
- + .atomic_update = vc4_plane_atomic_update,
- + .atomic_disable = vc4_plane_atomic_disable,
- +};
- +
- +static struct drm_plane *vc4_fkms_plane_init(struct drm_device *dev,
- + enum drm_plane_type type,
- + u8 display_num,
- + u8 plane_id)
- +{
- + struct drm_plane *plane = NULL;
- + struct vc4_fkms_plane *vc4_plane;
- + u32 formats[ARRAY_SIZE(vc_image_formats)];
- + unsigned int default_zpos = 0;
- + u32 num_formats = 0;
- + int ret = 0;
- + static const uint64_t modifiers[] = {
- + DRM_FORMAT_MOD_LINEAR,
- + /* VC4_T_TILED should come after linear, because we
- + * would prefer to scan out linear (less bus traffic).
- + */
- + DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED,
- + DRM_FORMAT_MOD_BROADCOM_SAND128,
- + DRM_FORMAT_MOD_INVALID,
- + };
- + int i;
- +
- + vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
- + GFP_KERNEL);
- + if (!vc4_plane) {
- + ret = -ENOMEM;
- + goto fail;
- + }
- +
- + for (i = 0; i < ARRAY_SIZE(vc_image_formats); i++)
- + formats[num_formats++] = vc_image_formats[i].drm;
- +
- + plane = &vc4_plane->base;
- + ret = drm_universal_plane_init(dev, plane, 0,
- + &vc4_plane_funcs,
- + formats, num_formats, modifiers,
- + type, NULL);
- +
- + /* FIXME: Do we need to be checking return values from all these calls?
- + */
- + drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
- +
- + drm_plane_create_alpha_property(plane);
- + drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
- + DRM_MODE_ROTATE_0 |
- + DRM_MODE_ROTATE_180 |
- + DRM_MODE_REFLECT_X |
- + DRM_MODE_REFLECT_Y);
- + drm_plane_create_color_properties(plane,
- + BIT(DRM_COLOR_YCBCR_BT601) |
- + BIT(DRM_COLOR_YCBCR_BT709) |
- + BIT(DRM_COLOR_YCBCR_BT2020),
- + BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
- + BIT(DRM_COLOR_YCBCR_FULL_RANGE),
- + DRM_COLOR_YCBCR_BT709,
- + DRM_COLOR_YCBCR_LIMITED_RANGE);
- +
- + /*
- + * Default frame buffer setup is with FB on -127, and raspistill etc
- + * tend to drop overlays on layer 2. Cursor plane was on layer +127.
- + *
- + * For F-KMS the mailbox call allows for a s8.
- + * Remap zpos 0 to -127 for the background layer, but leave all the
- + * other layers as requested by KMS.
- + */
- + switch (type) {
- + default:
- + case DRM_PLANE_TYPE_PRIMARY:
- + default_zpos = 0;
- + break;
- + case DRM_PLANE_TYPE_OVERLAY:
- + default_zpos = 1;
- + break;
- + case DRM_PLANE_TYPE_CURSOR:
- + default_zpos = 2;
- + break;
- + }
- + drm_plane_create_zpos_property(plane, default_zpos, 0, 127);
- +
- + /* Prepare the static elements of the mailbox structure */
- + vc4_plane->mb.tag.tag = RPI_FIRMWARE_SET_PLANE;
- + vc4_plane->mb.tag.buf_size = sizeof(struct set_plane);
- + vc4_plane->mb.tag.req_resp_size = 0;
- + vc4_plane->mb.plane.display = display_num;
- + vc4_plane->mb.plane.plane_id = plane_id;
- + vc4_plane->mb.plane.layer = default_zpos ? default_zpos : -127;
- +
- + return plane;
- +fail:
- + if (plane)
- + vc4_plane_destroy(plane);
- +
- + return ERR_PTR(ret);
- +}
- +
- +static void vc4_crtc_mode_set_nofb(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 drm_display_mode *mode = &crtc->state->adjusted_mode;
- + struct vc4_fkms_encoder *vc4_encoder =
- + to_vc4_fkms_encoder(vc4_crtc->encoder);
- + struct mailbox_set_mode mb = {
- + .tag1 = { RPI_FIRMWARE_SET_TIMING,
- + sizeof(struct set_timings), 0},
- + };
- + union hdmi_infoframe frame;
- + int ret;
- +
- + ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, vc4_crtc->connector, mode);
- + if (ret < 0) {
- + DRM_ERROR("couldn't fill AVI infoframe\n");
- + return;
- + }
- +
- + DRM_DEBUG_KMS("Setting mode for display num %u mode name %s, clk %d, h(disp %d, start %d, end %d, total %d, skew %d) v(disp %d, start %d, end %d, total %d, scan %d), vrefresh %d, par %u, flags 0x%04x\n",
- + vc4_crtc->display_number, mode->name, mode->clock,
- + mode->hdisplay, mode->hsync_start, mode->hsync_end,
- + mode->htotal, mode->hskew, mode->vdisplay,
- + mode->vsync_start, mode->vsync_end, mode->vtotal,
- + mode->vscan, drm_mode_vrefresh(mode),
- + mode->picture_aspect_ratio, mode->flags);
- + mb.timings.display = vc4_crtc->display_number;
- +
- + mb.timings.clock = mode->clock;
- + mb.timings.hdisplay = mode->hdisplay;
- + mb.timings.hsync_start = mode->hsync_start;
- + mb.timings.hsync_end = mode->hsync_end;
- + mb.timings.htotal = mode->htotal;
- + mb.timings.hskew = mode->hskew;
- + mb.timings.vdisplay = mode->vdisplay;
- + mb.timings.vsync_start = mode->vsync_start;
- + mb.timings.vsync_end = mode->vsync_end;
- + mb.timings.vtotal = mode->vtotal;
- + mb.timings.vscan = mode->vscan;
- + mb.timings.vrefresh = drm_mode_vrefresh(mode);
- + mb.timings.flags = 0;
- + if (mode->flags & DRM_MODE_FLAG_PHSYNC)
- + mb.timings.flags |= TIMINGS_FLAGS_H_SYNC_POS;
- + if (mode->flags & DRM_MODE_FLAG_PVSYNC)
- + mb.timings.flags |= TIMINGS_FLAGS_V_SYNC_POS;
- +
- + switch (frame.avi.picture_aspect) {
- + default:
- + case HDMI_PICTURE_ASPECT_NONE:
- + mb.timings.flags |= TIMINGS_FLAGS_ASPECT_NONE;
- + break;
- + case HDMI_PICTURE_ASPECT_4_3:
- + mb.timings.flags |= TIMINGS_FLAGS_ASPECT_4_3;
- + break;
- + case HDMI_PICTURE_ASPECT_16_9:
- + mb.timings.flags |= TIMINGS_FLAGS_ASPECT_16_9;
- + break;
- + case HDMI_PICTURE_ASPECT_64_27:
- + mb.timings.flags |= TIMINGS_FLAGS_ASPECT_64_27;
- + break;
- + case HDMI_PICTURE_ASPECT_256_135:
- + mb.timings.flags |= TIMINGS_FLAGS_ASPECT_256_135;
- + break;
- + }
- +
- + if (mode->flags & DRM_MODE_FLAG_INTERLACE)
- + mb.timings.flags |= TIMINGS_FLAGS_INTERLACE;
- + if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- + mb.timings.flags |= TIMINGS_FLAGS_DBL_CLK;
- +
- + mb.timings.video_id_code = frame.avi.video_code;
- +
- + if (!vc4_encoder->hdmi_monitor) {
- + mb.timings.flags |= TIMINGS_FLAGS_DVI;
- + } else {
- + struct vc4_fkms_connector_state *conn_state =
- + to_vc4_fkms_connector_state(vc4_crtc->connector->state);
- +
- + if (conn_state->broadcast_rgb == VC4_BROADCAST_RGB_AUTO) {
- + /* See CEA-861-E - 5.1 Default Encoding Parameters */
- + if (drm_default_rgb_quant_range(mode) ==
- + HDMI_QUANTIZATION_RANGE_LIMITED)
- + mb.timings.flags |= TIMINGS_FLAGS_RGB_LIMITED;
- + } else {
- + if (conn_state->broadcast_rgb ==
- + VC4_BROADCAST_RGB_LIMITED)
- + mb.timings.flags |= TIMINGS_FLAGS_RGB_LIMITED;
- +
- + /* If not using the default range, then do not provide
- + * a VIC as the HDMI spec requires that we do not
- + * signal the opposite of the defined range in the AVI
- + * infoframe.
- + */
- + if (!!(mb.timings.flags & TIMINGS_FLAGS_RGB_LIMITED) !=
- + (drm_default_rgb_quant_range(mode) ==
- + HDMI_QUANTIZATION_RANGE_LIMITED))
- + mb.timings.video_id_code = 0;
- + }
- + }
- +
- + /*
- + * FIXME: To implement
- + * switch(mode->flag & DRM_MODE_FLAG_3D_MASK) {
- + * case DRM_MODE_FLAG_3D_NONE:
- + * case DRM_MODE_FLAG_3D_FRAME_PACKING:
- + * case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE:
- + * case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE:
- + * case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL:
- + * case DRM_MODE_FLAG_3D_L_DEPTH:
- + * case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH:
- + * case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
- + * case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
- + * }
- + */
- +
- + ret = rpi_firmware_property_list(vc4->firmware, &mb, sizeof(mb));
- +}
- +
- +static void vc4_crtc_disable(struct drm_crtc *crtc,
- + struct drm_atomic_state *state)
- +{
- + struct drm_device *dev = crtc->dev;
- + struct drm_plane *plane;
- +
- + DRM_DEBUG_KMS("[CRTC:%d] vblanks off.\n",
- + crtc->base.id);
- + drm_crtc_vblank_off(crtc);
- +
- + /* Always turn the planes off on CRTC disable. In DRM, planes
- + * are enabled/disabled through the update/disable hooks
- + * above, and the CRTC enable/disable independently controls
- + * whether anything scans out at all, but the firmware doesn't
- + * give us a CRTC-level control for that.
- + */
- +
- + drm_atomic_crtc_for_each_plane(plane, crtc)
- + vc4_plane_atomic_disable(plane, state);
- +
- + /*
- + * Make sure we issue a vblank event after disabling the CRTC if
- + * someone was waiting it.
- + */
- + if (crtc->state->event) {
- + unsigned long flags;
- +
- + spin_lock_irqsave(&dev->event_lock, flags);
- + drm_crtc_send_vblank_event(crtc, crtc->state->event);
- + crtc->state->event = NULL;
- + spin_unlock_irqrestore(&dev->event_lock, flags);
- + }
- +}
- +
- +static void vc4_crtc_consume_event(struct drm_crtc *crtc)
- +{
- + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- + struct drm_device *dev = crtc->dev;
- + unsigned long flags;
- +
- + if (!crtc->state->event)
- + return;
- +
- + crtc->state->event->pipe = drm_crtc_index(crtc);
- +
- + WARN_ON(drm_crtc_vblank_get(crtc) != 0);
- +
- + spin_lock_irqsave(&dev->event_lock, flags);
- + vc4_crtc->event = crtc->state->event;
- + crtc->state->event = NULL;
- + spin_unlock_irqrestore(&dev->event_lock, flags);
- +}
- +
- +static void vc4_crtc_enable(struct drm_crtc *crtc,
- + struct drm_atomic_state *state)
- +{
- + struct drm_plane *plane;
- +
- + DRM_DEBUG_KMS("[CRTC:%d] vblanks on.\n",
- + crtc->base.id);
- + drm_crtc_vblank_on(crtc);
- + vc4_crtc_consume_event(crtc);
- +
- + /* Unblank the planes (if they're supposed to be displayed). */
- + drm_atomic_crtc_for_each_plane(plane, crtc)
- + if (plane->state->fb)
- + vc4_plane_set_blank(plane, plane->state->visible);
- +}
- +
- +static enum drm_mode_status
- +vc4_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
- +{
- + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- + struct drm_device *dev = crtc->dev;
- + struct vc4_dev *vc4 = to_vc4_dev(dev);
- + struct vc4_fkms *fkms = vc4->fkms;
- +
- + /* Do not allow doublescan modes from user space */
- + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) {
- + DRM_DEBUG_KMS("[CRTC:%d] Doublescan mode rejected.\n",
- + crtc->base.id);
- + return MODE_NO_DBLESCAN;
- + }
- +
- + /* Disable refresh rates > defined threshold (default 85Hz) as limited
- + * gain from them
- + */
- + if (drm_mode_vrefresh(mode) > fkms_max_refresh_rate)
- + return MODE_BAD_VVALUE;
- +
- + /* Limit the pixel clock based on the HDMI clock limits from the
- + * firmware
- + */
- + switch (vc4_crtc->display_number) {
- + case 2: /* HDMI0 */
- + if (fkms->cfg.max_pixel_clock[0] &&
- + mode->clock > fkms->cfg.max_pixel_clock[0])
- + return MODE_CLOCK_HIGH;
- + break;
- + case 7: /* HDMI1 */
- + if (fkms->cfg.max_pixel_clock[1] &&
- + mode->clock > fkms->cfg.max_pixel_clock[1])
- + return MODE_CLOCK_HIGH;
- + break;
- + }
- +
- + /* Pi4 can't generate odd horizontal timings on HDMI, so reject modes
- + * that would set them.
- + */
- + if (fkms->bcm2711 &&
- + (vc4_crtc->display_number == 2 || vc4_crtc->display_number == 7) &&
- + !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
- + ((mode->hdisplay | /* active */
- + (mode->hsync_start - mode->hdisplay) | /* front porch */
- + (mode->hsync_end - mode->hsync_start) | /* sync pulse */
- + (mode->htotal - mode->hsync_end)) & 1)) /* back porch */ {
- + DRM_DEBUG_KMS("[CRTC:%d] Odd timing rejected %u %u %u %u.\n",
- + crtc->base.id, mode->hdisplay, mode->hsync_start,
- + mode->hsync_end, mode->htotal);
- + return MODE_H_ILLEGAL;
- + }
- +
- + return MODE_OK;
- +}
- +
- +static int vc4_crtc_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);
- + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
- + struct drm_connector *conn;
- + struct drm_connector_state *conn_state;
- + int i;
- +
- + DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_check.\n", crtc->base.id);
- +
- + for_each_new_connector_in_state(crtc_state->state, conn, conn_state, i) {
- + if (conn_state->crtc != crtc)
- + continue;
- +
- + vc4_state->margins.left = conn_state->tv.margins.left;
- + vc4_state->margins.right = conn_state->tv.margins.right;
- + vc4_state->margins.top = conn_state->tv.margins.top;
- + vc4_state->margins.bottom = conn_state->tv.margins.bottom;
- + break;
- + }
- + return 0;
- +}
- +
- +static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
- + struct drm_atomic_state *state)
- +{
- + struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
- + crtc);
- +
- + DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_flush.\n",
- + crtc->base.id);
- + if (crtc->state->active && old_state->active && crtc->state->event)
- + vc4_crtc_consume_event(crtc);
- +}
- +
- +static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
- +{
- + struct drm_crtc *crtc = &vc4_crtc->base;
- + struct drm_device *dev = crtc->dev;
- + unsigned long flags;
- +
- + spin_lock_irqsave(&dev->event_lock, flags);
- + if (vc4_crtc->event) {
- + drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
- + vc4_crtc->event = NULL;
- + drm_crtc_vblank_put(crtc);
- + }
- + spin_unlock_irqrestore(&dev->event_lock, flags);
- +}
- +
- +static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
- +{
- + struct vc4_crtc **crtc_list = data;
- + int i;
- + u32 stat = readl(crtc_list[0]->regs + SMICS);
- + irqreturn_t ret = IRQ_NONE;
- + u32 chan;
- +
- + if (stat & SMICS_INTERRUPTS) {
- + writel(0, crtc_list[0]->regs + SMICS);
- +
- + chan = readl(crtc_list[0]->regs + SMIDSW0);
- +
- + if ((chan & 0xFFFF0000) != SMI_NEW) {
- + /* Older firmware. Treat the one interrupt as vblank/
- + * complete for all crtcs.
- + */
- + for (i = 0; crtc_list[i]; i++) {
- + if (crtc_list[i]->vblank_enabled)
- + drm_crtc_handle_vblank(&crtc_list[i]->base);
- + vc4_crtc_handle_page_flip(crtc_list[i]);
- + }
- + } else {
- + if (chan & 1) {
- + writel(SMI_NEW, crtc_list[0]->regs + SMIDSW0);
- + if (crtc_list[0]->vblank_enabled)
- + drm_crtc_handle_vblank(&crtc_list[0]->base);
- + vc4_crtc_handle_page_flip(crtc_list[0]);
- + }
- +
- + if (crtc_list[1]) {
- + /* Check for the secondary display too */
- + chan = readl(crtc_list[0]->regs + SMIDSW1);
- +
- + if (chan & 1) {
- + writel(SMI_NEW, crtc_list[0]->regs + SMIDSW1);
- +
- + if (crtc_list[1]->vblank_enabled)
- + drm_crtc_handle_vblank(&crtc_list[1]->base);
- + vc4_crtc_handle_page_flip(crtc_list[1]);
- + }
- + }
- + }
- +
- + ret = IRQ_HANDLED;
- + }
- +
- + return ret;
- +}
- +
- +static int vc4_fkms_page_flip(struct drm_crtc *crtc,
- + struct drm_framebuffer *fb,
- + struct drm_pending_vblank_event *event,
- + uint32_t flags,
- + struct drm_modeset_acquire_ctx *ctx)
- +{
- + if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
- + DRM_ERROR("Async flips aren't allowed\n");
- + return -EINVAL;
- + }
- +
- + return drm_atomic_helper_page_flip(crtc, fb, event, flags, ctx);
- +}
- +
- +static struct drm_crtc_state *
- +vc4_fkms_crtc_duplicate_state(struct drm_crtc *crtc)
- +{
- + struct vc4_crtc_state *vc4_state, *old_vc4_state;
- +
- + vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
- + if (!vc4_state)
- + return NULL;
- +
- + old_vc4_state = to_vc4_crtc_state(crtc->state);
- + vc4_state->margins = old_vc4_state->margins;
- +
- + __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
- + return &vc4_state->base;
- +}
- +
- +static void
- +vc4_fkms_crtc_reset(struct drm_crtc *crtc)
- +{
- + if (crtc->state)
- + __drm_atomic_helper_crtc_destroy_state(crtc->state);
- +
- + crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
- + if (crtc->state)
- + crtc->state->crtc = crtc;
- +}
- +
- +static int vc4_fkms_enable_vblank(struct drm_crtc *crtc)
- +{
- + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- +
- + DRM_DEBUG_KMS("[CRTC:%d] enable_vblank.\n",
- + crtc->base.id);
- + vc4_crtc->vblank_enabled = true;
- +
- + return 0;
- +}
- +
- +static void vc4_fkms_disable_vblank(struct drm_crtc *crtc)
- +{
- + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- +
- + DRM_DEBUG_KMS("[CRTC:%d] disable_vblank.\n",
- + crtc->base.id);
- + vc4_crtc->vblank_enabled = false;
- +}
- +
- +static const struct drm_crtc_funcs vc4_crtc_funcs = {
- + .set_config = drm_atomic_helper_set_config,
- + .destroy = drm_crtc_cleanup,
- + .page_flip = vc4_fkms_page_flip,
- + .set_property = NULL,
- + .cursor_set = NULL, /* handled by drm_mode_cursor_universal */
- + .cursor_move = NULL, /* handled by drm_mode_cursor_universal */
- + .reset = vc4_fkms_crtc_reset,
- + .atomic_duplicate_state = vc4_fkms_crtc_duplicate_state,
- + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
- + .enable_vblank = vc4_fkms_enable_vblank,
- + .disable_vblank = vc4_fkms_disable_vblank,
- +};
- +
- +static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
- + .mode_set_nofb = vc4_crtc_mode_set_nofb,
- + .mode_valid = vc4_crtc_mode_valid,
- + .atomic_check = vc4_crtc_atomic_check,
- + .atomic_flush = vc4_crtc_atomic_flush,
- + .atomic_enable = vc4_crtc_enable,
- + .atomic_disable = vc4_crtc_disable,
- +};
- +
- +static const struct of_device_id vc4_firmware_kms_dt_match[] = {
- + { .compatible = "raspberrypi,rpi-firmware-kms" },
- + { .compatible = "raspberrypi,rpi-firmware-kms-2711",
- + .data = (void *)1 },
- + {}
- +};
- +
- +static enum drm_connector_status
- +vc4_fkms_connector_detect(struct drm_connector *connector, bool force)
- +{
- + DRM_DEBUG_KMS("connector detect.\n");
- + return connector_status_connected;
- +}
- +
- +/* Queries the firmware to populate a drm_mode structure for this display */
- +static int vc4_fkms_get_fw_mode(struct vc4_fkms_connector *fkms_connector,
- + struct drm_display_mode *mode)
- +{
- + struct vc4_dev *vc4 = fkms_connector->vc4_dev;
- + struct set_timings timings = { 0 };
- + int ret;
- +
- + timings.display = fkms_connector->display_number;
- +
- + ret = rpi_firmware_property(vc4->firmware,
- + RPI_FIRMWARE_GET_DISPLAY_TIMING, &timings,
- + sizeof(timings));
- + if (ret || !timings.clock)
- + /* No mode returned - abort */
- + return -1;
- +
- + /* Equivalent to DRM_MODE macro. */
- + memset(mode, 0, sizeof(*mode));
- + strncpy(mode->name, "FIXED_MODE", sizeof(mode->name));
- + mode->status = 0;
- + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
- + mode->clock = timings.clock;
- + mode->hdisplay = timings.hdisplay;
- + mode->hsync_start = timings.hsync_start;
- + mode->hsync_end = timings.hsync_end;
- + mode->htotal = timings.htotal;
- + mode->hskew = 0;
- + mode->vdisplay = timings.vdisplay;
- + mode->vsync_start = timings.vsync_start;
- + mode->vsync_end = timings.vsync_end;
- + mode->vtotal = timings.vtotal;
- + mode->vscan = timings.vscan;
- +
- + if (timings.flags & TIMINGS_FLAGS_H_SYNC_POS)
- + mode->flags |= DRM_MODE_FLAG_PHSYNC;
- + else
- + mode->flags |= DRM_MODE_FLAG_NHSYNC;
- +
- + if (timings.flags & TIMINGS_FLAGS_V_SYNC_POS)
- + mode->flags |= DRM_MODE_FLAG_PVSYNC;
- + else
- + mode->flags |= DRM_MODE_FLAG_NVSYNC;
- +
- + if (timings.flags & TIMINGS_FLAGS_INTERLACE)
- + mode->flags |= DRM_MODE_FLAG_INTERLACE;
- +
- + return 0;
- +}
- +
- +static int vc4_fkms_get_edid_block(void *data, u8 *buf, unsigned int block,
- + size_t len)
- +{
- + struct vc4_fkms_connector *fkms_connector =
- + (struct vc4_fkms_connector *)data;
- + struct vc4_dev *vc4 = fkms_connector->vc4_dev;
- + struct mailbox_get_edid mb = {
- + .tag1 = { RPI_FIRMWARE_GET_EDID_BLOCK_DISPLAY,
- + 128 + 8, 0 },
- + .block = block,
- + .display_number = fkms_connector->display_number,
- + };
- + int ret = 0;
- +
- + ret = rpi_firmware_property_list(vc4->firmware, &mb, sizeof(mb));
- +
- + if (!ret)
- + memcpy(buf, mb.edid, len);
- +
- + return ret;
- +}
- +
- +static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
- +{
- + struct vc4_fkms_connector *fkms_connector =
- + to_vc4_fkms_connector(connector);
- + struct drm_encoder *encoder = fkms_connector->encoder;
- + struct vc4_fkms_encoder *vc4_encoder = to_vc4_fkms_encoder(encoder);
- + struct drm_display_mode fw_mode;
- + struct drm_display_mode *mode;
- + struct edid *edid;
- + int num_modes;
- +
- + if (!vc4_fkms_get_fw_mode(fkms_connector, &fw_mode)) {
- + drm_mode_debug_printmodeline(&fw_mode);
- + mode = drm_mode_duplicate(connector->dev,
- + &fw_mode);
- + drm_mode_probed_add(connector, mode);
- + num_modes = 1; /* 1 mode */
- + } else {
- + edid = drm_do_get_edid(connector, vc4_fkms_get_edid_block,
- + fkms_connector);
- +
- + /* FIXME: Can we do CEC?
- + * cec_s_phys_addr_from_edid(vc4->hdmi->cec_adap, edid);
- + * if (!edid)
- + * return -ENODEV;
- + */
- +
- + vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
- +
- + drm_connector_update_edid_property(connector, edid);
- + num_modes = drm_add_edid_modes(connector, edid);
- + kfree(edid);
- + }
- +
- + return num_modes;
- +}
- +
- +/* This is the DSI panel resolution. Use this as a default should the firmware
- + * not respond to our request for the timings.
- + */
- +static const struct drm_display_mode lcd_mode = {
- + DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
- + 25979400 / 1000,
- + 800, 800 + 1, 800 + 1 + 2, 800 + 1 + 2 + 46, 0,
- + 480, 480 + 7, 480 + 7 + 2, 480 + 7 + 2 + 21, 0,
- + 0)
- +};
- +
- +static int vc4_fkms_lcd_connector_get_modes(struct drm_connector *connector)
- +{
- + struct vc4_fkms_connector *fkms_connector =
- + to_vc4_fkms_connector(connector);
- + struct drm_display_mode *mode;
- + struct drm_display_mode fw_mode;
- +
- + if (!vc4_fkms_get_fw_mode(fkms_connector, &fw_mode) && fw_mode.clock)
- + mode = drm_mode_duplicate(connector->dev,
- + &fw_mode);
- + else
- + mode = drm_mode_duplicate(connector->dev,
- + &lcd_mode);
- +
- + if (!mode) {
- + DRM_ERROR("Failed to create a new display mode\n");
- + return -ENOMEM;
- + }
- +
- + drm_mode_probed_add(connector, mode);
- +
- + /* We have one mode */
- + return 1;
- +}
- +
- +static struct drm_encoder *
- +vc4_fkms_connector_best_encoder(struct drm_connector *connector)
- +{
- + struct vc4_fkms_connector *fkms_connector =
- + to_vc4_fkms_connector(connector);
- + DRM_DEBUG_KMS("best_connector.\n");
- + return fkms_connector->encoder;
- +}
- +
- +static void vc4_fkms_connector_destroy(struct drm_connector *connector)
- +{
- + DRM_DEBUG_KMS("[CONNECTOR:%d] destroy.\n",
- + connector->base.id);
- + drm_connector_unregister(connector);
- + drm_connector_cleanup(connector);
- +}
- +
- +/**
- + * vc4_connector_duplicate_state - duplicate connector state
- + * @connector: digital connector
- + *
- + * Allocates and returns a copy of the connector state (both common and
- + * digital connector specific) for the specified connector.
- + *
- + * Returns: The newly allocated connector state, or NULL on failure.
- + */
- +struct drm_connector_state *
- +vc4_connector_duplicate_state(struct drm_connector *connector)
- +{
- + struct vc4_fkms_connector_state *state;
- +
- + state = kmemdup(connector->state, sizeof(*state), GFP_KERNEL);
- + if (!state)
- + return NULL;
- +
- + __drm_atomic_helper_connector_duplicate_state(connector, &state->base);
- + return &state->base;
- +}
- +
- +/**
- + * vc4_connector_atomic_get_property - hook for connector->atomic_get_property.
- + * @connector: Connector to get the property for.
- + * @state: Connector state to retrieve the property from.
- + * @property: Property to retrieve.
- + * @val: Return value for the property.
- + *
- + * Returns the atomic property value for a digital connector.
- + */
- +int vc4_connector_atomic_get_property(struct drm_connector *connector,
- + const struct drm_connector_state *state,
- + struct drm_property *property,
- + uint64_t *val)
- +{
- + struct vc4_fkms_connector *fkms_connector =
- + to_vc4_fkms_connector(connector);
- + struct vc4_fkms_connector_state *vc4_conn_state =
- + to_vc4_fkms_connector_state(state);
- +
- + if (property == fkms_connector->broadcast_rgb_property) {
- + *val = vc4_conn_state->broadcast_rgb;
- + } else {
- + DRM_DEBUG_ATOMIC("Unknown property [PROP:%d:%s]\n",
- + property->base.id, property->name);
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * vc4_connector_atomic_set_property - hook for connector->atomic_set_property.
- + * @connector: Connector to set the property for.
- + * @state: Connector state to set the property on.
- + * @property: Property to set.
- + * @val: New value for the property.
- + *
- + * Sets the atomic property value for a digital connector.
- + */
- +int vc4_connector_atomic_set_property(struct drm_connector *connector,
- + struct drm_connector_state *state,
- + struct drm_property *property,
- + uint64_t val)
- +{
- + struct vc4_fkms_connector *fkms_connector =
- + to_vc4_fkms_connector(connector);
- + struct vc4_fkms_connector_state *vc4_conn_state =
- + to_vc4_fkms_connector_state(state);
- +
- + if (property == fkms_connector->broadcast_rgb_property) {
- + vc4_conn_state->broadcast_rgb = val;
- + return 0;
- + }
- +
- + DRM_DEBUG_ATOMIC("Unknown property [PROP:%d:%s]\n",
- + property->base.id, property->name);
- + return -EINVAL;
- +}
- +
- +int vc4_connector_atomic_check(struct drm_connector *connector,
- + struct drm_atomic_state *state)
- +{
- + struct drm_connector_state *old_state =
- + drm_atomic_get_old_connector_state(state, connector);
- + struct vc4_fkms_connector_state *vc4_old_state =
- + to_vc4_fkms_connector_state(old_state);
- + struct drm_connector_state *new_state =
- + drm_atomic_get_new_connector_state(state, connector);
- + struct vc4_fkms_connector_state *vc4_new_state =
- + to_vc4_fkms_connector_state(new_state);
- + struct drm_crtc *crtc = new_state->crtc;
- +
- + if (!crtc)
- + return 0;
- +
- + if (vc4_old_state->broadcast_rgb != vc4_new_state->broadcast_rgb) {
- + struct drm_crtc_state *crtc_state;
- +
- + crtc_state = drm_atomic_get_crtc_state(state, crtc);
- + if (IS_ERR(crtc_state))
- + return PTR_ERR(crtc_state);
- +
- + crtc_state->mode_changed = true;
- + }
- + return 0;
- +}
- +
- +static void vc4_hdmi_connector_reset(struct drm_connector *connector)
- +{
- + drm_atomic_helper_connector_reset(connector);
- + drm_atomic_helper_connector_tv_reset(connector);
- +}
- +
- +static const struct drm_connector_funcs vc4_fkms_connector_funcs = {
- + .detect = vc4_fkms_connector_detect,
- + .fill_modes = drm_helper_probe_single_connector_modes,
- + .destroy = vc4_fkms_connector_destroy,
- + .reset = vc4_hdmi_connector_reset,
- + .atomic_duplicate_state = vc4_connector_duplicate_state,
- + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- + .atomic_get_property = vc4_connector_atomic_get_property,
- + .atomic_set_property = vc4_connector_atomic_set_property,
- +};
- +
- +static const struct drm_connector_helper_funcs vc4_fkms_connector_helper_funcs = {
- + .get_modes = vc4_fkms_connector_get_modes,
- + .best_encoder = vc4_fkms_connector_best_encoder,
- + .atomic_check = vc4_connector_atomic_check,
- +};
- +
- +static const struct drm_connector_helper_funcs vc4_fkms_lcd_conn_helper_funcs = {
- + .get_modes = vc4_fkms_lcd_connector_get_modes,
- + .best_encoder = vc4_fkms_connector_best_encoder,
- +};
- +
- +static const struct drm_prop_enum_list broadcast_rgb_names[] = {
- + { VC4_BROADCAST_RGB_AUTO, "Automatic" },
- + { VC4_BROADCAST_RGB_FULL, "Full" },
- + { VC4_BROADCAST_RGB_LIMITED, "Limited 16:235" },
- +};
- +
- +static void
- +vc4_attach_broadcast_rgb_property(struct vc4_fkms_connector *fkms_connector)
- +{
- + struct drm_device *dev = fkms_connector->base.dev;
- + struct drm_property *prop;
- +
- + prop = fkms_connector->broadcast_rgb_property;
- + if (!prop) {
- + prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
- + "Broadcast RGB",
- + broadcast_rgb_names,
- + ARRAY_SIZE(broadcast_rgb_names));
- + if (!prop)
- + return;
- +
- + fkms_connector->broadcast_rgb_property = prop;
- + }
- +
- + drm_object_attach_property(&fkms_connector->base.base, prop, 0);
- +}
- +
- +static struct drm_connector *
- +vc4_fkms_connector_init(struct drm_device *dev, struct drm_encoder *encoder,
- + u32 display_num)
- +{
- + struct drm_connector *connector = NULL;
- + struct vc4_fkms_connector *fkms_connector;
- + struct vc4_fkms_connector_state *conn_state = NULL;
- + struct vc4_dev *vc4_dev = to_vc4_dev(dev);
- + int ret = 0;
- +
- + DRM_DEBUG_KMS("connector_init, display_num %u\n", display_num);
- +
- + fkms_connector = devm_kzalloc(dev->dev, sizeof(*fkms_connector),
- + GFP_KERNEL);
- + if (!fkms_connector)
- + return ERR_PTR(-ENOMEM);
- +
- + /*
- + * Allocate enough memory to hold vc4_fkms_connector_state,
- + */
- + conn_state = kzalloc(sizeof(*conn_state), GFP_KERNEL);
- + if (!conn_state) {
- + kfree(fkms_connector);
- + return ERR_PTR(-ENOMEM);
- + }
- +
- + connector = &fkms_connector->base;
- +
- + fkms_connector->encoder = encoder;
- + fkms_connector->display_number = display_num;
- + fkms_connector->display_type = vc4_get_display_type(display_num);
- + fkms_connector->vc4_dev = vc4_dev;
- +
- + __drm_atomic_helper_connector_reset(connector,
- + &conn_state->base);
- +
- + if (fkms_connector->display_type == DRM_MODE_ENCODER_DSI) {
- + drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
- + DRM_MODE_CONNECTOR_DSI);
- + drm_connector_helper_add(connector,
- + &vc4_fkms_lcd_conn_helper_funcs);
- + connector->interlace_allowed = 0;
- + } else if (fkms_connector->display_type == DRM_MODE_ENCODER_TVDAC) {
- + drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
- + DRM_MODE_CONNECTOR_Composite);
- + drm_connector_helper_add(connector,
- + &vc4_fkms_lcd_conn_helper_funcs);
- + connector->interlace_allowed = 1;
- + } else {
- + drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
- + DRM_MODE_CONNECTOR_HDMIA);
- + drm_connector_helper_add(connector,
- + &vc4_fkms_connector_helper_funcs);
- + connector->interlace_allowed = 1;
- + }
- +
- + ret = drm_mode_create_tv_margin_properties(dev);
- + if (ret)
- + goto fail;
- +
- + drm_connector_attach_tv_margin_properties(connector);
- +
- + connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
- + DRM_CONNECTOR_POLL_DISCONNECT);
- +
- + connector->doublescan_allowed = 0;
- +
- + vc4_attach_broadcast_rgb_property(fkms_connector);
- +
- + drm_connector_attach_encoder(connector, encoder);
- +
- + return connector;
- +
- + fail:
- + if (connector)
- + vc4_fkms_connector_destroy(connector);
- +
- + return ERR_PTR(ret);
- +}
- +
- +static void vc4_fkms_encoder_destroy(struct drm_encoder *encoder)
- +{
- + DRM_DEBUG_KMS("Encoder_destroy\n");
- + drm_encoder_cleanup(encoder);
- +}
- +
- +static const struct drm_encoder_funcs vc4_fkms_encoder_funcs = {
- + .destroy = vc4_fkms_encoder_destroy,
- +};
- +
- +static void vc4_fkms_display_power(struct drm_encoder *encoder, bool power)
- +{
- + struct vc4_fkms_encoder *vc4_encoder = to_vc4_fkms_encoder(encoder);
- + struct vc4_dev *vc4 = to_vc4_dev(encoder->dev);
- +
- + struct mailbox_display_pwr pwr = {
- + .tag1 = {RPI_FIRMWARE_SET_DISPLAY_POWER, 8, 0, },
- + .display = vc4_encoder->display_num,
- + .state = power ? 1 : 0,
- + };
- +
- + rpi_firmware_property_list(vc4->firmware, &pwr, sizeof(pwr));
- +}
- +
- +static void vc4_fkms_encoder_enable(struct drm_encoder *encoder)
- +{
- + vc4_fkms_display_power(encoder, true);
- + DRM_DEBUG_KMS("Encoder_enable\n");
- +}
- +
- +static void vc4_fkms_encoder_disable(struct drm_encoder *encoder)
- +{
- + vc4_fkms_display_power(encoder, false);
- + DRM_DEBUG_KMS("Encoder_disable\n");
- +}
- +
- +static const struct drm_encoder_helper_funcs vc4_fkms_encoder_helper_funcs = {
- + .enable = vc4_fkms_encoder_enable,
- + .disable = vc4_fkms_encoder_disable,
- +};
- +
- +static int vc4_fkms_create_screen(struct device *dev, struct drm_device *drm,
- + int display_idx, int display_ref,
- + struct vc4_crtc **ret_crtc)
- +{
- + struct vc4_dev *vc4 = to_vc4_dev(drm);
- + struct vc4_crtc *vc4_crtc;
- + struct vc4_fkms_encoder *vc4_encoder;
- + struct drm_crtc *crtc;
- + struct drm_plane *destroy_plane, *temp;
- + struct mailbox_blank_display blank = {
- + .tag1 = {RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM, 4, 0, },
- + .display = display_idx,
- + .tag2 = { RPI_FIRMWARE_FRAMEBUFFER_BLANK, 4, 0, },
- + .blank = 1,
- + };
- + struct drm_plane *planes[PLANES_PER_CRTC];
- + int ret, i;
- +
- + vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
- + if (!vc4_crtc)
- + return -ENOMEM;
- + crtc = &vc4_crtc->base;
- +
- + vc4_crtc->display_number = display_ref;
- + vc4_crtc->display_type = vc4_get_display_type(display_ref);
- +
- + /* Blank the firmware provided framebuffer */
- + rpi_firmware_property_list(vc4->firmware, &blank, sizeof(blank));
- +
- + for (i = 0; i < PLANES_PER_CRTC; i++) {
- + planes[i] = vc4_fkms_plane_init(drm,
- + (i == 0) ?
- + DRM_PLANE_TYPE_PRIMARY :
- + (i == PLANES_PER_CRTC - 1) ?
- + DRM_PLANE_TYPE_CURSOR :
- + DRM_PLANE_TYPE_OVERLAY,
- + display_ref,
- + i + (display_idx * PLANES_PER_CRTC)
- + );
- + if (IS_ERR(planes[i])) {
- + dev_err(dev, "failed to construct plane %u\n", i);
- + ret = PTR_ERR(planes[i]);
- + goto err;
- + }
- + }
- +
- + drm_crtc_init_with_planes(drm, crtc, planes[0],
- + planes[PLANES_PER_CRTC - 1], &vc4_crtc_funcs,
- + NULL);
- + drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
- +
- + /* Update the possible_crtcs mask for the overlay plane(s) */
- + for (i = 1; i < (PLANES_PER_CRTC - 1); i++)
- + planes[i]->possible_crtcs = drm_crtc_mask(crtc);
- +
- + vc4_encoder = devm_kzalloc(dev, sizeof(*vc4_encoder), GFP_KERNEL);
- + if (!vc4_encoder)
- + return -ENOMEM;
- + vc4_crtc->encoder = &vc4_encoder->base;
- +
- + vc4_encoder->display_num = display_ref;
- + vc4_encoder->base.possible_crtcs |= drm_crtc_mask(crtc);
- +
- + drm_encoder_init(drm, &vc4_encoder->base, &vc4_fkms_encoder_funcs,
- + vc4_crtc->display_type, NULL);
- + drm_encoder_helper_add(&vc4_encoder->base,
- + &vc4_fkms_encoder_helper_funcs);
- +
- + vc4_crtc->connector = vc4_fkms_connector_init(drm, &vc4_encoder->base,
- + display_ref);
- + if (IS_ERR(vc4_crtc->connector)) {
- + ret = PTR_ERR(vc4_crtc->connector);
- + goto err_destroy_encoder;
- + }
- +
- + *ret_crtc = vc4_crtc;
- +
- + return 0;
- +
- +err_destroy_encoder:
- + vc4_fkms_encoder_destroy(vc4_crtc->encoder);
- + list_for_each_entry_safe(destroy_plane, temp,
- + &drm->mode_config.plane_list, head) {
- + if (destroy_plane->possible_crtcs == 1 << drm_crtc_index(crtc))
- + destroy_plane->funcs->destroy(destroy_plane);
- + }
- +err:
- + return ret;
- +}
- +
- +static int vc4_fkms_bind(struct device *dev, struct device *master, void *data)
- +{
- + struct platform_device *pdev = to_platform_device(dev);
- + struct drm_device *drm = dev_get_drvdata(master);
- + struct vc4_dev *vc4 = to_vc4_dev(drm);
- + struct device_node *firmware_node;
- + const struct of_device_id *match;
- + struct vc4_crtc **crtc_list;
- + u32 num_displays, display_num;
- + struct vc4_fkms *fkms;
- + int ret;
- + u32 display_id;
- +
- + vc4->firmware_kms = true;
- +
- + fkms = devm_kzalloc(dev, sizeof(*fkms), GFP_KERNEL);
- + if (!fkms)
- + return -ENOMEM;
- +
- + match = of_match_device(vc4_firmware_kms_dt_match, dev);
- + if (!match)
- + return -ENODEV;
- + if (match->data)
- + fkms->bcm2711 = true;
- +
- + firmware_node = of_parse_phandle(dev->of_node, "brcm,firmware", 0);
- + vc4->firmware = devm_rpi_firmware_get(&pdev->dev, firmware_node);
- + if (!vc4->firmware) {
- + DRM_DEBUG("Failed to get Raspberry Pi firmware reference.\n");
- + return -EPROBE_DEFER;
- + }
- + of_node_put(firmware_node);
- +
- + ret = rpi_firmware_property(vc4->firmware,
- + RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS,
- + &num_displays, sizeof(u32));
- +
- + /* If we fail to get the number of displays, then
- + * assume old firmware that doesn't have the mailbox call, so just
- + * set one display
- + */
- + if (ret) {
- + num_displays = 1;
- + DRM_WARN("Unable to determine number of displays - assuming 1\n");
- + ret = 0;
- + }
- +
- + ret = rpi_firmware_property(vc4->firmware,
- + RPI_FIRMWARE_GET_DISPLAY_CFG,
- + &fkms->cfg, sizeof(fkms->cfg));
- +
- + if (ret)
- + return -EINVAL;
- + /* The firmware works in Hz. This will be compared against kHz, so div
- + * 1000 now rather than multiple times later.
- + */
- + fkms->cfg.max_pixel_clock[0] /= 1000;
- + fkms->cfg.max_pixel_clock[1] /= 1000;
- +
- + /* Allocate a list, with space for a NULL on the end */
- + crtc_list = devm_kzalloc(dev, sizeof(crtc_list) * (num_displays + 1),
- + GFP_KERNEL);
- + if (!crtc_list)
- + return -ENOMEM;
- +
- + for (display_num = 0; display_num < num_displays; display_num++) {
- + display_id = display_num;
- + ret = rpi_firmware_property(vc4->firmware,
- + RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_ID,
- + &display_id, sizeof(display_id));
- + /* FIXME: Determine the correct error handling here.
- + * Should we fail to create the one "screen" but keep the
- + * others, or fail the whole thing?
- + */
- + if (ret)
- + DRM_ERROR("Failed to get display id %u\n", display_num);
- +
- + ret = vc4_fkms_create_screen(dev, drm, display_num, display_id,
- + &crtc_list[display_num]);
- + if (ret)
- + DRM_ERROR("Oh dear, failed to create display %u\n",
- + display_num);
- + }
- +
- + if (num_displays > 0) {
- + /* Map the SMI interrupt reg */
- + crtc_list[0]->regs = vc4_ioremap_regs(pdev, 0);
- + if (IS_ERR(crtc_list[0]->regs))
- + DRM_ERROR("Oh dear, failed to map registers\n");
- +
- + writel(0, crtc_list[0]->regs + SMICS);
- + ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
- + vc4_crtc_irq_handler, 0,
- + "vc4 firmware kms", crtc_list);
- + if (ret)
- + DRM_ERROR("Oh dear, failed to register IRQ\n");
- + } else {
- + DRM_WARN("No displays found. Consider forcing hotplug if HDMI is attached\n");
- + }
- +
- + vc4->fkms = fkms;
- +
- + platform_set_drvdata(pdev, crtc_list);
- +
- + return 0;
- +}
- +
- +static void vc4_fkms_unbind(struct device *dev, struct device *master,
- + void *data)
- +{
- + struct platform_device *pdev = to_platform_device(dev);
- + struct vc4_crtc **crtc_list = dev_get_drvdata(dev);
- + int i;
- +
- + for (i = 0; crtc_list[i]; i++) {
- + vc4_fkms_connector_destroy(crtc_list[i]->connector);
- + vc4_fkms_encoder_destroy(crtc_list[i]->encoder);
- + drm_crtc_cleanup(&crtc_list[i]->base);
- + }
- +
- + platform_set_drvdata(pdev, NULL);
- +}
- +
- +static const struct component_ops vc4_fkms_ops = {
- + .bind = vc4_fkms_bind,
- + .unbind = vc4_fkms_unbind,
- +};
- +
- +static int vc4_fkms_probe(struct platform_device *pdev)
- +{
- + return component_add(&pdev->dev, &vc4_fkms_ops);
- +}
- +
- +static int vc4_fkms_remove(struct platform_device *pdev)
- +{
- + component_del(&pdev->dev, &vc4_fkms_ops);
- + return 0;
- +}
- +
- +struct platform_driver vc4_firmware_kms_driver = {
- + .probe = vc4_fkms_probe,
- + .remove = vc4_fkms_remove,
- + .driver = {
- + .name = "vc4_firmware_kms",
- + .of_match_table = vc4_firmware_kms_dt_match,
- + },
- +};
- --- a/drivers/gpu/drm/vc4/vc4_kms.c
- +++ b/drivers/gpu/drm/vc4/vc4_kms.c
- @@ -160,6 +160,9 @@ vc4_ctm_commit(struct vc4_dev *vc4, stru
- struct vc4_ctm_state *ctm_state = to_vc4_ctm_state(vc4->ctm_manager.state);
- struct drm_color_ctm *ctm = ctm_state->ctm;
-
- + if (vc4->firmware_kms)
- + return;
- +
- if (ctm_state->fifo) {
- HVS_WRITE(SCALER_OLEDCOEF2,
- VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[0]),
- @@ -359,7 +362,7 @@ static void vc4_atomic_commit_tail(struc
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- struct vc4_crtc_state *vc4_crtc_state;
-
- - if (!new_crtc_state->commit)
- + if (!new_crtc_state->commit || vc4->firmware_kms)
- continue;
-
- vc4_crtc_state = to_vc4_crtc_state(new_crtc_state);
- @@ -385,7 +388,7 @@ static void vc4_atomic_commit_tail(struc
- old_hvs_state->fifo_state[channel].pending_commit = NULL;
- }
-
- - if (vc4->hvs->hvs5) {
- + if (vc4->hvs && vc4->hvs->hvs5) {
- unsigned long core_rate = max_t(unsigned long,
- 500000000,
- new_hvs_state->core_clock_rate);
- @@ -402,10 +405,12 @@ static void vc4_atomic_commit_tail(struc
-
- vc4_ctm_commit(vc4, state);
-
- - if (vc4->hvs->hvs5)
- - vc5_hvs_pv_muxing_commit(vc4, state);
- - else
- - vc4_hvs_pv_muxing_commit(vc4, state);
- + if (!vc4->firmware_kms) {
- + if (vc4->hvs && vc4->hvs->hvs5)
- + vc5_hvs_pv_muxing_commit(vc4, state);
- + else
- + vc4_hvs_pv_muxing_commit(vc4, state);
- + }
-
- drm_atomic_helper_commit_planes(dev, state, 0);
-
- @@ -419,7 +424,7 @@ static void vc4_atomic_commit_tail(struc
-
- drm_atomic_helper_cleanup_planes(dev, state);
-
- - if (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);
-
- @@ -437,11 +442,21 @@ static void vc4_atomic_commit_tail(struc
-
- static int vc4_atomic_commit_setup(struct drm_atomic_state *state)
- {
- + struct drm_device *dev = state->dev;
- + struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct drm_crtc_state *crtc_state;
- struct vc4_hvs_state *hvs_state;
- struct drm_crtc *crtc;
- unsigned int i;
-
- + /* We know for sure we don't want an async update here. Set
- + * state->legacy_cursor_update to false to prevent
- + * drm_atomic_helper_setup_commit() from auto-completing
- + * commit->flip_done.
- + */
- + if (!vc4->firmware_kms)
- + state->legacy_cursor_update = false;
- +
- hvs_state = vc4_hvs_get_new_global_state(state);
- if (WARN_ON(IS_ERR(hvs_state)))
- return PTR_ERR(hvs_state);
- @@ -775,6 +790,7 @@ static int vc4_hvs_channels_obj_init(str
- static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
- struct drm_atomic_state *state)
- {
- + struct vc4_dev *vc4 = to_vc4_dev(state->dev);
- struct vc4_hvs_state *hvs_new_state;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct drm_crtc *crtc;
- @@ -798,6 +814,9 @@ static int vc4_pv_muxing_atomic_check(st
- unsigned int matching_channels;
- unsigned int channel;
-
- + if (vc4->firmware_kms)
- + continue;
- +
- /* Nothing to do here, let's skip it */
- if (old_crtc_state->enable == new_crtc_state->enable)
- continue;
- @@ -995,6 +1014,8 @@ int vc4_kms_load(struct drm_device *dev)
- dev->mode_config.helper_private = &vc4_mode_config_helpers;
- dev->mode_config.preferred_depth = 24;
- dev->mode_config.async_page_flip = true;
- + if (vc4->firmware_kms)
- + dev->mode_config.normalize_zpos = true;
-
- ret = vc4_ctm_obj_init(vc4);
- if (ret)
- --- /dev/null
- +++ b/drivers/gpu/drm/vc4/vc_image_types.h
- @@ -0,0 +1,175 @@
- +
- +/*
- + * Copyright (c) 2012, Broadcom Europe Ltd
- + *
- + * Values taken from vc_image_types.h released by Broadcom at
- + * https://github.com/raspberrypi/userland/blob/master/interface/vctypes/vc_image_types.h
- + * and vc_image_structs.h at
- + * https://github.com/raspberrypi/userland/blob/master/interface/vctypes/vc_image_structs.h
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License version 2 as
- + * published by the Free Software Foundation.
- + */
- +
- +enum {
- + VC_IMAGE_MIN = 0, //bounds for error checking
- +
- + VC_IMAGE_RGB565 = 1,
- + VC_IMAGE_1BPP,
- + VC_IMAGE_YUV420,
- + VC_IMAGE_48BPP,
- + VC_IMAGE_RGB888,
- + VC_IMAGE_8BPP,
- + /* 4bpp palettised image */
- + VC_IMAGE_4BPP,
- + /* A separated format of 16 colour/light shorts followed by 16 z
- + * values
- + */
- + VC_IMAGE_3D32,
- + /* 16 colours followed by 16 z values */
- + VC_IMAGE_3D32B,
- + /* A separated format of 16 material/colour/light shorts followed by
- + * 16 z values
- + */
- + VC_IMAGE_3D32MAT,
- + /* 32 bit format containing 18 bits of 6.6.6 RGB, 9 bits per short */
- + VC_IMAGE_RGB2X9,
- + /* 32-bit format holding 18 bits of 6.6.6 RGB */
- + VC_IMAGE_RGB666,
- + /* 4bpp palettised image with embedded palette */
- + VC_IMAGE_PAL4_OBSOLETE,
- + /* 8bpp palettised image with embedded palette */
- + VC_IMAGE_PAL8_OBSOLETE,
- + /* RGB888 with an alpha byte after each pixel */
- + VC_IMAGE_RGBA32,
- + /* a line of Y (32-byte padded), a line of U (16-byte padded), and a
- + * line of V (16-byte padded)
- + */
- + VC_IMAGE_YUV422,
- + /* RGB565 with a transparent patch */
- + VC_IMAGE_RGBA565,
- + /* Compressed (4444) version of RGBA32 */
- + VC_IMAGE_RGBA16,
- + /* VCIII codec format */
- + VC_IMAGE_YUV_UV,
- + /* VCIII T-format RGBA8888 */
- + VC_IMAGE_TF_RGBA32,
- + /* VCIII T-format RGBx8888 */
- + VC_IMAGE_TF_RGBX32,
- + /* VCIII T-format float */
- + VC_IMAGE_TF_FLOAT,
- + /* VCIII T-format RGBA4444 */
- + VC_IMAGE_TF_RGBA16,
- + /* VCIII T-format RGB5551 */
- + VC_IMAGE_TF_RGBA5551,
- + /* VCIII T-format RGB565 */
- + VC_IMAGE_TF_RGB565,
- + /* VCIII T-format 8-bit luma and 8-bit alpha */
- + VC_IMAGE_TF_YA88,
- + /* VCIII T-format 8 bit generic sample */
- + VC_IMAGE_TF_BYTE,
- + /* VCIII T-format 8-bit palette */
- + VC_IMAGE_TF_PAL8,
- + /* VCIII T-format 4-bit palette */
- + VC_IMAGE_TF_PAL4,
- + /* VCIII T-format Ericsson Texture Compressed */
- + VC_IMAGE_TF_ETC1,
- + /* RGB888 with R & B swapped */
- + VC_IMAGE_BGR888,
- + /* RGB888 with R & B swapped, but with no pitch, i.e. no padding after
- + * each row of pixels
- + */
- + VC_IMAGE_BGR888_NP,
- + /* Bayer image, extra defines which variant is being used */
- + VC_IMAGE_BAYER,
- + /* General wrapper for codec images e.g. JPEG from camera */
- + VC_IMAGE_CODEC,
- + /* VCIII codec format */
- + VC_IMAGE_YUV_UV32,
- + /* VCIII T-format 8-bit luma */
- + VC_IMAGE_TF_Y8,
- + /* VCIII T-format 8-bit alpha */
- + VC_IMAGE_TF_A8,
- + /* VCIII T-format 16-bit generic sample */
- + VC_IMAGE_TF_SHORT,
- + /* VCIII T-format 1bpp black/white */
- + VC_IMAGE_TF_1BPP,
- + VC_IMAGE_OPENGL,
- + /* VCIII-B0 HVS YUV 4:4:4 interleaved samples */
- + VC_IMAGE_YUV444I,
- + /* Y, U, & V planes separately (VC_IMAGE_YUV422 has them interleaved on
- + * a per line basis)
- + */
- + VC_IMAGE_YUV422PLANAR,
- + /* 32bpp with 8bit alpha at MS byte, with R, G, B (LS byte) */
- + VC_IMAGE_ARGB8888,
- + /* 32bpp with 8bit unused at MS byte, with R, G, B (LS byte) */
- + VC_IMAGE_XRGB8888,
- +
- + /* interleaved 8 bit samples of Y, U, Y, V (4 flavours) */
- + VC_IMAGE_YUV422YUYV,
- + VC_IMAGE_YUV422YVYU,
- + VC_IMAGE_YUV422UYVY,
- + VC_IMAGE_YUV422VYUY,
- +
- + /* 32bpp like RGBA32 but with unused alpha */
- + VC_IMAGE_RGBX32,
- + /* 32bpp, corresponding to RGBA with unused alpha */
- + VC_IMAGE_RGBX8888,
- + /* 32bpp, corresponding to BGRA with unused alpha */
- + VC_IMAGE_BGRX8888,
- +
- + /* Y as a plane, then UV byte interleaved in plane with same pitch,
- + * half height
- + */
- + VC_IMAGE_YUV420SP,
- +
- + /* Y, U, & V planes separately 4:4:4 */
- + VC_IMAGE_YUV444PLANAR,
- +
- + /* T-format 8-bit U - same as TF_Y8 buf from U plane */
- + VC_IMAGE_TF_U8,
- + /* T-format 8-bit U - same as TF_Y8 buf from V plane */
- + VC_IMAGE_TF_V8,
- +
- + /* YUV4:2:0 planar, 16bit values */
- + VC_IMAGE_YUV420_16,
- + /* YUV4:2:0 codec format, 16bit values */
- + VC_IMAGE_YUV_UV_16,
- + /* YUV4:2:0 with U,V in side-by-side format */
- + VC_IMAGE_YUV420_S,
- + /* 10-bit YUV 420 column image format */
- + VC_IMAGE_YUV10COL,
- + /* 32-bpp, 10-bit R/G/B, 2-bit Alpha */
- + VC_IMAGE_RGBA1010102,
- +
- + VC_IMAGE_MAX, /* bounds for error checking */
- + VC_IMAGE_FORCE_ENUM_16BIT = 0xffff,
- +};
- +
- +enum {
- + /* Unknown or unset - defaults to BT601 interstitial */
- + VC_IMAGE_YUVINFO_UNSPECIFIED = 0,
- +
- + /* colour-space conversions data [4 bits] */
- +
- + /* ITU-R BT.601-5 [SDTV] (compatible with VideoCore-II) */
- + VC_IMAGE_YUVINFO_CSC_ITUR_BT601 = 1,
- + /* ITU-R BT.709-3 [HDTV] */
- + VC_IMAGE_YUVINFO_CSC_ITUR_BT709 = 2,
- + /* JPEG JFIF */
- + VC_IMAGE_YUVINFO_CSC_JPEG_JFIF = 3,
- + /* Title 47 Code of Federal Regulations (2003) 73.682 (a) (20) */
- + VC_IMAGE_YUVINFO_CSC_FCC = 4,
- + /* Society of Motion Picture and Television Engineers 240M (1999) */
- + VC_IMAGE_YUVINFO_CSC_SMPTE_240M = 5,
- + /* ITU-R BT.470-2 System M */
- + VC_IMAGE_YUVINFO_CSC_ITUR_BT470_2_M = 6,
- + /* ITU-R BT.470-2 System B,G */
- + VC_IMAGE_YUVINFO_CSC_ITUR_BT470_2_BG = 7,
- + /* JPEG JFIF, but with 16..255 luma */
- + VC_IMAGE_YUVINFO_CSC_JPEG_JFIF_Y16_255 = 8,
- + /* Rec 2020 */
- + VC_IMAGE_YUVINFO_CSC_REC_2020 = 9,
- +};
- --- a/include/soc/bcm2835/raspberrypi-firmware.h
- +++ b/include/soc/bcm2835/raspberrypi-firmware.h
- @@ -73,6 +73,7 @@ enum rpi_firmware_property_tag {
- RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014,
- RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020,
- RPI_FIRMWARE_GET_CUSTOMER_OTP = 0x00030021,
- + RPI_FIRMWARE_GET_EDID_BLOCK_DISPLAY = 0x00030023,
- RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030,
- RPI_FIRMWARE_GET_THROTTLED = 0x00030046,
- RPI_FIRMWARE_GET_CLOCK_MEASURED = 0x00030047,
- @@ -149,6 +150,11 @@ enum rpi_firmware_property_tag {
-
- RPI_FIRMWARE_VCHIQ_INIT = 0x00048010,
-
- + RPI_FIRMWARE_SET_PLANE = 0x00048015,
- + RPI_FIRMWARE_GET_DISPLAY_TIMING = 0x00040017,
- + RPI_FIRMWARE_SET_TIMING = 0x00048017,
- + RPI_FIRMWARE_GET_DISPLAY_CFG = 0x00040018,
- + RPI_FIRMWARE_SET_DISPLAY_POWER = 0x00048019,
- RPI_FIRMWARE_GET_COMMAND_LINE = 0x00050001,
- RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001,
- };
|