Merge branch 'drm-next-merged' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie: "Highlights: Core: - Virtual GEM layer merged, this has been around for a long time, and it provides a software backed device that allows userspace to use it as a GEM shared memory handler. This makes it a lot easier to do certain things when you have no GPU but still have to deal with DRI expectations. - atomic helper updates. - framebuffer modifier interface added. - i2c over auxch displayport fixes. - fb width/height confusion fixes. - new driver for ps8622/ps8625 bridge chips - lots of new panels i915: - more plane atomic conversion - vGPU guest support for XenGT - Skylake workarounds and fixes - Y-tiling support - work on dynamic pagetable allocation - EU count report param for gen9+ - CHV fixes (no longer prelim) - remove ilk rc6 - frontbuffer tracking for fbc - Displayport link rate refactoring - sprite colorkey refactor radeon: - Displayport MST support (not enabled by default) - non-ATOM native hw auxch support (DCE5+) - output csc support - new queries for userspace debug support - new VCE packet nouveau: - gk20a iommu support - gm107 graphics support - more gm20x bringup (waiting on signed nvidia fw). amdkfd: - multiple kgd instance support - use 64-bit time accessors msm: - stolen memory support - DSI and dual-DSI support - snapdragon 410 support exynos: - cleanups for atomic and pageflip imx-drm: - more media-bus formats - TV output prep - drm panel support tegra: - hw vblank counter using host1x syncpoints omap: - universal plane support - prep work for atomic modesetting rcar-du: - ported to atomic modesetting atmel-hlcdc: - ported to atomic modesetting - added suspend/resume support sti: - ported to atomic modesetting dwhdmi: - more compliant audio support - update rockchip phy support tda998x: - DT probing for attached crtcs - simplified EDID reading rockchip: - fixes adv7511: - fixes" * 'drm-next-merged' of git://people.freedesktop.org/~airlied/linux: (689 commits) media-bus: Fixup RGB444_1X12, RGB565_1X16, and YUV8_1X24 media bus format drm/i915: Dont enable CS_PARSER_ERROR interrupts at all drm/i915: Move drm_framebuffer_unreference out of struct_mutex for takeover drm: fix trivial typo mistake drm: Make integer overflow checking cover universal cursor updates (v2) drm/nouveau/bios: fix fetching from acpi on certain systems drm/nouveau/gr/gm206: initial init+ctx code drm/nouveau/ce/gm206: enable support via gm204 code drm/nouveau/fifo/gm206: enable support via gm204 code drm/nouveau/gr/gm204: initial init+ctx code drm/nouveau: support for buffer moves via MaxwellDmaCopyA drm/nouveau/ce/gm204: initial support drm/nouveau: add support for gm20x fifo channels drm/nouveau/fifo/gm204: initial support drm/nouveau/gr/gk104-: prevent reading non-existent regs in intr handler drm/nouveau/gr/gm107: very slightly demagic part of attrib cb setup drm/nouveau/gr/gk104-: correct crop/zrop num_active_fbps setting drm/nouveau/gr/gf100-: add symbolic names for classes drm/nouveau/gr/gm107: support tpc "strand" ctxsw in gpccs ucode drm/nouveau/gr/gf100-: support mmio access with gpc offset from gpccs ucode ...
This commit is contained in:
commit
14aa024490
@ -3979,6 +3979,11 @@ int num_ioctls;</synopsis>
|
||||
!Fdrivers/gpu/drm/i915/i915_irq.c intel_runtime_pm_disable_interrupts
|
||||
!Fdrivers/gpu/drm/i915/i915_irq.c intel_runtime_pm_enable_interrupts
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Intel GVT-g Guest Support(vGPU)</title>
|
||||
!Pdrivers/gpu/drm/i915/i915_vgpu.c Intel GVT-g guest support
|
||||
!Idrivers/gpu/drm/i915/i915_vgpu.c
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1>
|
||||
<title>Display Hardware Handling</title>
|
||||
@ -4046,6 +4051,17 @@ int num_ioctls;</synopsis>
|
||||
<title>Frame Buffer Compression (FBC)</title>
|
||||
!Pdrivers/gpu/drm/i915/intel_fbc.c Frame Buffer Compression (FBC)
|
||||
!Idrivers/gpu/drm/i915/intel_fbc.c
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Display Refresh Rate Switching (DRRS)</title>
|
||||
!Pdrivers/gpu/drm/i915/intel_dp.c Display Refresh Rate Switching (DRRS)
|
||||
!Fdrivers/gpu/drm/i915/intel_dp.c intel_dp_set_drrs_state
|
||||
!Fdrivers/gpu/drm/i915/intel_dp.c intel_edp_drrs_enable
|
||||
!Fdrivers/gpu/drm/i915/intel_dp.c intel_edp_drrs_disable
|
||||
!Fdrivers/gpu/drm/i915/intel_dp.c intel_edp_drrs_invalidate
|
||||
!Fdrivers/gpu/drm/i915/intel_dp.c intel_edp_drrs_flush
|
||||
!Fdrivers/gpu/drm/i915/intel_dp.c intel_dp_drrs_init
|
||||
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>DPIO</title>
|
||||
@ -4168,7 +4184,7 @@ int num_ioctls;</synopsis>
|
||||
<sect2>
|
||||
<title>Buffer Object Eviction</title>
|
||||
<para>
|
||||
This section documents the interface function for evicting buffer
|
||||
This section documents the interface functions for evicting buffer
|
||||
objects to make space available in the virtual gpu address spaces.
|
||||
Note that this is mostly orthogonal to shrinking buffer objects
|
||||
caches, which has the goal to make main memory (shared with the gpu
|
||||
@ -4176,6 +4192,17 @@ int num_ioctls;</synopsis>
|
||||
</para>
|
||||
!Idrivers/gpu/drm/i915/i915_gem_evict.c
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Buffer Object Memory Shrinking</title>
|
||||
<para>
|
||||
This section documents the interface function for shrinking memory
|
||||
usage of buffer object caches. Shrinking is used to make main memory
|
||||
available. Note that this is mostly orthogonal to evicting buffer
|
||||
objects, which has the goal to make space in gpu virtual address
|
||||
spaces.
|
||||
</para>
|
||||
!Idrivers/gpu/drm/i915/i915_gem_shrinker.c
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
|
@ -91,7 +91,9 @@ see <xref linkend="colorspaces" />.</entry>
|
||||
<listitem><para>For formats where the total number of bits per pixel is smaller
|
||||
than the number of bus samples per pixel times the bus width, a padding
|
||||
value stating if the bytes are padded in their most high order bits
|
||||
(PADHI) or low order bits (PADLO).</para></listitem>
|
||||
(PADHI) or low order bits (PADLO). A "C" prefix is used for component-wise
|
||||
padding in the most high order bits (CPADHI) or low order bits (CPADLO)
|
||||
of each separate component.</para></listitem>
|
||||
<listitem><para>For formats where the number of bus samples per pixel is larger
|
||||
than 1, an endianness value stating if the pixel is transferred MSB first
|
||||
(BE) or LSB first (LE).</para></listitem>
|
||||
@ -192,6 +194,24 @@ see <xref linkend="colorspaces" />.</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<row id="MEDIA-BUS-FMT-RGB444-1X12">
|
||||
<entry>MEDIA_BUS_FMT_RGB444_1X12</entry>
|
||||
<entry>0x1016</entry>
|
||||
<entry></entry>
|
||||
&dash-ent-20;
|
||||
<entry>r<subscript>3</subscript></entry>
|
||||
<entry>r<subscript>2</subscript></entry>
|
||||
<entry>r<subscript>1</subscript></entry>
|
||||
<entry>r<subscript>0</subscript></entry>
|
||||
<entry>g<subscript>3</subscript></entry>
|
||||
<entry>g<subscript>2</subscript></entry>
|
||||
<entry>g<subscript>1</subscript></entry>
|
||||
<entry>g<subscript>0</subscript></entry>
|
||||
<entry>b<subscript>3</subscript></entry>
|
||||
<entry>b<subscript>2</subscript></entry>
|
||||
<entry>b<subscript>1</subscript></entry>
|
||||
<entry>b<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-RGB444-2X8-PADHI-BE">
|
||||
<entry>MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE</entry>
|
||||
<entry>0x1001</entry>
|
||||
@ -304,6 +324,28 @@ see <xref linkend="colorspaces" />.</entry>
|
||||
<entry>g<subscript>4</subscript></entry>
|
||||
<entry>g<subscript>3</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-RGB565-1X16">
|
||||
<entry>MEDIA_BUS_FMT_RGB565_1X16</entry>
|
||||
<entry>0x1017</entry>
|
||||
<entry></entry>
|
||||
&dash-ent-16;
|
||||
<entry>r<subscript>4</subscript></entry>
|
||||
<entry>r<subscript>3</subscript></entry>
|
||||
<entry>r<subscript>2</subscript></entry>
|
||||
<entry>r<subscript>1</subscript></entry>
|
||||
<entry>r<subscript>0</subscript></entry>
|
||||
<entry>g<subscript>5</subscript></entry>
|
||||
<entry>g<subscript>4</subscript></entry>
|
||||
<entry>g<subscript>3</subscript></entry>
|
||||
<entry>g<subscript>2</subscript></entry>
|
||||
<entry>g<subscript>1</subscript></entry>
|
||||
<entry>g<subscript>0</subscript></entry>
|
||||
<entry>b<subscript>4</subscript></entry>
|
||||
<entry>b<subscript>3</subscript></entry>
|
||||
<entry>b<subscript>2</subscript></entry>
|
||||
<entry>b<subscript>1</subscript></entry>
|
||||
<entry>b<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-BGR565-2X8-BE">
|
||||
<entry>MEDIA_BUS_FMT_BGR565_2X8_BE</entry>
|
||||
<entry>0x1005</entry>
|
||||
@ -440,6 +482,96 @@ see <xref linkend="colorspaces" />.</entry>
|
||||
<entry>b<subscript>1</subscript></entry>
|
||||
<entry>b<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-RGB666-1X24_CPADHI">
|
||||
<entry>MEDIA_BUS_FMT_RGB666_1X24_CPADHI</entry>
|
||||
<entry>0x1015</entry>
|
||||
<entry></entry>
|
||||
&dash-ent-8;
|
||||
<entry>0</entry>
|
||||
<entry>0</entry>
|
||||
<entry>r<subscript>5</subscript></entry>
|
||||
<entry>r<subscript>4</subscript></entry>
|
||||
<entry>r<subscript>3</subscript></entry>
|
||||
<entry>r<subscript>2</subscript></entry>
|
||||
<entry>r<subscript>1</subscript></entry>
|
||||
<entry>r<subscript>0</subscript></entry>
|
||||
<entry>0</entry>
|
||||
<entry>0</entry>
|
||||
<entry>g<subscript>5</subscript></entry>
|
||||
<entry>g<subscript>4</subscript></entry>
|
||||
<entry>g<subscript>3</subscript></entry>
|
||||
<entry>g<subscript>2</subscript></entry>
|
||||
<entry>g<subscript>1</subscript></entry>
|
||||
<entry>g<subscript>0</subscript></entry>
|
||||
<entry>0</entry>
|
||||
<entry>0</entry>
|
||||
<entry>b<subscript>5</subscript></entry>
|
||||
<entry>b<subscript>4</subscript></entry>
|
||||
<entry>b<subscript>3</subscript></entry>
|
||||
<entry>b<subscript>2</subscript></entry>
|
||||
<entry>b<subscript>1</subscript></entry>
|
||||
<entry>b<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-BGR888-1X24">
|
||||
<entry>MEDIA_BUS_FMT_BGR888_1X24</entry>
|
||||
<entry>0x1013</entry>
|
||||
<entry></entry>
|
||||
&dash-ent-8;
|
||||
<entry>b<subscript>7</subscript></entry>
|
||||
<entry>b<subscript>6</subscript></entry>
|
||||
<entry>b<subscript>5</subscript></entry>
|
||||
<entry>b<subscript>4</subscript></entry>
|
||||
<entry>b<subscript>3</subscript></entry>
|
||||
<entry>b<subscript>2</subscript></entry>
|
||||
<entry>b<subscript>1</subscript></entry>
|
||||
<entry>b<subscript>0</subscript></entry>
|
||||
<entry>g<subscript>7</subscript></entry>
|
||||
<entry>g<subscript>6</subscript></entry>
|
||||
<entry>g<subscript>5</subscript></entry>
|
||||
<entry>g<subscript>4</subscript></entry>
|
||||
<entry>g<subscript>3</subscript></entry>
|
||||
<entry>g<subscript>2</subscript></entry>
|
||||
<entry>g<subscript>1</subscript></entry>
|
||||
<entry>g<subscript>0</subscript></entry>
|
||||
<entry>r<subscript>7</subscript></entry>
|
||||
<entry>r<subscript>6</subscript></entry>
|
||||
<entry>r<subscript>5</subscript></entry>
|
||||
<entry>r<subscript>4</subscript></entry>
|
||||
<entry>r<subscript>3</subscript></entry>
|
||||
<entry>r<subscript>2</subscript></entry>
|
||||
<entry>r<subscript>1</subscript></entry>
|
||||
<entry>r<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-GBR888-1X24">
|
||||
<entry>MEDIA_BUS_FMT_GBR888_1X24</entry>
|
||||
<entry>0x1014</entry>
|
||||
<entry></entry>
|
||||
&dash-ent-8;
|
||||
<entry>g<subscript>7</subscript></entry>
|
||||
<entry>g<subscript>6</subscript></entry>
|
||||
<entry>g<subscript>5</subscript></entry>
|
||||
<entry>g<subscript>4</subscript></entry>
|
||||
<entry>g<subscript>3</subscript></entry>
|
||||
<entry>g<subscript>2</subscript></entry>
|
||||
<entry>g<subscript>1</subscript></entry>
|
||||
<entry>g<subscript>0</subscript></entry>
|
||||
<entry>b<subscript>7</subscript></entry>
|
||||
<entry>b<subscript>6</subscript></entry>
|
||||
<entry>b<subscript>5</subscript></entry>
|
||||
<entry>b<subscript>4</subscript></entry>
|
||||
<entry>b<subscript>3</subscript></entry>
|
||||
<entry>b<subscript>2</subscript></entry>
|
||||
<entry>b<subscript>1</subscript></entry>
|
||||
<entry>b<subscript>0</subscript></entry>
|
||||
<entry>r<subscript>7</subscript></entry>
|
||||
<entry>r<subscript>6</subscript></entry>
|
||||
<entry>r<subscript>5</subscript></entry>
|
||||
<entry>r<subscript>4</subscript></entry>
|
||||
<entry>r<subscript>3</subscript></entry>
|
||||
<entry>r<subscript>2</subscript></entry>
|
||||
<entry>r<subscript>1</subscript></entry>
|
||||
<entry>r<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-RGB888-1X24">
|
||||
<entry>MEDIA_BUS_FMT_RGB888_1X24</entry>
|
||||
<entry>0x100a</entry>
|
||||
@ -582,6 +714,261 @@ see <xref linkend="colorspaces" />.</entry>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>On LVDS buses, usually each sample is transferred serialized in
|
||||
seven time slots per pixel clock, on three (18-bit) or four (24-bit)
|
||||
differential data pairs at the same time. The remaining bits are used for
|
||||
control signals as defined by SPWG/PSWG/VESA or JEIDA standards.
|
||||
The 24-bit RGB format serialized in seven time slots on four lanes using
|
||||
JEIDA defined bit mapping will be named
|
||||
<constant>MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA</constant>, for example.
|
||||
</para>
|
||||
|
||||
<table pgwide="0" frame="none" id="v4l2-mbus-pixelcode-rgb-lvds">
|
||||
<title>LVDS RGB formats</title>
|
||||
<tgroup cols="8">
|
||||
<colspec colname="id" align="left" />
|
||||
<colspec colname="code" align="center" />
|
||||
<colspec colname="slot" align="center" />
|
||||
<colspec colname="lane" />
|
||||
<colspec colnum="5" colname="l03" align="center" />
|
||||
<colspec colnum="6" colname="l02" align="center" />
|
||||
<colspec colnum="7" colname="l01" align="center" />
|
||||
<colspec colnum="8" colname="l00" align="center" />
|
||||
<spanspec namest="l03" nameend="l00" spanname="l0" />
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Identifier</entry>
|
||||
<entry>Code</entry>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry spanname="l0">Data organization</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>Timeslot</entry>
|
||||
<entry>Lane</entry>
|
||||
<entry>3</entry>
|
||||
<entry>2</entry>
|
||||
<entry>1</entry>
|
||||
<entry>0</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<row id="MEDIA-BUS-FMT-RGB666-1X7X3-SPWG">
|
||||
<entry>MEDIA_BUS_FMT_RGB666_1X7X3_SPWG</entry>
|
||||
<entry>0x1010</entry>
|
||||
<entry>0</entry>
|
||||
<entry></entry>
|
||||
<entry>-</entry>
|
||||
<entry>d</entry>
|
||||
<entry>b<subscript>1</subscript></entry>
|
||||
<entry>g<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>1</entry>
|
||||
<entry></entry>
|
||||
<entry>-</entry>
|
||||
<entry>d</entry>
|
||||
<entry>b<subscript>0</subscript></entry>
|
||||
<entry>r<subscript>5</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>2</entry>
|
||||
<entry></entry>
|
||||
<entry>-</entry>
|
||||
<entry>d</entry>
|
||||
<entry>g<subscript>5</subscript></entry>
|
||||
<entry>r<subscript>4</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>3</entry>
|
||||
<entry></entry>
|
||||
<entry>-</entry>
|
||||
<entry>b<subscript>5</subscript></entry>
|
||||
<entry>g<subscript>4</subscript></entry>
|
||||
<entry>r<subscript>3</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>4</entry>
|
||||
<entry></entry>
|
||||
<entry>-</entry>
|
||||
<entry>b<subscript>4</subscript></entry>
|
||||
<entry>g<subscript>3</subscript></entry>
|
||||
<entry>r<subscript>2</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>5</entry>
|
||||
<entry></entry>
|
||||
<entry>-</entry>
|
||||
<entry>b<subscript>3</subscript></entry>
|
||||
<entry>g<subscript>2</subscript></entry>
|
||||
<entry>r<subscript>1</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>6</entry>
|
||||
<entry></entry>
|
||||
<entry>-</entry>
|
||||
<entry>b<subscript>2</subscript></entry>
|
||||
<entry>g<subscript>1</subscript></entry>
|
||||
<entry>r<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-RGB888-1X7X4-SPWG">
|
||||
<entry>MEDIA_BUS_FMT_RGB888_1X7X4_SPWG</entry>
|
||||
<entry>0x1011</entry>
|
||||
<entry>0</entry>
|
||||
<entry></entry>
|
||||
<entry>d</entry>
|
||||
<entry>d</entry>
|
||||
<entry>b<subscript>1</subscript></entry>
|
||||
<entry>g<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>1</entry>
|
||||
<entry></entry>
|
||||
<entry>b<subscript>7</subscript></entry>
|
||||
<entry>d</entry>
|
||||
<entry>b<subscript>0</subscript></entry>
|
||||
<entry>r<subscript>5</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>2</entry>
|
||||
<entry></entry>
|
||||
<entry>b<subscript>6</subscript></entry>
|
||||
<entry>d</entry>
|
||||
<entry>g<subscript>5</subscript></entry>
|
||||
<entry>r<subscript>4</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>3</entry>
|
||||
<entry></entry>
|
||||
<entry>g<subscript>7</subscript></entry>
|
||||
<entry>b<subscript>5</subscript></entry>
|
||||
<entry>g<subscript>4</subscript></entry>
|
||||
<entry>r<subscript>3</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>4</entry>
|
||||
<entry></entry>
|
||||
<entry>g<subscript>6</subscript></entry>
|
||||
<entry>b<subscript>4</subscript></entry>
|
||||
<entry>g<subscript>3</subscript></entry>
|
||||
<entry>r<subscript>2</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>5</entry>
|
||||
<entry></entry>
|
||||
<entry>r<subscript>7</subscript></entry>
|
||||
<entry>b<subscript>3</subscript></entry>
|
||||
<entry>g<subscript>2</subscript></entry>
|
||||
<entry>r<subscript>1</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>6</entry>
|
||||
<entry></entry>
|
||||
<entry>r<subscript>6</subscript></entry>
|
||||
<entry>b<subscript>2</subscript></entry>
|
||||
<entry>g<subscript>1</subscript></entry>
|
||||
<entry>r<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-RGB888-1X7X4-JEIDA">
|
||||
<entry>MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA</entry>
|
||||
<entry>0x1012</entry>
|
||||
<entry>0</entry>
|
||||
<entry></entry>
|
||||
<entry>d</entry>
|
||||
<entry>d</entry>
|
||||
<entry>b<subscript>3</subscript></entry>
|
||||
<entry>g<subscript>2</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>1</entry>
|
||||
<entry></entry>
|
||||
<entry>b<subscript>1</subscript></entry>
|
||||
<entry>d</entry>
|
||||
<entry>b<subscript>2</subscript></entry>
|
||||
<entry>r<subscript>7</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>2</entry>
|
||||
<entry></entry>
|
||||
<entry>b<subscript>0</subscript></entry>
|
||||
<entry>d</entry>
|
||||
<entry>g<subscript>7</subscript></entry>
|
||||
<entry>r<subscript>6</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>3</entry>
|
||||
<entry></entry>
|
||||
<entry>g<subscript>1</subscript></entry>
|
||||
<entry>b<subscript>7</subscript></entry>
|
||||
<entry>g<subscript>6</subscript></entry>
|
||||
<entry>r<subscript>5</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>4</entry>
|
||||
<entry></entry>
|
||||
<entry>g<subscript>0</subscript></entry>
|
||||
<entry>b<subscript>6</subscript></entry>
|
||||
<entry>g<subscript>5</subscript></entry>
|
||||
<entry>r<subscript>4</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>5</entry>
|
||||
<entry></entry>
|
||||
<entry>r<subscript>1</subscript></entry>
|
||||
<entry>b<subscript>5</subscript></entry>
|
||||
<entry>g<subscript>4</subscript></entry>
|
||||
<entry>r<subscript>3</subscript></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
<entry>6</entry>
|
||||
<entry></entry>
|
||||
<entry>r<subscript>0</subscript></entry>
|
||||
<entry>b<subscript>4</subscript></entry>
|
||||
<entry>g<subscript>3</subscript></entry>
|
||||
<entry>r<subscript>2</subscript></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@ -2660,6 +3047,43 @@ see <xref linkend="colorspaces" />.</entry>
|
||||
<entry>u<subscript>1</subscript></entry>
|
||||
<entry>u<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-YUV8-1X24">
|
||||
<entry>MEDIA_BUS_FMT_YUV8_1X24</entry>
|
||||
<entry>0x2025</entry>
|
||||
<entry></entry>
|
||||
<entry>-</entry>
|
||||
<entry>-</entry>
|
||||
<entry>-</entry>
|
||||
<entry>-</entry>
|
||||
<entry>-</entry>
|
||||
<entry>-</entry>
|
||||
<entry>-</entry>
|
||||
<entry>-</entry>
|
||||
<entry>y<subscript>7</subscript></entry>
|
||||
<entry>y<subscript>6</subscript></entry>
|
||||
<entry>y<subscript>5</subscript></entry>
|
||||
<entry>y<subscript>4</subscript></entry>
|
||||
<entry>y<subscript>3</subscript></entry>
|
||||
<entry>y<subscript>2</subscript></entry>
|
||||
<entry>y<subscript>1</subscript></entry>
|
||||
<entry>y<subscript>0</subscript></entry>
|
||||
<entry>u<subscript>7</subscript></entry>
|
||||
<entry>u<subscript>6</subscript></entry>
|
||||
<entry>u<subscript>5</subscript></entry>
|
||||
<entry>u<subscript>4</subscript></entry>
|
||||
<entry>u<subscript>3</subscript></entry>
|
||||
<entry>u<subscript>2</subscript></entry>
|
||||
<entry>u<subscript>1</subscript></entry>
|
||||
<entry>u<subscript>0</subscript></entry>
|
||||
<entry>v<subscript>7</subscript></entry>
|
||||
<entry>v<subscript>6</subscript></entry>
|
||||
<entry>v<subscript>5</subscript></entry>
|
||||
<entry>v<subscript>4</subscript></entry>
|
||||
<entry>v<subscript>3</subscript></entry>
|
||||
<entry>v<subscript>2</subscript></entry>
|
||||
<entry>v<subscript>1</subscript></entry>
|
||||
<entry>v<subscript>0</subscript></entry>
|
||||
</row>
|
||||
<row id="MEDIA-BUS-FMT-YUV10-1X30">
|
||||
<entry>MEDIA_BUS_FMT_YUV10_1X30</entry>
|
||||
<entry>0x2016</entry>
|
||||
|
@ -44,23 +44,30 @@ Optional properties:
|
||||
LVDS Channel
|
||||
============
|
||||
|
||||
Each LVDS Channel has to contain a display-timings node that describes the
|
||||
video timings for the connected LVDS display. For detailed information, also
|
||||
have a look at Documentation/devicetree/bindings/video/display-timing.txt.
|
||||
Each LVDS Channel has to contain either an of graph link to a panel device node
|
||||
or a display-timings node that describes the video timings for the connected
|
||||
LVDS display as well as the fsl,data-mapping and fsl,data-width properties.
|
||||
|
||||
Required properties:
|
||||
- reg : should be <0> or <1>
|
||||
- port: Input and output port nodes with endpoint definitions as defined in
|
||||
Documentation/devicetree/bindings/graph.txt.
|
||||
On i.MX5, the internal two-input-multiplexer is used. Due to hardware
|
||||
limitations, only one input port (port@[0,1]) can be used for each channel
|
||||
(lvds-channel@[0,1], respectively).
|
||||
On i.MX6, there should be four input ports (port@[0-3]) that correspond
|
||||
to the four LVDS multiplexer inputs.
|
||||
A single output port (port@2 on i.MX5, port@4 on i.MX6) must be connected
|
||||
to a panel input port. Optionally, the output port can be left out if
|
||||
display-timings are used instead.
|
||||
|
||||
Optional properties (required if display-timings are used):
|
||||
- display-timings : A node that describes the display timings as defined in
|
||||
Documentation/devicetree/bindings/video/display-timing.txt.
|
||||
- fsl,data-mapping : should be "spwg" or "jeida"
|
||||
This describes how the color bits are laid out in the
|
||||
serialized LVDS signal.
|
||||
- fsl,data-width : should be <18> or <24>
|
||||
- port: A port node with endpoint definitions as defined in
|
||||
Documentation/devicetree/bindings/media/video-interfaces.txt.
|
||||
On i.MX5, the internal two-input-multiplexer is used.
|
||||
Due to hardware limitations, only one port (port@[0,1])
|
||||
can be used for each channel (lvds-channel@[0,1], respectively)
|
||||
On i.MX6, there should be four ports (port@[0-3]) that correspond
|
||||
to the four LVDS multiplexer inputs.
|
||||
|
||||
example:
|
||||
|
||||
@ -73,23 +80,21 @@ ldb: ldb@53fa8008 {
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx53-ldb";
|
||||
gpr = <&gpr>;
|
||||
clocks = <&clks 122>, <&clks 120>,
|
||||
<&clks 115>, <&clks 116>,
|
||||
<&clks 123>, <&clks 85>;
|
||||
clocks = <&clks IMX5_CLK_LDB_DI0_SEL>,
|
||||
<&clks IMX5_CLK_LDB_DI1_SEL>,
|
||||
<&clks IMX5_CLK_IPU_DI0_SEL>,
|
||||
<&clks IMX5_CLK_IPU_DI1_SEL>,
|
||||
<&clks IMX5_CLK_LDB_DI0_GATE>,
|
||||
<&clks IMX5_CLK_LDB_DI1_GATE>;
|
||||
clock-names = "di0_pll", "di1_pll",
|
||||
"di0_sel", "di1_sel",
|
||||
"di0", "di1";
|
||||
|
||||
/* Using an of-graph endpoint link to connect the panel */
|
||||
lvds-channel@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>;
|
||||
fsl,data-mapping = "spwg";
|
||||
fsl,data-width = <24>;
|
||||
|
||||
display-timings {
|
||||
/* ... */
|
||||
};
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
@ -98,8 +103,17 @@ ldb: ldb@53fa8008 {
|
||||
remote-endpoint = <&ipu_di0_lvds0>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
|
||||
lvds0_out: endpoint {
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/* Using display-timings and fsl,data-mapping/width instead */
|
||||
lvds-channel@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@ -120,3 +134,13 @@ ldb: ldb@53fa8008 {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
panel: lvds-panel {
|
||||
/* ... */
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&lvds0_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -0,0 +1,7 @@
|
||||
Ampire AM-800480R3TMQW-A1H 7.0" WVGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "ampire,am800480r3tmqwa1h"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,7 @@
|
||||
AU Optronics Corporation 10.1" WSVGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "auo,b101ean01"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,7 @@
|
||||
Innolux AT043TN24 4.3" WQVGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "innolux,at043tn24"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,7 @@
|
||||
Innolux Corporation 7.0" WSVGA (1024x600) TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "innolux,zj070na-01p"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,7 @@
|
||||
OrtusTech COM43H4M85ULC Blanview 3.7" TFT-LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "ortustech,com43h4m85ulc"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,7 @@
|
||||
Samsung Electronics 14" WXGA (1366x768) TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "samsung,ltn140at29-301"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -0,0 +1,7 @@
|
||||
Shelly SCA07010-BFN-LNN 7.0" WVGA TFT LCD panel
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "shelly,sca07010-bfn-lnn"
|
||||
|
||||
This binding is compatible with the simple-panel binding, which is specified
|
||||
in simple-panel.txt in this directory.
|
@ -17,6 +17,7 @@ altr Altera Corp.
|
||||
amcc Applied Micro Circuits Corporation (APM, formally AMCC)
|
||||
amd Advanced Micro Devices (AMD), Inc.
|
||||
amlogic Amlogic, Inc.
|
||||
ampire Ampire Co., Ltd.
|
||||
ams AMS AG
|
||||
amstaos AMS-Taos Inc.
|
||||
apm Applied Micro Circuits Corporation (APM)
|
||||
@ -135,6 +136,7 @@ nvidia NVIDIA
|
||||
nxp NXP Semiconductors
|
||||
onnn ON Semiconductor Corp.
|
||||
opencores OpenCores.org
|
||||
ortustech Ortus Technology Co., Ltd.
|
||||
ovti OmniVision Technologies
|
||||
panasonic Panasonic Corporation
|
||||
parade Parade Technologies Inc.
|
||||
|
@ -3417,7 +3417,6 @@ T: git git://people.freedesktop.org/~airlied/linux
|
||||
S: Supported
|
||||
F: drivers/gpu/drm/rcar-du/
|
||||
F: drivers/gpu/drm/shmobile/
|
||||
F: include/linux/platform_data/rcar-du.h
|
||||
F: include/linux/platform_data/shmob_drm.h
|
||||
|
||||
DSBR100 USB FM RADIO DRIVER
|
||||
|
@ -165,6 +165,15 @@ config DRM_SAVAGE
|
||||
Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister
|
||||
chipset. If M is selected the module will be called savage.
|
||||
|
||||
config DRM_VGEM
|
||||
tristate "Virtual GEM provider"
|
||||
depends on DRM
|
||||
help
|
||||
Choose this option to get a virtual graphics memory manager,
|
||||
as used by Mesa's software renderer for enhanced performance.
|
||||
If M is selected the module will be called vgem.
|
||||
|
||||
|
||||
source "drivers/gpu/drm/exynos/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/rockchip/Kconfig"
|
||||
|
@ -48,6 +48,7 @@ obj-$(CONFIG_DRM_SIS) += sis/
|
||||
obj-$(CONFIG_DRM_SAVAGE)+= savage/
|
||||
obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
|
||||
obj-$(CONFIG_DRM_VIA) +=via/
|
||||
obj-$(CONFIG_DRM_VGEM) += vgem/
|
||||
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
|
||||
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
|
||||
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
|
||||
|
@ -435,21 +435,22 @@ static int kfd_ioctl_get_clock_counters(struct file *filep,
|
||||
{
|
||||
struct kfd_ioctl_get_clock_counters_args *args = data;
|
||||
struct kfd_dev *dev;
|
||||
struct timespec time;
|
||||
struct timespec64 time;
|
||||
|
||||
dev = kfd_device_by_id(args->gpu_id);
|
||||
if (dev == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* Reading GPU clock counter from KGD */
|
||||
args->gpu_clock_counter = kfd2kgd->get_gpu_clock_counter(dev->kgd);
|
||||
args->gpu_clock_counter =
|
||||
dev->kfd2kgd->get_gpu_clock_counter(dev->kgd);
|
||||
|
||||
/* No access to rdtsc. Using raw monotonic time */
|
||||
getrawmonotonic(&time);
|
||||
args->cpu_clock_counter = (uint64_t)timespec_to_ns(&time);
|
||||
getrawmonotonic64(&time);
|
||||
args->cpu_clock_counter = (uint64_t)timespec64_to_ns(&time);
|
||||
|
||||
get_monotonic_boottime(&time);
|
||||
args->system_clock_counter = (uint64_t)timespec_to_ns(&time);
|
||||
get_monotonic_boottime64(&time);
|
||||
args->system_clock_counter = (uint64_t)timespec64_to_ns(&time);
|
||||
|
||||
/* Since the counter is in nano-seconds we use 1GHz frequency */
|
||||
args->system_clock_freq = 1000000000;
|
||||
|
@ -94,7 +94,8 @@ static const struct kfd_device_info *lookup_device_info(unsigned short did)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct kfd_dev *kgd2kfd_probe(struct kgd_dev *kgd, struct pci_dev *pdev)
|
||||
struct kfd_dev *kgd2kfd_probe(struct kgd_dev *kgd,
|
||||
struct pci_dev *pdev, const struct kfd2kgd_calls *f2g)
|
||||
{
|
||||
struct kfd_dev *kfd;
|
||||
|
||||
@ -112,6 +113,11 @@ struct kfd_dev *kgd2kfd_probe(struct kgd_dev *kgd, struct pci_dev *pdev)
|
||||
kfd->device_info = device_info;
|
||||
kfd->pdev = pdev;
|
||||
kfd->init_complete = false;
|
||||
kfd->kfd2kgd = f2g;
|
||||
|
||||
mutex_init(&kfd->doorbell_mutex);
|
||||
memset(&kfd->doorbell_available_index, 0,
|
||||
sizeof(kfd->doorbell_available_index));
|
||||
|
||||
return kfd;
|
||||
}
|
||||
@ -200,8 +206,9 @@ bool kgd2kfd_device_init(struct kfd_dev *kfd,
|
||||
/* add another 512KB for all other allocations on gart (HPD, fences) */
|
||||
size += 512 * 1024;
|
||||
|
||||
if (kfd2kgd->init_gtt_mem_allocation(kfd->kgd, size, &kfd->gtt_mem,
|
||||
&kfd->gtt_start_gpu_addr, &kfd->gtt_start_cpu_ptr)) {
|
||||
if (kfd->kfd2kgd->init_gtt_mem_allocation(
|
||||
kfd->kgd, size, &kfd->gtt_mem,
|
||||
&kfd->gtt_start_gpu_addr, &kfd->gtt_start_cpu_ptr)){
|
||||
dev_err(kfd_device,
|
||||
"Could not allocate %d bytes for device (%x:%x)\n",
|
||||
size, kfd->pdev->vendor, kfd->pdev->device);
|
||||
@ -270,7 +277,7 @@ device_iommu_pasid_error:
|
||||
kfd_topology_add_device_error:
|
||||
kfd_gtt_sa_fini(kfd);
|
||||
kfd_gtt_sa_init_error:
|
||||
kfd2kgd->free_gtt_mem(kfd->kgd, kfd->gtt_mem);
|
||||
kfd->kfd2kgd->free_gtt_mem(kfd->kgd, kfd->gtt_mem);
|
||||
dev_err(kfd_device,
|
||||
"device (%x:%x) NOT added due to errors\n",
|
||||
kfd->pdev->vendor, kfd->pdev->device);
|
||||
@ -285,7 +292,7 @@ void kgd2kfd_device_exit(struct kfd_dev *kfd)
|
||||
amd_iommu_free_device(kfd->pdev);
|
||||
kfd_topology_remove_device(kfd);
|
||||
kfd_gtt_sa_fini(kfd);
|
||||
kfd2kgd->free_gtt_mem(kfd->kgd, kfd->gtt_mem);
|
||||
kfd->kfd2kgd->free_gtt_mem(kfd->kgd, kfd->gtt_mem);
|
||||
}
|
||||
|
||||
kfree(kfd);
|
||||
|
@ -82,7 +82,8 @@ static inline unsigned int get_pipes_num_cpsch(void)
|
||||
void program_sh_mem_settings(struct device_queue_manager *dqm,
|
||||
struct qcm_process_device *qpd)
|
||||
{
|
||||
return kfd2kgd->program_sh_mem_settings(dqm->dev->kgd, qpd->vmid,
|
||||
return dqm->dev->kfd2kgd->program_sh_mem_settings(
|
||||
dqm->dev->kgd, qpd->vmid,
|
||||
qpd->sh_mem_config,
|
||||
qpd->sh_mem_ape1_base,
|
||||
qpd->sh_mem_ape1_limit,
|
||||
@ -457,9 +458,12 @@ set_pasid_vmid_mapping(struct device_queue_manager *dqm, unsigned int pasid,
|
||||
{
|
||||
uint32_t pasid_mapping;
|
||||
|
||||
pasid_mapping = (pasid == 0) ? 0 : (uint32_t)pasid |
|
||||
ATC_VMID_PASID_MAPPING_VALID;
|
||||
return kfd2kgd->set_pasid_vmid_mapping(dqm->dev->kgd, pasid_mapping,
|
||||
pasid_mapping = (pasid == 0) ? 0 :
|
||||
(uint32_t)pasid |
|
||||
ATC_VMID_PASID_MAPPING_VALID;
|
||||
|
||||
return dqm->dev->kfd2kgd->set_pasid_vmid_mapping(
|
||||
dqm->dev->kgd, pasid_mapping,
|
||||
vmid);
|
||||
}
|
||||
|
||||
@ -511,7 +515,7 @@ int init_pipelines(struct device_queue_manager *dqm,
|
||||
pipe_hpd_addr = dqm->pipelines_addr + i * CIK_HPD_EOP_BYTES;
|
||||
pr_debug("kfd: pipeline address %llX\n", pipe_hpd_addr);
|
||||
/* = log2(bytes/4)-1 */
|
||||
kfd2kgd->init_pipeline(dqm->dev->kgd, inx,
|
||||
dqm->dev->kfd2kgd->init_pipeline(dqm->dev->kgd, inx,
|
||||
CIK_HPD_EOP_BYTES_LOG2 - 3, pipe_hpd_addr);
|
||||
}
|
||||
|
||||
@ -905,7 +909,7 @@ out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int fence_wait_timeout(unsigned int *fence_addr,
|
||||
static int amdkfd_fence_wait_timeout(unsigned int *fence_addr,
|
||||
unsigned int fence_value,
|
||||
unsigned long timeout)
|
||||
{
|
||||
@ -961,7 +965,7 @@ static int destroy_queues_cpsch(struct device_queue_manager *dqm, bool lock)
|
||||
pm_send_query_status(&dqm->packets, dqm->fence_gpu_addr,
|
||||
KFD_FENCE_COMPLETED);
|
||||
/* should be timed out */
|
||||
fence_wait_timeout(dqm->fence_addr, KFD_FENCE_COMPLETED,
|
||||
amdkfd_fence_wait_timeout(dqm->fence_addr, KFD_FENCE_COMPLETED,
|
||||
QUEUE_PREEMPT_DEFAULT_TIMEOUT_MS);
|
||||
pm_release_ib(&dqm->packets);
|
||||
dqm->active_runlist = false;
|
||||
|
@ -32,9 +32,6 @@
|
||||
* and that's assures that any user process won't get access to the
|
||||
* kernel doorbells page
|
||||
*/
|
||||
static DEFINE_MUTEX(doorbell_mutex);
|
||||
static unsigned long doorbell_available_index[
|
||||
DIV_ROUND_UP(KFD_MAX_NUM_OF_QUEUES_PER_PROCESS, BITS_PER_LONG)] = { 0 };
|
||||
|
||||
#define KERNEL_DOORBELL_PASID 1
|
||||
#define KFD_SIZE_OF_DOORBELL_IN_BYTES 4
|
||||
@ -170,12 +167,12 @@ u32 __iomem *kfd_get_kernel_doorbell(struct kfd_dev *kfd,
|
||||
|
||||
BUG_ON(!kfd || !doorbell_off);
|
||||
|
||||
mutex_lock(&doorbell_mutex);
|
||||
inx = find_first_zero_bit(doorbell_available_index,
|
||||
mutex_lock(&kfd->doorbell_mutex);
|
||||
inx = find_first_zero_bit(kfd->doorbell_available_index,
|
||||
KFD_MAX_NUM_OF_QUEUES_PER_PROCESS);
|
||||
|
||||
__set_bit(inx, doorbell_available_index);
|
||||
mutex_unlock(&doorbell_mutex);
|
||||
__set_bit(inx, kfd->doorbell_available_index);
|
||||
mutex_unlock(&kfd->doorbell_mutex);
|
||||
|
||||
if (inx >= KFD_MAX_NUM_OF_QUEUES_PER_PROCESS)
|
||||
return NULL;
|
||||
@ -203,9 +200,9 @@ void kfd_release_kernel_doorbell(struct kfd_dev *kfd, u32 __iomem *db_addr)
|
||||
|
||||
inx = (unsigned int)(db_addr - kfd->doorbell_kernel_ptr);
|
||||
|
||||
mutex_lock(&doorbell_mutex);
|
||||
__clear_bit(inx, doorbell_available_index);
|
||||
mutex_unlock(&doorbell_mutex);
|
||||
mutex_lock(&kfd->doorbell_mutex);
|
||||
__clear_bit(inx, kfd->doorbell_available_index);
|
||||
mutex_unlock(&kfd->doorbell_mutex);
|
||||
}
|
||||
|
||||
inline void write_kernel_doorbell(u32 __iomem *db, u32 value)
|
||||
|
@ -34,7 +34,6 @@
|
||||
#define KFD_DRIVER_MINOR 7
|
||||
#define KFD_DRIVER_PATCHLEVEL 1
|
||||
|
||||
const struct kfd2kgd_calls *kfd2kgd;
|
||||
static const struct kgd2kfd_calls kgd2kfd = {
|
||||
.exit = kgd2kfd_exit,
|
||||
.probe = kgd2kfd_probe,
|
||||
@ -55,9 +54,7 @@ module_param(max_num_of_queues_per_device, int, 0444);
|
||||
MODULE_PARM_DESC(max_num_of_queues_per_device,
|
||||
"Maximum number of supported queues per device (1 = Minimum, 4096 = default)");
|
||||
|
||||
bool kgd2kfd_init(unsigned interface_version,
|
||||
const struct kfd2kgd_calls *f2g,
|
||||
const struct kgd2kfd_calls **g2f)
|
||||
bool kgd2kfd_init(unsigned interface_version, const struct kgd2kfd_calls **g2f)
|
||||
{
|
||||
/*
|
||||
* Only one interface version is supported,
|
||||
@ -66,11 +63,6 @@ bool kgd2kfd_init(unsigned interface_version,
|
||||
if (interface_version != KFD_INTERFACE_VERSION)
|
||||
return false;
|
||||
|
||||
/* Protection against multiple amd kgd loads */
|
||||
if (kfd2kgd)
|
||||
return true;
|
||||
|
||||
kfd2kgd = f2g;
|
||||
*g2f = &kgd2kfd;
|
||||
|
||||
return true;
|
||||
@ -85,8 +77,6 @@ static int __init kfd_module_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
kfd2kgd = NULL;
|
||||
|
||||
/* Verify module parameters */
|
||||
if ((sched_policy < KFD_SCHED_POLICY_HWS) ||
|
||||
(sched_policy > KFD_SCHED_POLICY_NO_HWS)) {
|
||||
|
@ -151,14 +151,15 @@ static void uninit_mqd_sdma(struct mqd_manager *mm, void *mqd,
|
||||
static int load_mqd(struct mqd_manager *mm, void *mqd, uint32_t pipe_id,
|
||||
uint32_t queue_id, uint32_t __user *wptr)
|
||||
{
|
||||
return kfd2kgd->hqd_load(mm->dev->kgd, mqd, pipe_id, queue_id, wptr);
|
||||
return mm->dev->kfd2kgd->hqd_load
|
||||
(mm->dev->kgd, mqd, pipe_id, queue_id, wptr);
|
||||
}
|
||||
|
||||
static int load_mqd_sdma(struct mqd_manager *mm, void *mqd,
|
||||
uint32_t pipe_id, uint32_t queue_id,
|
||||
uint32_t __user *wptr)
|
||||
{
|
||||
return kfd2kgd->hqd_sdma_load(mm->dev->kgd, mqd);
|
||||
return mm->dev->kfd2kgd->hqd_sdma_load(mm->dev->kgd, mqd);
|
||||
}
|
||||
|
||||
static int update_mqd(struct mqd_manager *mm, void *mqd,
|
||||
@ -245,7 +246,7 @@ static int destroy_mqd(struct mqd_manager *mm, void *mqd,
|
||||
unsigned int timeout, uint32_t pipe_id,
|
||||
uint32_t queue_id)
|
||||
{
|
||||
return kfd2kgd->hqd_destroy(mm->dev->kgd, type, timeout,
|
||||
return mm->dev->kfd2kgd->hqd_destroy(mm->dev->kgd, type, timeout,
|
||||
pipe_id, queue_id);
|
||||
}
|
||||
|
||||
@ -258,7 +259,7 @@ static int destroy_mqd_sdma(struct mqd_manager *mm, void *mqd,
|
||||
unsigned int timeout, uint32_t pipe_id,
|
||||
uint32_t queue_id)
|
||||
{
|
||||
return kfd2kgd->hqd_sdma_destroy(mm->dev->kgd, mqd, timeout);
|
||||
return mm->dev->kfd2kgd->hqd_sdma_destroy(mm->dev->kgd, mqd, timeout);
|
||||
}
|
||||
|
||||
static bool is_occupied(struct mqd_manager *mm, void *mqd,
|
||||
@ -266,7 +267,7 @@ static bool is_occupied(struct mqd_manager *mm, void *mqd,
|
||||
uint32_t queue_id)
|
||||
{
|
||||
|
||||
return kfd2kgd->hqd_is_occupied(mm->dev->kgd, queue_address,
|
||||
return mm->dev->kfd2kgd->hqd_is_occupied(mm->dev->kgd, queue_address,
|
||||
pipe_id, queue_id);
|
||||
|
||||
}
|
||||
@ -275,7 +276,7 @@ static bool is_occupied_sdma(struct mqd_manager *mm, void *mqd,
|
||||
uint64_t queue_address, uint32_t pipe_id,
|
||||
uint32_t queue_id)
|
||||
{
|
||||
return kfd2kgd->hqd_sdma_is_occupied(mm->dev->kgd, mqd);
|
||||
return mm->dev->kfd2kgd->hqd_sdma_is_occupied(mm->dev->kgd, mqd);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -148,6 +148,11 @@ struct kfd_dev {
|
||||
|
||||
struct kgd2kfd_shared_resources shared_resources;
|
||||
|
||||
const struct kfd2kgd_calls *kfd2kgd;
|
||||
struct mutex doorbell_mutex;
|
||||
unsigned long doorbell_available_index[DIV_ROUND_UP(
|
||||
KFD_MAX_NUM_OF_QUEUES_PER_PROCESS, BITS_PER_LONG)];
|
||||
|
||||
void *gtt_mem;
|
||||
uint64_t gtt_start_gpu_addr;
|
||||
void *gtt_start_cpu_ptr;
|
||||
@ -164,13 +169,12 @@ struct kfd_dev {
|
||||
|
||||
/* KGD2KFD callbacks */
|
||||
void kgd2kfd_exit(void);
|
||||
struct kfd_dev *kgd2kfd_probe(struct kgd_dev *kgd, struct pci_dev *pdev);
|
||||
struct kfd_dev *kgd2kfd_probe(struct kgd_dev *kgd,
|
||||
struct pci_dev *pdev, const struct kfd2kgd_calls *f2g);
|
||||
bool kgd2kfd_device_init(struct kfd_dev *kfd,
|
||||
const struct kgd2kfd_shared_resources *gpu_resources);
|
||||
const struct kgd2kfd_shared_resources *gpu_resources);
|
||||
void kgd2kfd_device_exit(struct kfd_dev *kfd);
|
||||
|
||||
extern const struct kfd2kgd_calls *kfd2kgd;
|
||||
|
||||
enum kfd_mempool {
|
||||
KFD_MEMPOOL_SYSTEM_CACHEABLE = 1,
|
||||
KFD_MEMPOOL_SYSTEM_WRITECOMBINE = 2,
|
||||
@ -378,8 +382,6 @@ struct qcm_process_device {
|
||||
/* The Device Queue Manager that owns this data */
|
||||
struct device_queue_manager *dqm;
|
||||
struct process_queue_manager *pqm;
|
||||
/* Device Queue Manager lock */
|
||||
struct mutex *lock;
|
||||
/* Queues list */
|
||||
struct list_head queues_list;
|
||||
struct list_head priv_queue_list;
|
||||
|
@ -162,10 +162,16 @@ static void kfd_process_wq_release(struct work_struct *work)
|
||||
|
||||
p = my_work->p;
|
||||
|
||||
pr_debug("Releasing process (pasid %d) in workqueue\n",
|
||||
p->pasid);
|
||||
|
||||
mutex_lock(&p->mutex);
|
||||
|
||||
list_for_each_entry_safe(pdd, temp, &p->per_device_data,
|
||||
per_device_list) {
|
||||
pr_debug("Releasing pdd (topology id %d) for process (pasid %d) in workqueue\n",
|
||||
pdd->dev->id, p->pasid);
|
||||
|
||||
amd_iommu_unbind_pasid(pdd->dev->pdev, p->pasid);
|
||||
list_del(&pdd->per_device_list);
|
||||
|
||||
|
@ -726,13 +726,14 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
|
||||
}
|
||||
|
||||
sysfs_show_32bit_prop(buffer, "max_engine_clk_fcompute",
|
||||
kfd2kgd->get_max_engine_clock_in_mhz(
|
||||
dev->gpu->kfd2kgd->get_max_engine_clock_in_mhz(
|
||||
dev->gpu->kgd));
|
||||
sysfs_show_64bit_prop(buffer, "local_mem_size",
|
||||
kfd2kgd->get_vmem_size(dev->gpu->kgd));
|
||||
dev->gpu->kfd2kgd->get_vmem_size(
|
||||
dev->gpu->kgd));
|
||||
|
||||
sysfs_show_32bit_prop(buffer, "fw_version",
|
||||
kfd2kgd->get_fw_version(
|
||||
dev->gpu->kfd2kgd->get_fw_version(
|
||||
dev->gpu->kgd,
|
||||
KGD_ENGINE_MEC1));
|
||||
}
|
||||
@ -1099,8 +1100,9 @@ static uint32_t kfd_generate_gpu_id(struct kfd_dev *gpu)
|
||||
buf[2] = gpu->pdev->subsystem_device;
|
||||
buf[3] = gpu->pdev->device;
|
||||
buf[4] = gpu->pdev->bus->number;
|
||||
buf[5] = (uint32_t)(kfd2kgd->get_vmem_size(gpu->kgd) & 0xffffffff);
|
||||
buf[6] = (uint32_t)(kfd2kgd->get_vmem_size(gpu->kgd) >> 32);
|
||||
buf[5] = (uint32_t)(gpu->kfd2kgd->get_vmem_size(gpu->kgd)
|
||||
& 0xffffffff);
|
||||
buf[6] = (uint32_t)(gpu->kfd2kgd->get_vmem_size(gpu->kgd) >> 32);
|
||||
|
||||
for (i = 0, hashout = 0; i < 7; i++)
|
||||
hashout ^= hash_32(buf[i], KFD_GPU_ID_HASH_WIDTH);
|
||||
|
@ -76,37 +76,6 @@ struct kgd2kfd_shared_resources {
|
||||
size_t doorbell_start_offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kgd2kfd_calls
|
||||
*
|
||||
* @exit: Notifies amdkfd that kgd module is unloaded
|
||||
*
|
||||
* @probe: Notifies amdkfd about a probe done on a device in the kgd driver.
|
||||
*
|
||||
* @device_init: Initialize the newly probed device (if it is a device that
|
||||
* amdkfd supports)
|
||||
*
|
||||
* @device_exit: Notifies amdkfd about a removal of a kgd device
|
||||
*
|
||||
* @suspend: Notifies amdkfd about a suspend action done to a kgd device
|
||||
*
|
||||
* @resume: Notifies amdkfd about a resume action done to a kgd device
|
||||
*
|
||||
* This structure contains function callback pointers so the kgd driver
|
||||
* will notify to the amdkfd about certain status changes.
|
||||
*
|
||||
*/
|
||||
struct kgd2kfd_calls {
|
||||
void (*exit)(void);
|
||||
struct kfd_dev* (*probe)(struct kgd_dev *kgd, struct pci_dev *pdev);
|
||||
bool (*device_init)(struct kfd_dev *kfd,
|
||||
const struct kgd2kfd_shared_resources *gpu_resources);
|
||||
void (*device_exit)(struct kfd_dev *kfd);
|
||||
void (*interrupt)(struct kfd_dev *kfd, const void *ih_ring_entry);
|
||||
void (*suspend)(struct kfd_dev *kfd);
|
||||
int (*resume)(struct kfd_dev *kfd);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kfd2kgd_calls
|
||||
*
|
||||
@ -196,8 +165,39 @@ struct kfd2kgd_calls {
|
||||
enum kgd_engine_type type);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kgd2kfd_calls
|
||||
*
|
||||
* @exit: Notifies amdkfd that kgd module is unloaded
|
||||
*
|
||||
* @probe: Notifies amdkfd about a probe done on a device in the kgd driver.
|
||||
*
|
||||
* @device_init: Initialize the newly probed device (if it is a device that
|
||||
* amdkfd supports)
|
||||
*
|
||||
* @device_exit: Notifies amdkfd about a removal of a kgd device
|
||||
*
|
||||
* @suspend: Notifies amdkfd about a suspend action done to a kgd device
|
||||
*
|
||||
* @resume: Notifies amdkfd about a resume action done to a kgd device
|
||||
*
|
||||
* This structure contains function callback pointers so the kgd driver
|
||||
* will notify to the amdkfd about certain status changes.
|
||||
*
|
||||
*/
|
||||
struct kgd2kfd_calls {
|
||||
void (*exit)(void);
|
||||
struct kfd_dev* (*probe)(struct kgd_dev *kgd, struct pci_dev *pdev,
|
||||
const struct kfd2kgd_calls *f2g);
|
||||
bool (*device_init)(struct kfd_dev *kfd,
|
||||
const struct kgd2kfd_shared_resources *gpu_resources);
|
||||
void (*device_exit)(struct kfd_dev *kfd);
|
||||
void (*interrupt)(struct kfd_dev *kfd, const void *ih_ring_entry);
|
||||
void (*suspend)(struct kfd_dev *kfd);
|
||||
int (*resume)(struct kfd_dev *kfd);
|
||||
};
|
||||
|
||||
bool kgd2kfd_init(unsigned interface_version,
|
||||
const struct kfd2kgd_calls *f2g,
|
||||
const struct kgd2kfd_calls **g2f);
|
||||
|
||||
#endif /* KGD_KFD_INTERFACE_H_INCLUDED */
|
||||
|
@ -9,7 +9,7 @@
|
||||
#define ARMADA_CONNETOR_H
|
||||
|
||||
#define encoder_helper_funcs(encoder) \
|
||||
((struct drm_encoder_helper_funcs *)encoder->helper_private)
|
||||
((const struct drm_encoder_helper_funcs *)encoder->helper_private)
|
||||
|
||||
struct armada_output_type {
|
||||
int connector_type;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
@ -37,14 +38,14 @@
|
||||
* @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
|
||||
* @event: pointer to the current page flip event
|
||||
* @id: CRTC id (returned by drm_crtc_index)
|
||||
* @dpms: DPMS mode
|
||||
* @enabled: CRTC state
|
||||
*/
|
||||
struct atmel_hlcdc_crtc {
|
||||
struct drm_crtc base;
|
||||
struct atmel_hlcdc_dc *dc;
|
||||
struct drm_pending_vblank_event *event;
|
||||
int id;
|
||||
int dpms;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static inline struct atmel_hlcdc_crtc *
|
||||
@ -53,86 +54,17 @@ drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
|
||||
return container_of(crtc, struct atmel_hlcdc_crtc, base);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_crtc_dpms(struct drm_crtc *c, int mode)
|
||||
{
|
||||
struct drm_device *dev = c->dev;
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
struct regmap *regmap = crtc->dc->hlcdc->regmap;
|
||||
unsigned int status;
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON)
|
||||
mode = DRM_MODE_DPMS_OFF;
|
||||
|
||||
if (crtc->dpms == mode)
|
||||
return;
|
||||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON) {
|
||||
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
(status & ATMEL_HLCDC_DISP))
|
||||
cpu_relax();
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
(status & ATMEL_HLCDC_SYNC))
|
||||
cpu_relax();
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
(status & ATMEL_HLCDC_PIXEL_CLK))
|
||||
cpu_relax();
|
||||
|
||||
clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
|
||||
|
||||
pm_runtime_allow(dev->dev);
|
||||
} else {
|
||||
pm_runtime_forbid(dev->dev);
|
||||
|
||||
clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
!(status & ATMEL_HLCDC_PIXEL_CLK))
|
||||
cpu_relax();
|
||||
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
!(status & ATMEL_HLCDC_SYNC))
|
||||
cpu_relax();
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
!(status & ATMEL_HLCDC_DISP))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
|
||||
crtc->dpms = mode;
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj,
|
||||
int x, int y,
|
||||
struct drm_framebuffer *old_fb)
|
||||
static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
|
||||
{
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
struct regmap *regmap = crtc->dc->hlcdc->regmap;
|
||||
struct drm_plane *plane = c->primary;
|
||||
struct drm_framebuffer *fb;
|
||||
struct drm_display_mode *adj = &c->state->adjusted_mode;
|
||||
unsigned long mode_rate;
|
||||
struct videomode vm;
|
||||
unsigned long prate;
|
||||
unsigned int cfg;
|
||||
int div;
|
||||
|
||||
if (atmel_hlcdc_dc_mode_valid(crtc->dc, adj) != MODE_OK)
|
||||
return -EINVAL;
|
||||
|
||||
vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay;
|
||||
vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end;
|
||||
vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start;
|
||||
@ -156,7 +88,7 @@ static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c,
|
||||
cfg = 0;
|
||||
|
||||
prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
|
||||
mode_rate = mode->crtc_clock * 1000;
|
||||
mode_rate = adj->crtc_clock * 1000;
|
||||
if ((prate / 2) < mode_rate) {
|
||||
prate *= 2;
|
||||
cfg |= ATMEL_HLCDC_CLKSEL;
|
||||
@ -174,10 +106,10 @@ static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c,
|
||||
|
||||
cfg = 0;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
if (adj->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
cfg |= ATMEL_HLCDC_VSPOL;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
if (adj->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
cfg |= ATMEL_HLCDC_HSPOL;
|
||||
|
||||
regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5),
|
||||
@ -187,44 +119,6 @@ static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c,
|
||||
ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
|
||||
ATMEL_HLCDC_GUARDTIME_MASK,
|
||||
cfg);
|
||||
|
||||
fb = plane->fb;
|
||||
plane->fb = old_fb;
|
||||
|
||||
return atmel_hlcdc_plane_update_with_mode(plane, c, fb, 0, 0,
|
||||
adj->hdisplay, adj->vdisplay,
|
||||
x << 16, y << 16,
|
||||
adj->hdisplay << 16,
|
||||
adj->vdisplay << 16,
|
||||
adj);
|
||||
}
|
||||
|
||||
int atmel_hlcdc_crtc_mode_set_base(struct drm_crtc *c, int x, int y,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct drm_plane *plane = c->primary;
|
||||
struct drm_framebuffer *fb = plane->fb;
|
||||
struct drm_display_mode *mode = &c->hwmode;
|
||||
|
||||
plane->fb = old_fb;
|
||||
|
||||
return plane->funcs->update_plane(plane, c, fb,
|
||||
0, 0,
|
||||
mode->hdisplay,
|
||||
mode->vdisplay,
|
||||
x << 16, y << 16,
|
||||
mode->hdisplay << 16,
|
||||
mode->vdisplay << 16);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_crtc_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_crtc_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
@ -234,30 +128,146 @@ static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_crtc_disable(struct drm_crtc *crtc)
|
||||
static void atmel_hlcdc_crtc_disable(struct drm_crtc *c)
|
||||
{
|
||||
struct drm_plane *plane;
|
||||
struct drm_device *dev = c->dev;
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
struct regmap *regmap = crtc->dc->hlcdc->regmap;
|
||||
unsigned int status;
|
||||
|
||||
atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
|
||||
crtc->primary->funcs->disable_plane(crtc->primary);
|
||||
if (!crtc->enabled)
|
||||
return;
|
||||
|
||||
drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
|
||||
if (plane->crtc != crtc)
|
||||
continue;
|
||||
drm_crtc_vblank_off(c);
|
||||
|
||||
plane->funcs->disable_plane(crtc->primary);
|
||||
plane->crtc = NULL;
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
(status & ATMEL_HLCDC_DISP))
|
||||
cpu_relax();
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
(status & ATMEL_HLCDC_SYNC))
|
||||
cpu_relax();
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
(status & ATMEL_HLCDC_PIXEL_CLK))
|
||||
cpu_relax();
|
||||
|
||||
clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
|
||||
pinctrl_pm_select_sleep_state(dev->dev);
|
||||
|
||||
pm_runtime_allow(dev->dev);
|
||||
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
|
||||
crtc->enabled = false;
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_crtc_enable(struct drm_crtc *c)
|
||||
{
|
||||
struct drm_device *dev = c->dev;
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
struct regmap *regmap = crtc->dc->hlcdc->regmap;
|
||||
unsigned int status;
|
||||
|
||||
if (crtc->enabled)
|
||||
return;
|
||||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
pm_runtime_forbid(dev->dev);
|
||||
|
||||
pinctrl_pm_select_default_state(dev->dev);
|
||||
clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
!(status & ATMEL_HLCDC_PIXEL_CLK))
|
||||
cpu_relax();
|
||||
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
!(status & ATMEL_HLCDC_SYNC))
|
||||
cpu_relax();
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
|
||||
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
|
||||
!(status & ATMEL_HLCDC_DISP))
|
||||
cpu_relax();
|
||||
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
|
||||
drm_crtc_vblank_on(c);
|
||||
|
||||
crtc->enabled = true;
|
||||
}
|
||||
|
||||
void atmel_hlcdc_crtc_suspend(struct drm_crtc *c)
|
||||
{
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
|
||||
if (crtc->enabled) {
|
||||
atmel_hlcdc_crtc_disable(c);
|
||||
/* save enable state for resume */
|
||||
crtc->enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void atmel_hlcdc_crtc_resume(struct drm_crtc *c)
|
||||
{
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
|
||||
if (crtc->enabled) {
|
||||
crtc->enabled = false;
|
||||
atmel_hlcdc_crtc_enable(c);
|
||||
}
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c,
|
||||
struct drm_crtc_state *s)
|
||||
{
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
|
||||
if (atmel_hlcdc_dc_mode_valid(crtc->dc, &s->adjusted_mode) != MODE_OK)
|
||||
return -EINVAL;
|
||||
|
||||
return atmel_hlcdc_plane_prepare_disc_area(s);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c)
|
||||
{
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
|
||||
if (c->state->event) {
|
||||
c->state->event->pipe = drm_crtc_index(c);
|
||||
|
||||
WARN_ON(drm_crtc_vblank_get(c) != 0);
|
||||
|
||||
crtc->event = c->state->event;
|
||||
c->state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc)
|
||||
{
|
||||
/* TODO: write common plane control register if available */
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
|
||||
.mode_fixup = atmel_hlcdc_crtc_mode_fixup,
|
||||
.dpms = atmel_hlcdc_crtc_dpms,
|
||||
.mode_set = atmel_hlcdc_crtc_mode_set,
|
||||
.mode_set_base = atmel_hlcdc_crtc_mode_set_base,
|
||||
.prepare = atmel_hlcdc_crtc_prepare,
|
||||
.commit = atmel_hlcdc_crtc_commit,
|
||||
.mode_set = drm_helper_crtc_mode_set,
|
||||
.mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb,
|
||||
.mode_set_base = drm_helper_crtc_mode_set_base,
|
||||
.disable = atmel_hlcdc_crtc_disable,
|
||||
.enable = atmel_hlcdc_crtc_enable,
|
||||
.atomic_check = atmel_hlcdc_crtc_atomic_check,
|
||||
.atomic_begin = atmel_hlcdc_crtc_atomic_begin,
|
||||
.atomic_flush = atmel_hlcdc_crtc_atomic_flush,
|
||||
};
|
||||
|
||||
static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c)
|
||||
@ -306,61 +316,13 @@ void atmel_hlcdc_crtc_irq(struct drm_crtc *c)
|
||||
atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c));
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_crtc_page_flip(struct drm_crtc *c,
|
||||
struct drm_framebuffer *fb,
|
||||
struct drm_pending_vblank_event *event,
|
||||
uint32_t page_flip_flags)
|
||||
{
|
||||
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
|
||||
struct atmel_hlcdc_plane_update_req req;
|
||||
struct drm_plane *plane = c->primary;
|
||||
struct drm_device *dev = c->dev;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
if (crtc->event)
|
||||
ret = -EBUSY;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.crtc_x = 0;
|
||||
req.crtc_y = 0;
|
||||
req.crtc_h = c->mode.crtc_vdisplay;
|
||||
req.crtc_w = c->mode.crtc_hdisplay;
|
||||
req.src_x = c->x << 16;
|
||||
req.src_y = c->y << 16;
|
||||
req.src_w = req.crtc_w << 16;
|
||||
req.src_h = req.crtc_h << 16;
|
||||
req.fb = fb;
|
||||
|
||||
ret = atmel_hlcdc_plane_prepare_update_req(plane, &req, &c->hwmode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (event) {
|
||||
drm_vblank_get(c->dev, crtc->id);
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
crtc->event = event;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
ret = atmel_hlcdc_plane_apply_update_req(plane, &req);
|
||||
if (ret)
|
||||
crtc->event = NULL;
|
||||
else
|
||||
plane->fb = fb;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
|
||||
.page_flip = atmel_hlcdc_crtc_page_flip,
|
||||
.set_config = drm_crtc_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.destroy = atmel_hlcdc_crtc_destroy,
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
};
|
||||
|
||||
int atmel_hlcdc_crtc_create(struct drm_device *dev)
|
||||
@ -375,7 +337,6 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev)
|
||||
if (!crtc)
|
||||
return -ENOMEM;
|
||||
|
||||
crtc->dpms = DRM_MODE_DPMS_OFF;
|
||||
crtc->dc = dc;
|
||||
|
||||
ret = drm_crtc_init_with_planes(dev, &crtc->base,
|
||||
|
@ -222,6 +222,8 @@ static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
|
||||
static const struct drm_mode_config_funcs mode_config_funcs = {
|
||||
.fb_create = atmel_hlcdc_fb_create,
|
||||
.output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
|
||||
@ -317,6 +319,8 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
|
||||
goto err_periph_clk_disable;
|
||||
}
|
||||
|
||||
drm_mode_config_reset(dev);
|
||||
|
||||
ret = drm_vblank_init(dev, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "failed to initialize vblank\n");
|
||||
@ -555,6 +559,41 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int atmel_hlcdc_dc_drm_suspend(struct device *dev)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(dev);
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
|
||||
drm_modeset_lock_all(drm_dev);
|
||||
list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head)
|
||||
atmel_hlcdc_crtc_suspend(crtc);
|
||||
drm_modeset_unlock_all(drm_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_dc_drm_resume(struct device *dev)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(dev);
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
|
||||
drm_modeset_lock_all(drm_dev);
|
||||
list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head)
|
||||
atmel_hlcdc_crtc_resume(crtc);
|
||||
drm_modeset_unlock_all(drm_dev);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops,
|
||||
atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume);
|
||||
|
||||
static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
|
||||
{ .compatible = "atmel,hlcdc-display-controller" },
|
||||
{ },
|
||||
@ -565,6 +604,7 @@ static struct platform_driver atmel_hlcdc_dc_platform_driver = {
|
||||
.remove = atmel_hlcdc_dc_drm_remove,
|
||||
.driver = {
|
||||
.name = "atmel-hlcdc-display-controller",
|
||||
.pm = &atmel_hlcdc_dc_drm_pm_ops,
|
||||
.of_match_table = atmel_hlcdc_dc_of_match,
|
||||
},
|
||||
};
|
||||
|
@ -26,11 +26,14 @@
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "atmel_hlcdc_layer.h"
|
||||
@ -69,7 +72,6 @@ struct atmel_hlcdc_dc_desc {
|
||||
*/
|
||||
struct atmel_hlcdc_plane_properties {
|
||||
struct drm_property *alpha;
|
||||
struct drm_property *rotation;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -84,7 +86,6 @@ struct atmel_hlcdc_plane {
|
||||
struct drm_plane base;
|
||||
struct atmel_hlcdc_layer layer;
|
||||
struct atmel_hlcdc_plane_properties *properties;
|
||||
unsigned int rotation;
|
||||
};
|
||||
|
||||
static inline struct atmel_hlcdc_plane *
|
||||
@ -99,43 +100,6 @@ atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l)
|
||||
return container_of(l, struct atmel_hlcdc_plane, layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atmel HLCDC Plane update request structure.
|
||||
*
|
||||
* @crtc_x: x position of the plane relative to the CRTC
|
||||
* @crtc_y: y position of the plane relative to the CRTC
|
||||
* @crtc_w: visible width of the plane
|
||||
* @crtc_h: visible height of the plane
|
||||
* @src_x: x buffer position
|
||||
* @src_y: y buffer position
|
||||
* @src_w: buffer width
|
||||
* @src_h: buffer height
|
||||
* @fb: framebuffer object object
|
||||
* @bpp: bytes per pixel deduced from pixel_format
|
||||
* @offsets: offsets to apply to the GEM buffers
|
||||
* @xstride: value to add to the pixel pointer between each line
|
||||
* @pstride: value to add to the pixel pointer between each pixel
|
||||
* @nplanes: number of planes (deduced from pixel_format)
|
||||
*/
|
||||
struct atmel_hlcdc_plane_update_req {
|
||||
int crtc_x;
|
||||
int crtc_y;
|
||||
unsigned int crtc_w;
|
||||
unsigned int crtc_h;
|
||||
uint32_t src_x;
|
||||
uint32_t src_y;
|
||||
uint32_t src_w;
|
||||
uint32_t src_h;
|
||||
struct drm_framebuffer *fb;
|
||||
|
||||
/* These fields are private and should not be touched */
|
||||
int bpp[ATMEL_HLCDC_MAX_PLANES];
|
||||
unsigned int offsets[ATMEL_HLCDC_MAX_PLANES];
|
||||
int xstride[ATMEL_HLCDC_MAX_PLANES];
|
||||
int pstride[ATMEL_HLCDC_MAX_PLANES];
|
||||
int nplanes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Atmel HLCDC Planes.
|
||||
*
|
||||
@ -184,28 +148,16 @@ int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
|
||||
struct atmel_hlcdc_planes *
|
||||
atmel_hlcdc_create_planes(struct drm_device *dev);
|
||||
|
||||
int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
|
||||
struct atmel_hlcdc_plane_update_req *req,
|
||||
const struct drm_display_mode *mode);
|
||||
|
||||
int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
|
||||
struct atmel_hlcdc_plane_update_req *req);
|
||||
|
||||
int atmel_hlcdc_plane_update_with_mode(struct drm_plane *p,
|
||||
struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb,
|
||||
int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w,
|
||||
unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h,
|
||||
const struct drm_display_mode *mode);
|
||||
int atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state);
|
||||
|
||||
void atmel_hlcdc_crtc_irq(struct drm_crtc *c);
|
||||
|
||||
void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *crtc,
|
||||
struct drm_file *file);
|
||||
|
||||
void atmel_hlcdc_crtc_suspend(struct drm_crtc *crtc);
|
||||
void atmel_hlcdc_crtc_resume(struct drm_crtc *crtc);
|
||||
|
||||
int atmel_hlcdc_crtc_create(struct drm_device *dev);
|
||||
|
||||
int atmel_hlcdc_create_outputs(struct drm_device *dev);
|
||||
|
@ -298,7 +298,7 @@ void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
|
||||
spin_unlock_irqrestore(&layer->lock, flags);
|
||||
}
|
||||
|
||||
int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
|
||||
void atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
|
||||
{
|
||||
struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
|
||||
struct atmel_hlcdc_layer_update *upd = &layer->update;
|
||||
@ -341,8 +341,6 @@ int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
|
||||
dma->status = ATMEL_HLCDC_LAYER_DISABLED;
|
||||
|
||||
spin_unlock_irqrestore(&layer->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer)
|
||||
|
@ -120,6 +120,7 @@
|
||||
#define ATMEL_HLCDC_LAYER_DISCEN BIT(11)
|
||||
#define ATMEL_HLCDC_LAYER_GA_SHIFT 16
|
||||
#define ATMEL_HLCDC_LAYER_GA_MASK GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT)
|
||||
#define ATMEL_HLCDC_LAYER_GA(x) ((x) << ATMEL_HLCDC_LAYER_GA_SHIFT)
|
||||
|
||||
#define ATMEL_HLCDC_LAYER_CSC_CFG(p, o) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.csc + o)
|
||||
|
||||
@ -376,7 +377,7 @@ int atmel_hlcdc_layer_init(struct drm_device *dev,
|
||||
void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
|
||||
struct atmel_hlcdc_layer *layer);
|
||||
|
||||
int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
|
||||
void atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
|
||||
|
||||
int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer);
|
||||
|
||||
|
@ -86,25 +86,22 @@ atmel_hlcdc_rgb_output_to_panel(struct atmel_hlcdc_rgb_output *output)
|
||||
return container_of(output, struct atmel_hlcdc_panel, base);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_panel_encoder_dpms(struct drm_encoder *encoder,
|
||||
int mode)
|
||||
static void atmel_hlcdc_panel_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct atmel_hlcdc_rgb_output *rgb =
|
||||
drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
|
||||
struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON)
|
||||
mode = DRM_MODE_DPMS_OFF;
|
||||
drm_panel_enable(panel->panel);
|
||||
}
|
||||
|
||||
if (mode == rgb->dpms)
|
||||
return;
|
||||
static void atmel_hlcdc_panel_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct atmel_hlcdc_rgb_output *rgb =
|
||||
drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
|
||||
struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON)
|
||||
drm_panel_disable(panel->panel);
|
||||
else
|
||||
drm_panel_enable(panel->panel);
|
||||
|
||||
rgb->dpms = mode;
|
||||
drm_panel_disable(panel->panel);
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -115,16 +112,6 @@ atmel_hlcdc_panel_encoder_mode_fixup(struct drm_encoder *encoder,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_panel_encoder_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
atmel_hlcdc_panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_panel_encoder_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
atmel_hlcdc_panel_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
static void
|
||||
atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
@ -156,11 +143,10 @@ atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder,
|
||||
}
|
||||
|
||||
static struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = {
|
||||
.dpms = atmel_hlcdc_panel_encoder_dpms,
|
||||
.mode_fixup = atmel_hlcdc_panel_encoder_mode_fixup,
|
||||
.prepare = atmel_hlcdc_panel_encoder_prepare,
|
||||
.commit = atmel_hlcdc_panel_encoder_commit,
|
||||
.mode_set = atmel_hlcdc_rgb_encoder_mode_set,
|
||||
.disable = atmel_hlcdc_panel_encoder_disable,
|
||||
.enable = atmel_hlcdc_panel_encoder_enable,
|
||||
};
|
||||
|
||||
static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder)
|
||||
@ -226,10 +212,13 @@ atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector)
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.detect = atmel_hlcdc_panel_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = atmel_hlcdc_panel_connector_destroy,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static int atmel_hlcdc_create_panel_output(struct drm_device *dev,
|
||||
|
@ -19,6 +19,59 @@
|
||||
|
||||
#include "atmel_hlcdc_dc.h"
|
||||
|
||||
/**
|
||||
* Atmel HLCDC Plane state structure.
|
||||
*
|
||||
* @base: DRM plane state
|
||||
* @crtc_x: x position of the plane relative to the CRTC
|
||||
* @crtc_y: y position of the plane relative to the CRTC
|
||||
* @crtc_w: visible width of the plane
|
||||
* @crtc_h: visible height of the plane
|
||||
* @src_x: x buffer position
|
||||
* @src_y: y buffer position
|
||||
* @src_w: buffer width
|
||||
* @src_h: buffer height
|
||||
* @alpha: alpha blending of the plane
|
||||
* @bpp: bytes per pixel deduced from pixel_format
|
||||
* @offsets: offsets to apply to the GEM buffers
|
||||
* @xstride: value to add to the pixel pointer between each line
|
||||
* @pstride: value to add to the pixel pointer between each pixel
|
||||
* @nplanes: number of planes (deduced from pixel_format)
|
||||
*/
|
||||
struct atmel_hlcdc_plane_state {
|
||||
struct drm_plane_state base;
|
||||
int crtc_x;
|
||||
int crtc_y;
|
||||
unsigned int crtc_w;
|
||||
unsigned int crtc_h;
|
||||
uint32_t src_x;
|
||||
uint32_t src_y;
|
||||
uint32_t src_w;
|
||||
uint32_t src_h;
|
||||
|
||||
u8 alpha;
|
||||
|
||||
bool disc_updated;
|
||||
|
||||
int disc_x;
|
||||
int disc_y;
|
||||
int disc_w;
|
||||
int disc_h;
|
||||
|
||||
/* These fields are private and should not be touched */
|
||||
int bpp[ATMEL_HLCDC_MAX_PLANES];
|
||||
unsigned int offsets[ATMEL_HLCDC_MAX_PLANES];
|
||||
int xstride[ATMEL_HLCDC_MAX_PLANES];
|
||||
int pstride[ATMEL_HLCDC_MAX_PLANES];
|
||||
int nplanes;
|
||||
};
|
||||
|
||||
static inline struct atmel_hlcdc_plane_state *
|
||||
drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)
|
||||
{
|
||||
return container_of(s, struct atmel_hlcdc_plane_state, base);
|
||||
}
|
||||
|
||||
#define SUBPIXEL_MASK 0xffff
|
||||
|
||||
static uint32_t rgb_formats[] = {
|
||||
@ -128,7 +181,7 @@ static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool atmel_hlcdc_format_embedds_alpha(u32 format)
|
||||
static bool atmel_hlcdc_format_embeds_alpha(u32 format)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -204,7 +257,7 @@ static u32 heo_upscaling_ycoef[] = {
|
||||
|
||||
static void
|
||||
atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_plane_update_req *req)
|
||||
struct atmel_hlcdc_plane_state *state)
|
||||
{
|
||||
const struct atmel_hlcdc_layer_cfg_layout *layout =
|
||||
&plane->layer.desc->layout;
|
||||
@ -213,69 +266,69 @@ atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
layout->size,
|
||||
0xffffffff,
|
||||
(req->crtc_w - 1) |
|
||||
((req->crtc_h - 1) << 16));
|
||||
(state->crtc_w - 1) |
|
||||
((state->crtc_h - 1) << 16));
|
||||
|
||||
if (layout->memsize)
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
layout->memsize,
|
||||
0xffffffff,
|
||||
(req->src_w - 1) |
|
||||
((req->src_h - 1) << 16));
|
||||
(state->src_w - 1) |
|
||||
((state->src_h - 1) << 16));
|
||||
|
||||
if (layout->pos)
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
layout->pos,
|
||||
0xffffffff,
|
||||
req->crtc_x |
|
||||
(req->crtc_y << 16));
|
||||
state->crtc_x |
|
||||
(state->crtc_y << 16));
|
||||
|
||||
/* TODO: rework the rescaling part */
|
||||
if (req->crtc_w != req->src_w || req->crtc_h != req->src_h) {
|
||||
if (state->crtc_w != state->src_w || state->crtc_h != state->src_h) {
|
||||
u32 factor_reg = 0;
|
||||
|
||||
if (req->crtc_w != req->src_w) {
|
||||
if (state->crtc_w != state->src_w) {
|
||||
int i;
|
||||
u32 factor;
|
||||
u32 *coeff_tab = heo_upscaling_xcoef;
|
||||
u32 max_memsize;
|
||||
|
||||
if (req->crtc_w < req->src_w)
|
||||
if (state->crtc_w < state->src_w)
|
||||
coeff_tab = heo_downscaling_xcoef;
|
||||
for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
17 + i,
|
||||
0xffffffff,
|
||||
coeff_tab[i]);
|
||||
factor = ((8 * 256 * req->src_w) - (256 * 4)) /
|
||||
req->crtc_w;
|
||||
factor = ((8 * 256 * state->src_w) - (256 * 4)) /
|
||||
state->crtc_w;
|
||||
factor++;
|
||||
max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
|
||||
max_memsize = ((factor * state->crtc_w) + (256 * 4)) /
|
||||
2048;
|
||||
if (max_memsize > req->src_w)
|
||||
if (max_memsize > state->src_w)
|
||||
factor--;
|
||||
factor_reg |= factor | 0x80000000;
|
||||
}
|
||||
|
||||
if (req->crtc_h != req->src_h) {
|
||||
if (state->crtc_h != state->src_h) {
|
||||
int i;
|
||||
u32 factor;
|
||||
u32 *coeff_tab = heo_upscaling_ycoef;
|
||||
u32 max_memsize;
|
||||
|
||||
if (req->crtc_w < req->src_w)
|
||||
if (state->crtc_w < state->src_w)
|
||||
coeff_tab = heo_downscaling_ycoef;
|
||||
for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
33 + i,
|
||||
0xffffffff,
|
||||
coeff_tab[i]);
|
||||
factor = ((8 * 256 * req->src_w) - (256 * 4)) /
|
||||
req->crtc_w;
|
||||
factor = ((8 * 256 * state->src_w) - (256 * 4)) /
|
||||
state->crtc_w;
|
||||
factor++;
|
||||
max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
|
||||
max_memsize = ((factor * state->crtc_w) + (256 * 4)) /
|
||||
2048;
|
||||
if (max_memsize > req->src_w)
|
||||
if (max_memsize > state->src_w)
|
||||
factor--;
|
||||
factor_reg |= (factor << 16) | 0x80000000;
|
||||
}
|
||||
@ -287,7 +340,7 @@ atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
|
||||
|
||||
static void
|
||||
atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_plane_update_req *req)
|
||||
struct atmel_hlcdc_plane_state *state)
|
||||
{
|
||||
const struct atmel_hlcdc_layer_cfg_layout *layout =
|
||||
&plane->layer.desc->layout;
|
||||
@ -297,10 +350,11 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
|
||||
cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
|
||||
ATMEL_HLCDC_LAYER_ITER;
|
||||
|
||||
if (atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format))
|
||||
if (atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format))
|
||||
cfg |= ATMEL_HLCDC_LAYER_LAEN;
|
||||
else
|
||||
cfg |= ATMEL_HLCDC_LAYER_GAEN;
|
||||
cfg |= ATMEL_HLCDC_LAYER_GAEN |
|
||||
ATMEL_HLCDC_LAYER_GA(state->alpha);
|
||||
}
|
||||
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
@ -312,24 +366,26 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
|
||||
ATMEL_HLCDC_LAYER_ITER2BL |
|
||||
ATMEL_HLCDC_LAYER_ITER |
|
||||
ATMEL_HLCDC_LAYER_GAEN |
|
||||
ATMEL_HLCDC_LAYER_GA_MASK |
|
||||
ATMEL_HLCDC_LAYER_LAEN |
|
||||
ATMEL_HLCDC_LAYER_OVR |
|
||||
ATMEL_HLCDC_LAYER_DMA, cfg);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_plane_update_req *req)
|
||||
struct atmel_hlcdc_plane_state *state)
|
||||
{
|
||||
u32 cfg;
|
||||
int ret;
|
||||
|
||||
ret = atmel_hlcdc_format_to_plane_mode(req->fb->pixel_format, &cfg);
|
||||
ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->pixel_format,
|
||||
&cfg);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
if ((req->fb->pixel_format == DRM_FORMAT_YUV422 ||
|
||||
req->fb->pixel_format == DRM_FORMAT_NV61) &&
|
||||
(plane->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))))
|
||||
if ((state->base.fb->pixel_format == DRM_FORMAT_YUV422 ||
|
||||
state->base.fb->pixel_format == DRM_FORMAT_NV61) &&
|
||||
(state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))))
|
||||
cfg |= ATMEL_HLCDC_YUV422ROT;
|
||||
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
@ -341,7 +397,7 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
|
||||
* Rotation optimization is not working on RGB888 (rotation is still
|
||||
* working but without any optimization).
|
||||
*/
|
||||
if (req->fb->pixel_format == DRM_FORMAT_RGB888)
|
||||
if (state->base.fb->pixel_format == DRM_FORMAT_RGB888)
|
||||
cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS;
|
||||
else
|
||||
cfg = 0;
|
||||
@ -352,73 +408,142 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_plane_update_req *req)
|
||||
struct atmel_hlcdc_plane_state *state)
|
||||
{
|
||||
struct atmel_hlcdc_layer *layer = &plane->layer;
|
||||
const struct atmel_hlcdc_layer_cfg_layout *layout =
|
||||
&layer->desc->layout;
|
||||
int i;
|
||||
|
||||
atmel_hlcdc_layer_update_set_fb(&plane->layer, req->fb, req->offsets);
|
||||
atmel_hlcdc_layer_update_set_fb(&plane->layer, state->base.fb,
|
||||
state->offsets);
|
||||
|
||||
for (i = 0; i < req->nplanes; i++) {
|
||||
for (i = 0; i < state->nplanes; i++) {
|
||||
if (layout->xstride[i]) {
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
layout->xstride[i],
|
||||
0xffffffff,
|
||||
req->xstride[i]);
|
||||
state->xstride[i]);
|
||||
}
|
||||
|
||||
if (layout->pstride[i]) {
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
layout->pstride[i],
|
||||
0xffffffff,
|
||||
req->pstride[i]);
|
||||
state->pstride[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_check_update_req(struct drm_plane *p,
|
||||
struct atmel_hlcdc_plane_update_req *req,
|
||||
const struct drm_display_mode *mode)
|
||||
int
|
||||
atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
const struct atmel_hlcdc_layer_cfg_layout *layout =
|
||||
&plane->layer.desc->layout;
|
||||
int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0;
|
||||
const struct atmel_hlcdc_layer_cfg_layout *layout;
|
||||
struct atmel_hlcdc_plane_state *primary_state;
|
||||
struct drm_plane_state *primary_s;
|
||||
struct atmel_hlcdc_plane *primary;
|
||||
struct drm_plane *ovl;
|
||||
|
||||
if (!layout->size &&
|
||||
(mode->hdisplay != req->crtc_w ||
|
||||
mode->vdisplay != req->crtc_h))
|
||||
return -EINVAL;
|
||||
primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);
|
||||
layout = &primary->layer.desc->layout;
|
||||
if (!layout->disc_pos || !layout->disc_size)
|
||||
return 0;
|
||||
|
||||
if (plane->layer.desc->max_height &&
|
||||
req->crtc_h > plane->layer.desc->max_height)
|
||||
return -EINVAL;
|
||||
primary_s = drm_atomic_get_plane_state(c_state->state,
|
||||
&primary->base);
|
||||
if (IS_ERR(primary_s))
|
||||
return PTR_ERR(primary_s);
|
||||
|
||||
if (plane->layer.desc->max_width &&
|
||||
req->crtc_w > plane->layer.desc->max_width)
|
||||
return -EINVAL;
|
||||
primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s);
|
||||
|
||||
if ((req->crtc_h != req->src_h || req->crtc_w != req->src_w) &&
|
||||
(!layout->memsize ||
|
||||
atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format)))
|
||||
return -EINVAL;
|
||||
drm_atomic_crtc_state_for_each_plane(ovl, c_state) {
|
||||
struct atmel_hlcdc_plane_state *ovl_state;
|
||||
struct drm_plane_state *ovl_s;
|
||||
|
||||
if (req->crtc_x < 0 || req->crtc_y < 0)
|
||||
return -EINVAL;
|
||||
if (ovl == c_state->crtc->primary)
|
||||
continue;
|
||||
|
||||
if (req->crtc_w + req->crtc_x > mode->hdisplay ||
|
||||
req->crtc_h + req->crtc_y > mode->vdisplay)
|
||||
return -EINVAL;
|
||||
ovl_s = drm_atomic_get_plane_state(c_state->state, ovl);
|
||||
if (IS_ERR(ovl_s))
|
||||
return PTR_ERR(ovl_s);
|
||||
|
||||
ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);
|
||||
|
||||
if (!ovl_s->fb ||
|
||||
atmel_hlcdc_format_embeds_alpha(ovl_s->fb->pixel_format) ||
|
||||
ovl_state->alpha != 255)
|
||||
continue;
|
||||
|
||||
/* TODO: implement a smarter hidden area detection */
|
||||
if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w)
|
||||
continue;
|
||||
|
||||
disc_x = ovl_state->crtc_x;
|
||||
disc_y = ovl_state->crtc_y;
|
||||
disc_h = ovl_state->crtc_h;
|
||||
disc_w = ovl_state->crtc_w;
|
||||
}
|
||||
|
||||
if (disc_x == primary_state->disc_x &&
|
||||
disc_y == primary_state->disc_y &&
|
||||
disc_w == primary_state->disc_w &&
|
||||
disc_h == primary_state->disc_h)
|
||||
return 0;
|
||||
|
||||
|
||||
primary_state->disc_x = disc_x;
|
||||
primary_state->disc_y = disc_y;
|
||||
primary_state->disc_w = disc_w;
|
||||
primary_state->disc_h = disc_h;
|
||||
primary_state->disc_updated = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
|
||||
struct atmel_hlcdc_plane_update_req *req,
|
||||
const struct drm_display_mode *mode)
|
||||
static void
|
||||
atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_plane_state *state)
|
||||
{
|
||||
const struct atmel_hlcdc_layer_cfg_layout *layout =
|
||||
&plane->layer.desc->layout;
|
||||
int disc_surface = 0;
|
||||
|
||||
if (!state->disc_updated)
|
||||
return;
|
||||
|
||||
disc_surface = state->disc_h * state->disc_w;
|
||||
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
|
||||
ATMEL_HLCDC_LAYER_DISCEN,
|
||||
disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0);
|
||||
|
||||
if (!disc_surface)
|
||||
return;
|
||||
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
layout->disc_pos,
|
||||
0xffffffff,
|
||||
state->disc_x | (state->disc_y << 16));
|
||||
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
layout->disc_size,
|
||||
0xffffffff,
|
||||
(state->disc_w - 1) |
|
||||
((state->disc_h - 1) << 16));
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
|
||||
struct drm_plane_state *s)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
struct atmel_hlcdc_plane_state *state =
|
||||
drm_plane_state_to_atmel_hlcdc_plane_state(s);
|
||||
const struct atmel_hlcdc_layer_cfg_layout *layout =
|
||||
&plane->layer.desc->layout;
|
||||
struct drm_framebuffer *fb = state->base.fb;
|
||||
const struct drm_display_mode *mode;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
unsigned int patched_crtc_w;
|
||||
unsigned int patched_crtc_h;
|
||||
unsigned int patched_src_w;
|
||||
@ -430,196 +555,196 @@ int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
|
||||
int vsub = 1;
|
||||
int i;
|
||||
|
||||
if ((req->src_x | req->src_y | req->src_w | req->src_h) &
|
||||
if (!state->base.crtc || !fb)
|
||||
return 0;
|
||||
|
||||
crtc_state = s->state->crtc_states[drm_crtc_index(s->crtc)];
|
||||
mode = &crtc_state->adjusted_mode;
|
||||
|
||||
state->src_x = s->src_x;
|
||||
state->src_y = s->src_y;
|
||||
state->src_h = s->src_h;
|
||||
state->src_w = s->src_w;
|
||||
state->crtc_x = s->crtc_x;
|
||||
state->crtc_y = s->crtc_y;
|
||||
state->crtc_h = s->crtc_h;
|
||||
state->crtc_w = s->crtc_w;
|
||||
if ((state->src_x | state->src_y | state->src_w | state->src_h) &
|
||||
SUBPIXEL_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
req->src_x >>= 16;
|
||||
req->src_y >>= 16;
|
||||
req->src_w >>= 16;
|
||||
req->src_h >>= 16;
|
||||
state->src_x >>= 16;
|
||||
state->src_y >>= 16;
|
||||
state->src_w >>= 16;
|
||||
state->src_h >>= 16;
|
||||
|
||||
req->nplanes = drm_format_num_planes(req->fb->pixel_format);
|
||||
if (req->nplanes > ATMEL_HLCDC_MAX_PLANES)
|
||||
state->nplanes = drm_format_num_planes(fb->pixel_format);
|
||||
if (state->nplanes > ATMEL_HLCDC_MAX_PLANES)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Swap width and size in case of 90 or 270 degrees rotation
|
||||
*/
|
||||
if (plane->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) {
|
||||
tmp = req->crtc_w;
|
||||
req->crtc_w = req->crtc_h;
|
||||
req->crtc_h = tmp;
|
||||
tmp = req->src_w;
|
||||
req->src_w = req->src_h;
|
||||
req->src_h = tmp;
|
||||
if (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) {
|
||||
tmp = state->crtc_w;
|
||||
state->crtc_w = state->crtc_h;
|
||||
state->crtc_h = tmp;
|
||||
tmp = state->src_w;
|
||||
state->src_w = state->src_h;
|
||||
state->src_h = tmp;
|
||||
}
|
||||
|
||||
if (req->crtc_x + req->crtc_w > mode->hdisplay)
|
||||
patched_crtc_w = mode->hdisplay - req->crtc_x;
|
||||
if (state->crtc_x + state->crtc_w > mode->hdisplay)
|
||||
patched_crtc_w = mode->hdisplay - state->crtc_x;
|
||||
else
|
||||
patched_crtc_w = req->crtc_w;
|
||||
patched_crtc_w = state->crtc_w;
|
||||
|
||||
if (req->crtc_x < 0) {
|
||||
patched_crtc_w += req->crtc_x;
|
||||
x_offset = -req->crtc_x;
|
||||
req->crtc_x = 0;
|
||||
if (state->crtc_x < 0) {
|
||||
patched_crtc_w += state->crtc_x;
|
||||
x_offset = -state->crtc_x;
|
||||
state->crtc_x = 0;
|
||||
}
|
||||
|
||||
if (req->crtc_y + req->crtc_h > mode->vdisplay)
|
||||
patched_crtc_h = mode->vdisplay - req->crtc_y;
|
||||
if (state->crtc_y + state->crtc_h > mode->vdisplay)
|
||||
patched_crtc_h = mode->vdisplay - state->crtc_y;
|
||||
else
|
||||
patched_crtc_h = req->crtc_h;
|
||||
patched_crtc_h = state->crtc_h;
|
||||
|
||||
if (req->crtc_y < 0) {
|
||||
patched_crtc_h += req->crtc_y;
|
||||
y_offset = -req->crtc_y;
|
||||
req->crtc_y = 0;
|
||||
if (state->crtc_y < 0) {
|
||||
patched_crtc_h += state->crtc_y;
|
||||
y_offset = -state->crtc_y;
|
||||
state->crtc_y = 0;
|
||||
}
|
||||
|
||||
patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * req->src_w,
|
||||
req->crtc_w);
|
||||
patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * req->src_h,
|
||||
req->crtc_h);
|
||||
patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * state->src_w,
|
||||
state->crtc_w);
|
||||
patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * state->src_h,
|
||||
state->crtc_h);
|
||||
|
||||
hsub = drm_format_horz_chroma_subsampling(req->fb->pixel_format);
|
||||
vsub = drm_format_vert_chroma_subsampling(req->fb->pixel_format);
|
||||
hsub = drm_format_horz_chroma_subsampling(fb->pixel_format);
|
||||
vsub = drm_format_vert_chroma_subsampling(fb->pixel_format);
|
||||
|
||||
for (i = 0; i < req->nplanes; i++) {
|
||||
for (i = 0; i < state->nplanes; i++) {
|
||||
unsigned int offset = 0;
|
||||
int xdiv = i ? hsub : 1;
|
||||
int ydiv = i ? vsub : 1;
|
||||
|
||||
req->bpp[i] = drm_format_plane_cpp(req->fb->pixel_format, i);
|
||||
if (!req->bpp[i])
|
||||
state->bpp[i] = drm_format_plane_cpp(fb->pixel_format, i);
|
||||
if (!state->bpp[i])
|
||||
return -EINVAL;
|
||||
|
||||
switch (plane->rotation & 0xf) {
|
||||
switch (state->base.rotation & 0xf) {
|
||||
case BIT(DRM_ROTATE_90):
|
||||
offset = ((y_offset + req->src_y + patched_src_w - 1) /
|
||||
ydiv) * req->fb->pitches[i];
|
||||
offset += ((x_offset + req->src_x) / xdiv) *
|
||||
req->bpp[i];
|
||||
req->xstride[i] = ((patched_src_w - 1) / ydiv) *
|
||||
req->fb->pitches[i];
|
||||
req->pstride[i] = -req->fb->pitches[i] - req->bpp[i];
|
||||
offset = ((y_offset + state->src_y + patched_src_w - 1) /
|
||||
ydiv) * fb->pitches[i];
|
||||
offset += ((x_offset + state->src_x) / xdiv) *
|
||||
state->bpp[i];
|
||||
state->xstride[i] = ((patched_src_w - 1) / ydiv) *
|
||||
fb->pitches[i];
|
||||
state->pstride[i] = -fb->pitches[i] - state->bpp[i];
|
||||
break;
|
||||
case BIT(DRM_ROTATE_180):
|
||||
offset = ((y_offset + req->src_y + patched_src_h - 1) /
|
||||
ydiv) * req->fb->pitches[i];
|
||||
offset += ((x_offset + req->src_x + patched_src_w - 1) /
|
||||
xdiv) * req->bpp[i];
|
||||
req->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) *
|
||||
req->bpp[i]) - req->fb->pitches[i];
|
||||
req->pstride[i] = -2 * req->bpp[i];
|
||||
offset = ((y_offset + state->src_y + patched_src_h - 1) /
|
||||
ydiv) * fb->pitches[i];
|
||||
offset += ((x_offset + state->src_x + patched_src_w - 1) /
|
||||
xdiv) * state->bpp[i];
|
||||
state->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) *
|
||||
state->bpp[i]) - fb->pitches[i];
|
||||
state->pstride[i] = -2 * state->bpp[i];
|
||||
break;
|
||||
case BIT(DRM_ROTATE_270):
|
||||
offset = ((y_offset + req->src_y) / ydiv) *
|
||||
req->fb->pitches[i];
|
||||
offset += ((x_offset + req->src_x + patched_src_h - 1) /
|
||||
xdiv) * req->bpp[i];
|
||||
req->xstride[i] = -(((patched_src_w - 1) / ydiv) *
|
||||
req->fb->pitches[i]) -
|
||||
(2 * req->bpp[i]);
|
||||
req->pstride[i] = req->fb->pitches[i] - req->bpp[i];
|
||||
offset = ((y_offset + state->src_y) / ydiv) *
|
||||
fb->pitches[i];
|
||||
offset += ((x_offset + state->src_x + patched_src_h - 1) /
|
||||
xdiv) * state->bpp[i];
|
||||
state->xstride[i] = -(((patched_src_w - 1) / ydiv) *
|
||||
fb->pitches[i]) -
|
||||
(2 * state->bpp[i]);
|
||||
state->pstride[i] = fb->pitches[i] - state->bpp[i];
|
||||
break;
|
||||
case BIT(DRM_ROTATE_0):
|
||||
default:
|
||||
offset = ((y_offset + req->src_y) / ydiv) *
|
||||
req->fb->pitches[i];
|
||||
offset += ((x_offset + req->src_x) / xdiv) *
|
||||
req->bpp[i];
|
||||
req->xstride[i] = req->fb->pitches[i] -
|
||||
offset = ((y_offset + state->src_y) / ydiv) *
|
||||
fb->pitches[i];
|
||||
offset += ((x_offset + state->src_x) / xdiv) *
|
||||
state->bpp[i];
|
||||
state->xstride[i] = fb->pitches[i] -
|
||||
((patched_src_w / xdiv) *
|
||||
req->bpp[i]);
|
||||
req->pstride[i] = 0;
|
||||
state->bpp[i]);
|
||||
state->pstride[i] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
req->offsets[i] = offset + req->fb->offsets[i];
|
||||
state->offsets[i] = offset + fb->offsets[i];
|
||||
}
|
||||
|
||||
req->src_w = patched_src_w;
|
||||
req->src_h = patched_src_h;
|
||||
req->crtc_w = patched_crtc_w;
|
||||
req->crtc_h = patched_crtc_h;
|
||||
state->src_w = patched_src_w;
|
||||
state->src_h = patched_src_h;
|
||||
state->crtc_w = patched_crtc_w;
|
||||
state->crtc_h = patched_crtc_h;
|
||||
|
||||
return atmel_hlcdc_plane_check_update_req(p, req, mode);
|
||||
}
|
||||
if (!layout->size &&
|
||||
(mode->hdisplay != state->crtc_w ||
|
||||
mode->vdisplay != state->crtc_h))
|
||||
return -EINVAL;
|
||||
|
||||
int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
|
||||
struct atmel_hlcdc_plane_update_req *req)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
int ret;
|
||||
if (plane->layer.desc->max_height &&
|
||||
state->crtc_h > plane->layer.desc->max_height)
|
||||
return -EINVAL;
|
||||
|
||||
ret = atmel_hlcdc_layer_update_start(&plane->layer);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (plane->layer.desc->max_width &&
|
||||
state->crtc_w > plane->layer.desc->max_width)
|
||||
return -EINVAL;
|
||||
|
||||
atmel_hlcdc_plane_update_pos_and_size(plane, req);
|
||||
atmel_hlcdc_plane_update_general_settings(plane, req);
|
||||
atmel_hlcdc_plane_update_format(plane, req);
|
||||
atmel_hlcdc_plane_update_buffers(plane, req);
|
||||
if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
|
||||
(!layout->memsize ||
|
||||
atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format)))
|
||||
return -EINVAL;
|
||||
|
||||
atmel_hlcdc_layer_update_commit(&plane->layer);
|
||||
if (state->crtc_x < 0 || state->crtc_y < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (state->crtc_w + state->crtc_x > mode->hdisplay ||
|
||||
state->crtc_h + state->crtc_y > mode->vdisplay)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atmel_hlcdc_plane_update_with_mode(struct drm_plane *p,
|
||||
struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb,
|
||||
int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w,
|
||||
unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
struct atmel_hlcdc_plane_update_req req;
|
||||
int ret = 0;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.crtc_x = crtc_x;
|
||||
req.crtc_y = crtc_y;
|
||||
req.crtc_w = crtc_w;
|
||||
req.crtc_h = crtc_h;
|
||||
req.src_x = src_x;
|
||||
req.src_y = src_y;
|
||||
req.src_w = src_w;
|
||||
req.src_h = src_h;
|
||||
req.fb = fb;
|
||||
|
||||
ret = atmel_hlcdc_plane_prepare_update_req(&plane->base, &req, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!req.crtc_h || !req.crtc_w)
|
||||
return atmel_hlcdc_layer_disable(&plane->layer);
|
||||
|
||||
return atmel_hlcdc_plane_apply_update_req(&plane->base, &req);
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_update(struct drm_plane *p,
|
||||
struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb,
|
||||
int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h)
|
||||
{
|
||||
return atmel_hlcdc_plane_update_with_mode(p, crtc, fb, crtc_x, crtc_y,
|
||||
crtc_w, crtc_h, src_x, src_y,
|
||||
src_w, src_h, &crtc->hwmode);
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_disable(struct drm_plane *p)
|
||||
static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p,
|
||||
struct drm_framebuffer *fb,
|
||||
const struct drm_plane_state *new_state)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
|
||||
return atmel_hlcdc_layer_disable(&plane->layer);
|
||||
return atmel_hlcdc_layer_update_start(&plane->layer);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
|
||||
struct drm_plane_state *old_s)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
struct atmel_hlcdc_plane_state *state =
|
||||
drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
|
||||
|
||||
if (!p->state->crtc || !p->state->fb)
|
||||
return;
|
||||
|
||||
atmel_hlcdc_plane_update_pos_and_size(plane, state);
|
||||
atmel_hlcdc_plane_update_general_settings(plane, state);
|
||||
atmel_hlcdc_plane_update_format(plane, state);
|
||||
atmel_hlcdc_plane_update_buffers(plane, state);
|
||||
atmel_hlcdc_plane_update_disc_area(plane, state);
|
||||
|
||||
atmel_hlcdc_layer_update_commit(&plane->layer);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
|
||||
atmel_hlcdc_layer_disable(&plane->layer);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
|
||||
@ -635,38 +760,36 @@ static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
|
||||
devm_kfree(p->dev->dev, plane);
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_set_alpha(struct atmel_hlcdc_plane *plane,
|
||||
u8 alpha)
|
||||
{
|
||||
atmel_hlcdc_layer_update_start(&plane->layer);
|
||||
atmel_hlcdc_layer_update_cfg(&plane->layer,
|
||||
plane->layer.desc->layout.general_config,
|
||||
ATMEL_HLCDC_LAYER_GA_MASK,
|
||||
alpha << ATMEL_HLCDC_LAYER_GA_SHIFT);
|
||||
atmel_hlcdc_layer_update_commit(&plane->layer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_set_rotation(struct atmel_hlcdc_plane *plane,
|
||||
unsigned int rotation)
|
||||
{
|
||||
plane->rotation = rotation;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_set_property(struct drm_plane *p,
|
||||
struct drm_property *property,
|
||||
uint64_t value)
|
||||
static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p,
|
||||
struct drm_plane_state *s,
|
||||
struct drm_property *property,
|
||||
uint64_t val)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
struct atmel_hlcdc_plane_properties *props = plane->properties;
|
||||
struct atmel_hlcdc_plane_state *state =
|
||||
drm_plane_state_to_atmel_hlcdc_plane_state(s);
|
||||
|
||||
if (property == props->alpha)
|
||||
atmel_hlcdc_plane_set_alpha(plane, value);
|
||||
else if (property == props->rotation)
|
||||
atmel_hlcdc_plane_set_rotation(plane, value);
|
||||
state->alpha = val;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
|
||||
const struct drm_plane_state *s,
|
||||
struct drm_property *property,
|
||||
uint64_t *val)
|
||||
{
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
struct atmel_hlcdc_plane_properties *props = plane->properties;
|
||||
const struct atmel_hlcdc_plane_state *state =
|
||||
container_of(s, const struct atmel_hlcdc_plane_state, base);
|
||||
|
||||
if (property == props->alpha)
|
||||
*val = state->alpha;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
@ -694,8 +817,8 @@ static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
|
||||
|
||||
if (desc->layout.xstride && desc->layout.pstride)
|
||||
drm_object_attach_property(&plane->base.base,
|
||||
props->rotation,
|
||||
BIT(DRM_ROTATE_0));
|
||||
plane->base.dev->mode_config.rotation_property,
|
||||
BIT(DRM_ROTATE_0));
|
||||
|
||||
if (desc->layout.csc) {
|
||||
/*
|
||||
@ -717,11 +840,76 @@ static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
|
||||
}
|
||||
}
|
||||
|
||||
static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
|
||||
.prepare_fb = atmel_hlcdc_plane_prepare_fb,
|
||||
.atomic_check = atmel_hlcdc_plane_atomic_check,
|
||||
.atomic_update = atmel_hlcdc_plane_atomic_update,
|
||||
.atomic_disable = atmel_hlcdc_plane_atomic_disable,
|
||||
};
|
||||
|
||||
static void atmel_hlcdc_plane_reset(struct drm_plane *p)
|
||||
{
|
||||
struct atmel_hlcdc_plane_state *state;
|
||||
|
||||
if (p->state) {
|
||||
state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
|
||||
|
||||
if (state->base.fb)
|
||||
drm_framebuffer_unreference(state->base.fb);
|
||||
|
||||
kfree(state);
|
||||
p->state = NULL;
|
||||
}
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (state) {
|
||||
state->alpha = 255;
|
||||
p->state = &state->base;
|
||||
p->state->plane = p;
|
||||
}
|
||||
}
|
||||
|
||||
static struct drm_plane_state *
|
||||
atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
|
||||
{
|
||||
struct atmel_hlcdc_plane_state *state =
|
||||
drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
|
||||
struct atmel_hlcdc_plane_state *copy;
|
||||
|
||||
copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
|
||||
if (!copy)
|
||||
return NULL;
|
||||
|
||||
copy->disc_updated = false;
|
||||
|
||||
if (copy->base.fb)
|
||||
drm_framebuffer_reference(copy->base.fb);
|
||||
|
||||
return ©->base;
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *s)
|
||||
{
|
||||
struct atmel_hlcdc_plane_state *state =
|
||||
drm_plane_state_to_atmel_hlcdc_plane_state(s);
|
||||
|
||||
if (s->fb)
|
||||
drm_framebuffer_unreference(s->fb);
|
||||
|
||||
kfree(state);
|
||||
}
|
||||
|
||||
static struct drm_plane_funcs layer_plane_funcs = {
|
||||
.update_plane = atmel_hlcdc_plane_update,
|
||||
.disable_plane = atmel_hlcdc_plane_disable,
|
||||
.set_property = atmel_hlcdc_plane_set_property,
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.set_property = drm_atomic_helper_plane_set_property,
|
||||
.destroy = atmel_hlcdc_plane_destroy,
|
||||
.reset = atmel_hlcdc_plane_reset,
|
||||
.atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
|
||||
.atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
|
||||
.atomic_set_property = atmel_hlcdc_plane_atomic_set_property,
|
||||
.atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
|
||||
};
|
||||
|
||||
static struct atmel_hlcdc_plane *
|
||||
@ -755,6 +943,9 @@ atmel_hlcdc_plane_create(struct drm_device *dev,
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
drm_plane_helper_add(&plane->base,
|
||||
&atmel_hlcdc_layer_plane_helper_funcs);
|
||||
|
||||
/* Set default property values*/
|
||||
atmel_hlcdc_plane_init_properties(plane, desc, props);
|
||||
|
||||
@ -774,12 +965,13 @@ atmel_hlcdc_plane_create_properties(struct drm_device *dev)
|
||||
if (!props->alpha)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
props->rotation = drm_mode_create_rotation_property(dev,
|
||||
BIT(DRM_ROTATE_0) |
|
||||
BIT(DRM_ROTATE_90) |
|
||||
BIT(DRM_ROTATE_180) |
|
||||
BIT(DRM_ROTATE_270));
|
||||
if (!props->rotation)
|
||||
dev->mode_config.rotation_property =
|
||||
drm_mode_create_rotation_property(dev,
|
||||
BIT(DRM_ROTATE_0) |
|
||||
BIT(DRM_ROTATE_90) |
|
||||
BIT(DRM_ROTATE_180) |
|
||||
BIT(DRM_ROTATE_270));
|
||||
if (!dev->mode_config.rotation_property)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return props;
|
||||
|
@ -164,6 +164,7 @@ void bochs_hw_setmode(struct bochs_device *bochs,
|
||||
|
||||
bochs_vga_writeb(bochs, 0x3c0, 0x20); /* unblank */
|
||||
|
||||
bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, 0);
|
||||
bochs_dispi_write(bochs, VBE_DISPI_INDEX_BPP, bochs->bpp);
|
||||
bochs_dispi_write(bochs, VBE_DISPI_INDEX_XRES, bochs->xres);
|
||||
bochs_dispi_write(bochs, VBE_DISPI_INDEX_YRES, bochs->yres);
|
||||
|
@ -11,3 +11,14 @@ config DRM_PTN3460
|
||||
select DRM_PANEL
|
||||
---help---
|
||||
ptn3460 eDP-LVDS bridge chip driver.
|
||||
|
||||
config DRM_PS8622
|
||||
tristate "Parade eDP/LVDS bridge"
|
||||
depends on DRM
|
||||
depends on OF
|
||||
select DRM_PANEL
|
||||
select DRM_KMS_HELPER
|
||||
select BACKLIGHT_LCD_SUPPORT
|
||||
select BACKLIGHT_CLASS_DEVICE
|
||||
---help---
|
||||
parade eDP-LVDS bridge chip driver.
|
||||
|
@ -1,4 +1,5 @@
|
||||
ccflags-y := -Iinclude/drm
|
||||
|
||||
obj-$(CONFIG_DRM_PS8622) += ps8622.o
|
||||
obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
|
||||
obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <drm/drm_of.h>
|
||||
@ -126,6 +127,7 @@ struct dw_hdmi {
|
||||
struct i2c_adapter *ddc;
|
||||
void __iomem *regs;
|
||||
|
||||
struct mutex audio_mutex;
|
||||
unsigned int sample_rate;
|
||||
int ratio;
|
||||
|
||||
@ -177,26 +179,23 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
|
||||
hdmi_modb(hdmi, data << shift, mask, reg);
|
||||
}
|
||||
|
||||
static void hdmi_set_clock_regenerator_n(struct dw_hdmi *hdmi,
|
||||
unsigned int value)
|
||||
{
|
||||
hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1);
|
||||
hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
|
||||
hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
|
||||
|
||||
/* nshift factor = 0 */
|
||||
hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
|
||||
}
|
||||
|
||||
static void hdmi_regenerate_cts(struct dw_hdmi *hdmi, unsigned int cts)
|
||||
static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
|
||||
unsigned int n)
|
||||
{
|
||||
/* Must be set/cleared first */
|
||||
hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
|
||||
|
||||
hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
|
||||
hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
|
||||
/* nshift factor = 0 */
|
||||
hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
|
||||
|
||||
hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
|
||||
HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
|
||||
hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
|
||||
hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
|
||||
|
||||
hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3);
|
||||
hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
|
||||
hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
|
||||
}
|
||||
|
||||
static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
|
||||
@ -355,18 +354,21 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
|
||||
__func__, hdmi->sample_rate, hdmi->ratio,
|
||||
pixel_clk, clk_n, clk_cts);
|
||||
|
||||
hdmi_set_clock_regenerator_n(hdmi, clk_n);
|
||||
hdmi_regenerate_cts(hdmi, clk_cts);
|
||||
hdmi_set_cts_n(hdmi, clk_cts, clk_n);
|
||||
}
|
||||
|
||||
static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
|
||||
{
|
||||
mutex_lock(&hdmi->audio_mutex);
|
||||
hdmi_set_clk_regenerator(hdmi, 74250000);
|
||||
mutex_unlock(&hdmi->audio_mutex);
|
||||
}
|
||||
|
||||
static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
|
||||
{
|
||||
mutex_lock(&hdmi->audio_mutex);
|
||||
hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock);
|
||||
mutex_unlock(&hdmi->audio_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -753,10 +755,10 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
|
||||
{
|
||||
unsigned res_idx, i;
|
||||
u8 val, msec;
|
||||
const struct dw_hdmi_mpll_config *mpll_config =
|
||||
hdmi->plat_data->mpll_cfg;
|
||||
const struct dw_hdmi_curr_ctrl *curr_ctrl = hdmi->plat_data->cur_ctr;
|
||||
const struct dw_hdmi_sym_term *sym_term = hdmi->plat_data->sym_term;
|
||||
const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
|
||||
const struct dw_hdmi_mpll_config *mpll_config = plat_data->mpll_cfg;
|
||||
const struct dw_hdmi_curr_ctrl *curr_ctrl = plat_data->cur_ctr;
|
||||
const struct dw_hdmi_phy_config *phy_config = plat_data->phy_config;
|
||||
|
||||
if (prep)
|
||||
return -EINVAL;
|
||||
@ -827,18 +829,18 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
|
||||
hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */
|
||||
hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
|
||||
|
||||
for (i = 0; sym_term[i].mpixelclock != (~0UL); i++)
|
||||
for (i = 0; phy_config[i].mpixelclock != (~0UL); i++)
|
||||
if (hdmi->hdmi_data.video_mode.mpixelclock <=
|
||||
sym_term[i].mpixelclock)
|
||||
phy_config[i].mpixelclock)
|
||||
break;
|
||||
|
||||
/* RESISTANCE TERM 133Ohm Cfg */
|
||||
hdmi_phy_i2c_write(hdmi, sym_term[i].term, 0x19); /* TXTERM */
|
||||
hdmi_phy_i2c_write(hdmi, phy_config[i].term, 0x19); /* TXTERM */
|
||||
/* PREEMP Cgf 0.00 */
|
||||
hdmi_phy_i2c_write(hdmi, sym_term[i].sym_ctr, 0x09); /* CKSYMTXCTRL */
|
||||
|
||||
hdmi_phy_i2c_write(hdmi, phy_config[i].sym_ctr, 0x09); /* CKSYMTXCTRL */
|
||||
/* TX/CK LVL 10 */
|
||||
hdmi_phy_i2c_write(hdmi, 0x01ad, 0x0E); /* VLEVCTRL */
|
||||
hdmi_phy_i2c_write(hdmi, phy_config[i].vlev_ctr, 0x0E); /* VLEVCTRL */
|
||||
|
||||
/* REMOVE CLK TERM */
|
||||
hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */
|
||||
|
||||
@ -1569,6 +1571,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
|
||||
hdmi->ratio = 100;
|
||||
hdmi->encoder = encoder;
|
||||
|
||||
mutex_init(&hdmi->audio_mutex);
|
||||
|
||||
of_property_read_u32(np, "reg-io-width", &val);
|
||||
|
||||
switch (val) {
|
||||
|
684
drivers/gpu/drm/bridge/ps8622.c
Normal file
684
drivers/gpu/drm/bridge/ps8622.c
Normal file
@ -0,0 +1,684 @@
|
||||
/*
|
||||
* Parade PS8622 eDP/LVDS bridge driver
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include "drmP.h"
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
|
||||
/* Brightness scale on the Parade chip */
|
||||
#define PS8622_MAX_BRIGHTNESS 0xff
|
||||
|
||||
/* Timings taken from the version 1.7 datasheet for the PS8622/PS8625 */
|
||||
#define PS8622_POWER_RISE_T1_MIN_US 10
|
||||
#define PS8622_POWER_RISE_T1_MAX_US 10000
|
||||
#define PS8622_RST_HIGH_T2_MIN_US 3000
|
||||
#define PS8622_RST_HIGH_T2_MAX_US 30000
|
||||
#define PS8622_PWMO_END_T12_MS 200
|
||||
#define PS8622_POWER_FALL_T16_MAX_US 10000
|
||||
#define PS8622_POWER_OFF_T17_MS 500
|
||||
|
||||
#if ((PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US) > \
|
||||
(PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US))
|
||||
#error "T2.min + T1.max must be less than T2.max + T1.min"
|
||||
#endif
|
||||
|
||||
struct ps8622_bridge {
|
||||
struct drm_connector connector;
|
||||
struct i2c_client *client;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_panel *panel;
|
||||
struct regulator *v12;
|
||||
struct backlight_device *bl;
|
||||
|
||||
struct gpio_desc *gpio_slp;
|
||||
struct gpio_desc *gpio_rst;
|
||||
|
||||
u32 max_lane_count;
|
||||
u32 lane_count;
|
||||
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static inline struct ps8622_bridge *
|
||||
bridge_to_ps8622(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct ps8622_bridge, bridge);
|
||||
}
|
||||
|
||||
static inline struct ps8622_bridge *
|
||||
connector_to_ps8622(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct ps8622_bridge, connector);
|
||||
}
|
||||
|
||||
static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_adapter *adap = client->adapter;
|
||||
struct i2c_msg msg;
|
||||
u8 data[] = {reg, val};
|
||||
|
||||
msg.addr = client->addr + page;
|
||||
msg.flags = 0;
|
||||
msg.len = sizeof(data);
|
||||
msg.buf = data;
|
||||
|
||||
ret = i2c_transfer(adap, &msg, 1);
|
||||
if (ret != 1)
|
||||
pr_warn("PS8622 I2C write (0x%02x,0x%02x,0x%02x) failed: %d\n",
|
||||
client->addr + page, reg, val, ret);
|
||||
return !(ret == 1);
|
||||
}
|
||||
|
||||
static int ps8622_send_config(struct ps8622_bridge *ps8622)
|
||||
{
|
||||
struct i2c_client *cl = ps8622->client;
|
||||
int err = 0;
|
||||
|
||||
/* HPD low */
|
||||
err = ps8622_set(cl, 0x02, 0xa1, 0x01);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* SW setting: [1:0] SW output 1.2V voltage is lower to 96% */
|
||||
err = ps8622_set(cl, 0x04, 0x14, 0x01);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* RCO SS setting: [5:4] = b01 0.5%, b10 1%, b11 1.5% */
|
||||
err = ps8622_set(cl, 0x04, 0xe3, 0x20);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* [7] RCO SS enable */
|
||||
err = ps8622_set(cl, 0x04, 0xe2, 0x80);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* RPHY Setting
|
||||
* [3:2] CDR tune wait cycle before measure for fine tune
|
||||
* b00: 1us b01: 0.5us b10:2us, b11: 4us
|
||||
*/
|
||||
err = ps8622_set(cl, 0x04, 0x8a, 0x0c);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* [3] RFD always on */
|
||||
err = ps8622_set(cl, 0x04, 0x89, 0x08);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* CTN lock in/out: 20000ppm/80000ppm. Lock out 2 times. */
|
||||
err = ps8622_set(cl, 0x04, 0x71, 0x2d);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* 2.7G CDR settings: NOF=40LSB for HBR CDR setting */
|
||||
err = ps8622_set(cl, 0x04, 0x7d, 0x07);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* [1:0] Fmin=+4bands */
|
||||
err = ps8622_set(cl, 0x04, 0x7b, 0x00);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* [7:5] DCO_FTRNG=+-40% */
|
||||
err = ps8622_set(cl, 0x04, 0x7a, 0xfd);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* 1.62G CDR settings: [5:2]NOF=64LSB [1:0]DCO scale is 2/5 */
|
||||
err = ps8622_set(cl, 0x04, 0xc0, 0x12);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Gitune=-37% */
|
||||
err = ps8622_set(cl, 0x04, 0xc1, 0x92);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Fbstep=100% */
|
||||
err = ps8622_set(cl, 0x04, 0xc2, 0x1c);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* [7] LOS signal disable */
|
||||
err = ps8622_set(cl, 0x04, 0x32, 0x80);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* RPIO Setting: [7:4] LVDS driver bias current : 75% (250mV swing) */
|
||||
err = ps8622_set(cl, 0x04, 0x00, 0xb0);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* [7:6] Right-bar GPIO output strength is 8mA */
|
||||
err = ps8622_set(cl, 0x04, 0x15, 0x40);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* EQ Training State Machine Setting, RCO calibration start */
|
||||
err = ps8622_set(cl, 0x04, 0x54, 0x10);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Logic, needs more than 10 I2C command */
|
||||
/* [4:0] MAX_LANE_COUNT set to max supported lanes */
|
||||
err = ps8622_set(cl, 0x01, 0x02, 0x80 | ps8622->max_lane_count);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* [4:0] LANE_COUNT_SET set to chosen lane count */
|
||||
err = ps8622_set(cl, 0x01, 0x21, 0x80 | ps8622->lane_count);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = ps8622_set(cl, 0x00, 0x52, 0x20);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* HPD CP toggle enable */
|
||||
err = ps8622_set(cl, 0x00, 0xf1, 0x03);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = ps8622_set(cl, 0x00, 0x62, 0x41);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Counter number, add 1ms counter delay */
|
||||
err = ps8622_set(cl, 0x00, 0xf6, 0x01);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* [6]PWM function control by DPCD0040f[7], default is PWM block */
|
||||
err = ps8622_set(cl, 0x00, 0x77, 0x06);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* 04h Adjust VTotal toleranceto fix the 30Hz no display issue */
|
||||
err = ps8622_set(cl, 0x00, 0x4c, 0x04);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD00400='h00, Parade OUI ='h001cf8 */
|
||||
err = ps8622_set(cl, 0x01, 0xc0, 0x00);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD00401='h1c */
|
||||
err = ps8622_set(cl, 0x01, 0xc1, 0x1c);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD00402='hf8 */
|
||||
err = ps8622_set(cl, 0x01, 0xc2, 0xf8);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD403~408 = ASCII code, D2SLV5='h4432534c5635 */
|
||||
err = ps8622_set(cl, 0x01, 0xc3, 0x44);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD404 */
|
||||
err = ps8622_set(cl, 0x01, 0xc4, 0x32);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD405 */
|
||||
err = ps8622_set(cl, 0x01, 0xc5, 0x53);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD406 */
|
||||
err = ps8622_set(cl, 0x01, 0xc6, 0x4c);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD407 */
|
||||
err = ps8622_set(cl, 0x01, 0xc7, 0x56);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD408 */
|
||||
err = ps8622_set(cl, 0x01, 0xc8, 0x35);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD40A, Initial Code major revision '01' */
|
||||
err = ps8622_set(cl, 0x01, 0xca, 0x01);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* DPCD40B, Initial Code minor revision '05' */
|
||||
err = ps8622_set(cl, 0x01, 0xcb, 0x05);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
|
||||
if (ps8622->bl) {
|
||||
/* DPCD720, internal PWM */
|
||||
err = ps8622_set(cl, 0x01, 0xa5, 0xa0);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* FFh for 100% brightness, 0h for 0% brightness */
|
||||
err = ps8622_set(cl, 0x01, 0xa7,
|
||||
ps8622->bl->props.brightness);
|
||||
if (err)
|
||||
goto error;
|
||||
} else {
|
||||
/* DPCD720, external PWM */
|
||||
err = ps8622_set(cl, 0x01, 0xa5, 0x80);
|
||||
if (err)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Set LVDS output as 6bit-VESA mapping, single LVDS channel */
|
||||
err = ps8622_set(cl, 0x01, 0xcc, 0x13);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Enable SSC set by register */
|
||||
err = ps8622_set(cl, 0x02, 0xb1, 0x20);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Set SSC enabled and +/-1% central spreading */
|
||||
err = ps8622_set(cl, 0x04, 0x10, 0x16);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Logic end */
|
||||
/* MPU Clock source: LC => RCO */
|
||||
err = ps8622_set(cl, 0x04, 0x59, 0x60);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* LC -> RCO */
|
||||
err = ps8622_set(cl, 0x04, 0x54, 0x14);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* HPD high */
|
||||
err = ps8622_set(cl, 0x02, 0xa1, 0x91);
|
||||
|
||||
error:
|
||||
return err ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int ps8622_backlight_update(struct backlight_device *bl)
|
||||
{
|
||||
struct ps8622_bridge *ps8622 = dev_get_drvdata(&bl->dev);
|
||||
int ret, brightness = bl->props.brightness;
|
||||
|
||||
if (bl->props.power != FB_BLANK_UNBLANK ||
|
||||
bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
|
||||
brightness = 0;
|
||||
|
||||
if (!ps8622->enabled)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ps8622_set(ps8622->client, 0x01, 0xa7, brightness);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct backlight_ops ps8622_backlight_ops = {
|
||||
.update_status = ps8622_backlight_update,
|
||||
};
|
||||
|
||||
static void ps8622_pre_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
|
||||
int ret;
|
||||
|
||||
if (ps8622->enabled)
|
||||
return;
|
||||
|
||||
gpiod_set_value(ps8622->gpio_rst, 0);
|
||||
|
||||
if (ps8622->v12) {
|
||||
ret = regulator_enable(ps8622->v12);
|
||||
if (ret)
|
||||
DRM_ERROR("fails to enable ps8622->v12");
|
||||
}
|
||||
|
||||
if (drm_panel_prepare(ps8622->panel)) {
|
||||
DRM_ERROR("failed to prepare panel\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gpiod_set_value(ps8622->gpio_slp, 1);
|
||||
|
||||
/*
|
||||
* T1 is the range of time that it takes for the power to rise after we
|
||||
* enable the lcd/ps8622 fet. T2 is the range of time in which the
|
||||
* data sheet specifies we should deassert the reset pin.
|
||||
*
|
||||
* If it takes T1.max for the power to rise, we need to wait atleast
|
||||
* T2.min before deasserting the reset pin. If it takes T1.min for the
|
||||
* power to rise, we need to wait at most T2.max before deasserting the
|
||||
* reset pin.
|
||||
*/
|
||||
usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US,
|
||||
PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US);
|
||||
|
||||
gpiod_set_value(ps8622->gpio_rst, 1);
|
||||
|
||||
/* wait 20ms after RST high */
|
||||
usleep_range(20000, 30000);
|
||||
|
||||
ret = ps8622_send_config(ps8622);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to send config to bridge (%d)\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ps8622->enabled = true;
|
||||
}
|
||||
|
||||
static void ps8622_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
|
||||
|
||||
if (drm_panel_enable(ps8622->panel)) {
|
||||
DRM_ERROR("failed to enable panel\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void ps8622_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
|
||||
|
||||
if (drm_panel_disable(ps8622->panel)) {
|
||||
DRM_ERROR("failed to disable panel\n");
|
||||
return;
|
||||
}
|
||||
msleep(PS8622_PWMO_END_T12_MS);
|
||||
}
|
||||
|
||||
static void ps8622_post_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
|
||||
|
||||
if (!ps8622->enabled)
|
||||
return;
|
||||
|
||||
ps8622->enabled = false;
|
||||
|
||||
/*
|
||||
* This doesn't matter if the regulators are turned off, but something
|
||||
* else might keep them on. In that case, we want to assert the slp gpio
|
||||
* to lower power.
|
||||
*/
|
||||
gpiod_set_value(ps8622->gpio_slp, 0);
|
||||
|
||||
if (drm_panel_unprepare(ps8622->panel)) {
|
||||
DRM_ERROR("failed to unprepare panel\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ps8622->v12)
|
||||
regulator_disable(ps8622->v12);
|
||||
|
||||
/*
|
||||
* Sleep for at least the amount of time that it takes the power rail to
|
||||
* fall to prevent asserting the rst gpio from doing anything.
|
||||
*/
|
||||
usleep_range(PS8622_POWER_FALL_T16_MAX_US,
|
||||
2 * PS8622_POWER_FALL_T16_MAX_US);
|
||||
gpiod_set_value(ps8622->gpio_rst, 0);
|
||||
|
||||
msleep(PS8622_POWER_OFF_T17_MS);
|
||||
}
|
||||
|
||||
static int ps8622_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct ps8622_bridge *ps8622;
|
||||
|
||||
ps8622 = connector_to_ps8622(connector);
|
||||
|
||||
return drm_panel_get_modes(ps8622->panel);
|
||||
}
|
||||
|
||||
static struct drm_encoder *ps8622_best_encoder(struct drm_connector *connector)
|
||||
{
|
||||
struct ps8622_bridge *ps8622;
|
||||
|
||||
ps8622 = connector_to_ps8622(connector);
|
||||
|
||||
return ps8622->bridge.encoder;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = {
|
||||
.get_modes = ps8622_get_modes,
|
||||
.best_encoder = ps8622_best_encoder,
|
||||
};
|
||||
|
||||
static enum drm_connector_status ps8622_detect(struct drm_connector *connector,
|
||||
bool force)
|
||||
{
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static void ps8622_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs ps8622_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = ps8622_detect,
|
||||
.destroy = ps8622_connector_destroy,
|
||||
};
|
||||
|
||||
static int ps8622_attach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
|
||||
int ret;
|
||||
|
||||
if (!bridge->encoder) {
|
||||
DRM_ERROR("Parent encoder object not found");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ps8622->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
ret = drm_connector_init(bridge->dev, &ps8622->connector,
|
||||
&ps8622_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to initialize connector with drm\n");
|
||||
return ret;
|
||||
}
|
||||
drm_connector_helper_add(&ps8622->connector,
|
||||
&ps8622_connector_helper_funcs);
|
||||
drm_connector_register(&ps8622->connector);
|
||||
drm_mode_connector_attach_encoder(&ps8622->connector,
|
||||
bridge->encoder);
|
||||
|
||||
if (ps8622->panel)
|
||||
drm_panel_attach(ps8622->panel, &ps8622->connector);
|
||||
|
||||
drm_helper_hpd_irq_event(ps8622->connector.dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs ps8622_bridge_funcs = {
|
||||
.pre_enable = ps8622_pre_enable,
|
||||
.enable = ps8622_enable,
|
||||
.disable = ps8622_disable,
|
||||
.post_disable = ps8622_post_disable,
|
||||
.attach = ps8622_attach,
|
||||
};
|
||||
|
||||
static const struct of_device_id ps8622_devices[] = {
|
||||
{.compatible = "parade,ps8622",},
|
||||
{.compatible = "parade,ps8625",},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ps8622_devices);
|
||||
|
||||
static int ps8622_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *endpoint, *panel_node;
|
||||
struct ps8622_bridge *ps8622;
|
||||
int ret;
|
||||
|
||||
ps8622 = devm_kzalloc(dev, sizeof(*ps8622), GFP_KERNEL);
|
||||
if (!ps8622)
|
||||
return -ENOMEM;
|
||||
|
||||
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
|
||||
if (endpoint) {
|
||||
panel_node = of_graph_get_remote_port_parent(endpoint);
|
||||
if (panel_node) {
|
||||
ps8622->panel = of_drm_find_panel(panel_node);
|
||||
of_node_put(panel_node);
|
||||
if (!ps8622->panel)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
}
|
||||
|
||||
ps8622->client = client;
|
||||
|
||||
ps8622->v12 = devm_regulator_get(dev, "vdd12");
|
||||
if (IS_ERR(ps8622->v12)) {
|
||||
dev_info(dev, "no 1.2v regulator found for PS8622\n");
|
||||
ps8622->v12 = NULL;
|
||||
}
|
||||
|
||||
ps8622->gpio_slp = devm_gpiod_get(dev, "sleep");
|
||||
if (IS_ERR(ps8622->gpio_slp)) {
|
||||
ret = PTR_ERR(ps8622->gpio_slp);
|
||||
dev_err(dev, "cannot get gpio_slp %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = gpiod_direction_output(ps8622->gpio_slp, 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot configure gpio_slp\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ps8622->gpio_rst = devm_gpiod_get(dev, "reset");
|
||||
if (IS_ERR(ps8622->gpio_rst)) {
|
||||
ret = PTR_ERR(ps8622->gpio_rst);
|
||||
dev_err(dev, "cannot get gpio_rst %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* Assert the reset pin high to avoid the bridge being
|
||||
* initialized prematurely
|
||||
*/
|
||||
ret = gpiod_direction_output(ps8622->gpio_rst, 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot configure gpio_rst\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ps8622->max_lane_count = id->driver_data;
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "lane-count",
|
||||
&ps8622->lane_count)) {
|
||||
ps8622->lane_count = ps8622->max_lane_count;
|
||||
} else if (ps8622->lane_count > ps8622->max_lane_count) {
|
||||
dev_info(dev, "lane-count property is too high,"
|
||||
"using max_lane_count\n");
|
||||
ps8622->lane_count = ps8622->max_lane_count;
|
||||
}
|
||||
|
||||
if (!of_find_property(dev->of_node, "use-external-pwm", NULL)) {
|
||||
ps8622->bl = backlight_device_register("ps8622-backlight",
|
||||
dev, ps8622, &ps8622_backlight_ops,
|
||||
NULL);
|
||||
if (IS_ERR(ps8622->bl)) {
|
||||
DRM_ERROR("failed to register backlight\n");
|
||||
ret = PTR_ERR(ps8622->bl);
|
||||
ps8622->bl = NULL;
|
||||
return ret;
|
||||
}
|
||||
ps8622->bl->props.max_brightness = PS8622_MAX_BRIGHTNESS;
|
||||
ps8622->bl->props.brightness = PS8622_MAX_BRIGHTNESS;
|
||||
}
|
||||
|
||||
ps8622->bridge.funcs = &ps8622_bridge_funcs;
|
||||
ps8622->bridge.of_node = dev->of_node;
|
||||
ret = drm_bridge_add(&ps8622->bridge);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to add bridge\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, ps8622);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ps8622_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ps8622_bridge *ps8622 = i2c_get_clientdata(client);
|
||||
|
||||
if (ps8622->bl)
|
||||
backlight_device_unregister(ps8622->bl);
|
||||
|
||||
drm_bridge_remove(&ps8622->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ps8622_i2c_table[] = {
|
||||
/* Device type, max_lane_count */
|
||||
{"ps8622", 1},
|
||||
{"ps8625", 2},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table);
|
||||
|
||||
static struct i2c_driver ps8622_driver = {
|
||||
.id_table = ps8622_i2c_table,
|
||||
.probe = ps8622_probe,
|
||||
.remove = ps8622_remove,
|
||||
.driver = {
|
||||
.name = "ps8622",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ps8622_devices,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(ps8622_driver);
|
||||
|
||||
MODULE_AUTHOR("Vincent Palatin <vpalatin@chromium.org>");
|
||||
MODULE_DESCRIPTION("Parade ps8622/ps8625 eDP-LVDS converter driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -265,7 +265,7 @@ static struct drm_connector_funcs ptn3460_connector_funcs = {
|
||||
.destroy = ptn3460_connector_destroy,
|
||||
};
|
||||
|
||||
int ptn3460_bridge_attach(struct drm_bridge *bridge)
|
||||
static int ptn3460_bridge_attach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
|
||||
int ret;
|
||||
|
@ -92,7 +92,7 @@ drm_atomic_state_alloc(struct drm_device *dev)
|
||||
|
||||
state->dev = dev;
|
||||
|
||||
DRM_DEBUG_KMS("Allocate atomic state %p\n", state);
|
||||
DRM_DEBUG_ATOMIC("Allocate atomic state %p\n", state);
|
||||
|
||||
return state;
|
||||
fail:
|
||||
@ -122,7 +122,7 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
int i;
|
||||
|
||||
DRM_DEBUG_KMS("Clearing atomic state %p\n", state);
|
||||
DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state);
|
||||
|
||||
for (i = 0; i < state->num_connector; i++) {
|
||||
struct drm_connector *connector = state->connectors[i];
|
||||
@ -134,6 +134,7 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)
|
||||
|
||||
connector->funcs->atomic_destroy_state(connector,
|
||||
state->connector_states[i]);
|
||||
state->connectors[i] = NULL;
|
||||
state->connector_states[i] = NULL;
|
||||
}
|
||||
|
||||
@ -145,6 +146,7 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)
|
||||
|
||||
crtc->funcs->atomic_destroy_state(crtc,
|
||||
state->crtc_states[i]);
|
||||
state->crtcs[i] = NULL;
|
||||
state->crtc_states[i] = NULL;
|
||||
}
|
||||
|
||||
@ -156,6 +158,7 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)
|
||||
|
||||
plane->funcs->atomic_destroy_state(plane,
|
||||
state->plane_states[i]);
|
||||
state->planes[i] = NULL;
|
||||
state->plane_states[i] = NULL;
|
||||
}
|
||||
}
|
||||
@ -170,9 +173,12 @@ EXPORT_SYMBOL(drm_atomic_state_clear);
|
||||
*/
|
||||
void drm_atomic_state_free(struct drm_atomic_state *state)
|
||||
{
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
drm_atomic_state_clear(state);
|
||||
|
||||
DRM_DEBUG_KMS("Freeing atomic state %p\n", state);
|
||||
DRM_DEBUG_ATOMIC("Freeing atomic state %p\n", state);
|
||||
|
||||
kfree_state(state);
|
||||
}
|
||||
@ -217,8 +223,8 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state,
|
||||
state->crtcs[index] = crtc;
|
||||
crtc_state->state = state;
|
||||
|
||||
DRM_DEBUG_KMS("Added [CRTC:%d] %p state to %p\n",
|
||||
crtc->base.id, crtc_state, state);
|
||||
DRM_DEBUG_ATOMIC("Added [CRTC:%d] %p state to %p\n",
|
||||
crtc->base.id, crtc_state, state);
|
||||
|
||||
return crtc_state;
|
||||
}
|
||||
@ -248,11 +254,14 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
|
||||
/* FIXME: Mode prop is missing, which also controls ->enable. */
|
||||
if (property == config->prop_active) {
|
||||
if (property == config->prop_active)
|
||||
state->active = val;
|
||||
} else if (crtc->funcs->atomic_set_property)
|
||||
else if (crtc->funcs->atomic_set_property)
|
||||
return crtc->funcs->atomic_set_property(crtc, state, property, val);
|
||||
return -EINVAL;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_crtc_set_property);
|
||||
|
||||
@ -266,9 +275,17 @@ int drm_atomic_crtc_get_property(struct drm_crtc *crtc,
|
||||
const struct drm_crtc_state *state,
|
||||
struct drm_property *property, uint64_t *val)
|
||||
{
|
||||
if (crtc->funcs->atomic_get_property)
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
|
||||
if (property == config->prop_active)
|
||||
*val = state->active;
|
||||
else if (crtc->funcs->atomic_get_property)
|
||||
return crtc->funcs->atomic_get_property(crtc, state, property, val);
|
||||
return -EINVAL;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -293,8 +310,8 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
|
||||
*/
|
||||
|
||||
if (state->active && !state->enable) {
|
||||
DRM_DEBUG_KMS("[CRTC:%d] active without enabled\n",
|
||||
crtc->base.id);
|
||||
DRM_DEBUG_ATOMIC("[CRTC:%d] active without enabled\n",
|
||||
crtc->base.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -340,8 +357,8 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
|
||||
state->planes[index] = plane;
|
||||
plane_state->state = state;
|
||||
|
||||
DRM_DEBUG_KMS("Added [PLANE:%d] %p state to %p\n",
|
||||
plane->base.id, plane_state, state);
|
||||
DRM_DEBUG_ATOMIC("Added [PLANE:%d] %p state to %p\n",
|
||||
plane->base.id, plane_state, state);
|
||||
|
||||
if (plane_state->crtc) {
|
||||
struct drm_crtc_state *crtc_state;
|
||||
@ -450,6 +467,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
|
||||
*val = state->src_w;
|
||||
} else if (property == config->prop_src_h) {
|
||||
*val = state->src_h;
|
||||
} else if (property == config->rotation_property) {
|
||||
*val = state->rotation;
|
||||
} else if (plane->funcs->atomic_get_property) {
|
||||
return plane->funcs->atomic_get_property(plane, state, property, val);
|
||||
} else {
|
||||
@ -473,14 +492,14 @@ static int drm_atomic_plane_check(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
unsigned int fb_width, fb_height;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
/* either *both* CRTC and FB must be set, or neither */
|
||||
if (WARN_ON(state->crtc && !state->fb)) {
|
||||
DRM_DEBUG_KMS("CRTC set but no FB\n");
|
||||
DRM_DEBUG_ATOMIC("CRTC set but no FB\n");
|
||||
return -EINVAL;
|
||||
} else if (WARN_ON(state->fb && !state->crtc)) {
|
||||
DRM_DEBUG_KMS("FB set but no CRTC\n");
|
||||
DRM_DEBUG_ATOMIC("FB set but no CRTC\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -490,18 +509,16 @@ static int drm_atomic_plane_check(struct drm_plane *plane,
|
||||
|
||||
/* Check whether this plane is usable on this CRTC */
|
||||
if (!(plane->possible_crtcs & drm_crtc_mask(state->crtc))) {
|
||||
DRM_DEBUG_KMS("Invalid crtc for plane\n");
|
||||
DRM_DEBUG_ATOMIC("Invalid crtc for plane\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check whether this plane supports the fb pixel format. */
|
||||
for (i = 0; i < plane->format_count; i++)
|
||||
if (state->fb->pixel_format == plane->format_types[i])
|
||||
break;
|
||||
if (i == plane->format_count) {
|
||||
DRM_DEBUG_KMS("Invalid pixel format %s\n",
|
||||
drm_get_format_name(state->fb->pixel_format));
|
||||
return -EINVAL;
|
||||
ret = drm_plane_check_pixel_format(plane, state->fb->pixel_format);
|
||||
if (ret) {
|
||||
DRM_DEBUG_ATOMIC("Invalid pixel format %s\n",
|
||||
drm_get_format_name(state->fb->pixel_format));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Give drivers some help against integer overflows */
|
||||
@ -509,9 +526,9 @@ static int drm_atomic_plane_check(struct drm_plane *plane,
|
||||
state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
|
||||
state->crtc_h > INT_MAX ||
|
||||
state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
|
||||
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
|
||||
state->crtc_w, state->crtc_h,
|
||||
state->crtc_x, state->crtc_y);
|
||||
DRM_DEBUG_ATOMIC("Invalid CRTC coordinates %ux%u+%d+%d\n",
|
||||
state->crtc_w, state->crtc_h,
|
||||
state->crtc_x, state->crtc_y);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
@ -523,12 +540,12 @@ static int drm_atomic_plane_check(struct drm_plane *plane,
|
||||
state->src_x > fb_width - state->src_w ||
|
||||
state->src_h > fb_height ||
|
||||
state->src_y > fb_height - state->src_h) {
|
||||
DRM_DEBUG_KMS("Invalid source coordinates "
|
||||
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
|
||||
state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10,
|
||||
state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10,
|
||||
state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10,
|
||||
state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10);
|
||||
DRM_DEBUG_ATOMIC("Invalid source coordinates "
|
||||
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
|
||||
state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10,
|
||||
state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10,
|
||||
state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10,
|
||||
state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
@ -575,7 +592,7 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state,
|
||||
* at most the array is a bit too large.
|
||||
*/
|
||||
if (index >= state->num_connector) {
|
||||
DRM_DEBUG_KMS("Hot-added connector would overflow state array, restarting\n");
|
||||
DRM_DEBUG_ATOMIC("Hot-added connector would overflow state array, restarting\n");
|
||||
return ERR_PTR(-EAGAIN);
|
||||
}
|
||||
|
||||
@ -590,8 +607,8 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state,
|
||||
state->connectors[index] = connector;
|
||||
connector_state->state = state;
|
||||
|
||||
DRM_DEBUG_KMS("Added [CONNECTOR:%d] %p state to %p\n",
|
||||
connector->base.id, connector_state, state);
|
||||
DRM_DEBUG_ATOMIC("Added [CONNECTOR:%d] %p state to %p\n",
|
||||
connector->base.id, connector_state, state);
|
||||
|
||||
if (connector_state->crtc) {
|
||||
struct drm_crtc_state *crtc_state;
|
||||
@ -752,17 +769,18 @@ drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state,
|
||||
}
|
||||
|
||||
if (crtc)
|
||||
DRM_DEBUG_KMS("Link plane state %p to [CRTC:%d]\n",
|
||||
plane_state, crtc->base.id);
|
||||
DRM_DEBUG_ATOMIC("Link plane state %p to [CRTC:%d]\n",
|
||||
plane_state, crtc->base.id);
|
||||
else
|
||||
DRM_DEBUG_KMS("Link plane state %p to [NOCRTC]\n", plane_state);
|
||||
DRM_DEBUG_ATOMIC("Link plane state %p to [NOCRTC]\n",
|
||||
plane_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_set_crtc_for_plane);
|
||||
|
||||
/**
|
||||
* drm_atomic_set_fb_for_plane - set crtc for plane
|
||||
* drm_atomic_set_fb_for_plane - set framebuffer for plane
|
||||
* @plane_state: atomic state object for the plane
|
||||
* @fb: fb to use for the plane
|
||||
*
|
||||
@ -782,10 +800,11 @@ drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state,
|
||||
plane_state->fb = fb;
|
||||
|
||||
if (fb)
|
||||
DRM_DEBUG_KMS("Set [FB:%d] for plane state %p\n",
|
||||
fb->base.id, plane_state);
|
||||
DRM_DEBUG_ATOMIC("Set [FB:%d] for plane state %p\n",
|
||||
fb->base.id, plane_state);
|
||||
else
|
||||
DRM_DEBUG_KMS("Set [NOFB] for plane state %p\n", plane_state);
|
||||
DRM_DEBUG_ATOMIC("Set [NOFB] for plane state %p\n",
|
||||
plane_state);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_set_fb_for_plane);
|
||||
|
||||
@ -818,11 +837,11 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,
|
||||
conn_state->crtc = crtc;
|
||||
|
||||
if (crtc)
|
||||
DRM_DEBUG_KMS("Link connector state %p to [CRTC:%d]\n",
|
||||
conn_state, crtc->base.id);
|
||||
DRM_DEBUG_ATOMIC("Link connector state %p to [CRTC:%d]\n",
|
||||
conn_state, crtc->base.id);
|
||||
else
|
||||
DRM_DEBUG_KMS("Link connector state %p to [NOCRTC]\n",
|
||||
conn_state);
|
||||
DRM_DEBUG_ATOMIC("Link connector state %p to [NOCRTC]\n",
|
||||
conn_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -858,8 +877,8 @@ drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DRM_DEBUG_KMS("Adding all current connectors for [CRTC:%d] to %p\n",
|
||||
crtc->base.id, state);
|
||||
DRM_DEBUG_ATOMIC("Adding all current connectors for [CRTC:%d] to %p\n",
|
||||
crtc->base.id, state);
|
||||
|
||||
/*
|
||||
* Changed connectors are already in @state, so only need to look at the
|
||||
@ -890,19 +909,18 @@ int
|
||||
drm_atomic_connectors_for_crtc(struct drm_atomic_state *state,
|
||||
struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_state *conn_state;
|
||||
|
||||
int i, num_connected_connectors = 0;
|
||||
|
||||
for (i = 0; i < state->num_connector; i++) {
|
||||
struct drm_connector_state *conn_state;
|
||||
|
||||
conn_state = state->connector_states[i];
|
||||
|
||||
if (conn_state && conn_state->crtc == crtc)
|
||||
for_each_connector_in_state(state, connector, conn_state, i) {
|
||||
if (conn_state->crtc == crtc)
|
||||
num_connected_connectors++;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("State %p has %i connectors for [CRTC:%d]\n",
|
||||
state, num_connected_connectors, crtc->base.id);
|
||||
DRM_DEBUG_ATOMIC("State %p has %i connectors for [CRTC:%d]\n",
|
||||
state, num_connected_connectors, crtc->base.id);
|
||||
|
||||
return num_connected_connectors;
|
||||
}
|
||||
@ -914,7 +932,7 @@ EXPORT_SYMBOL(drm_atomic_connectors_for_crtc);
|
||||
*
|
||||
* This function should be used by legacy entry points which don't understand
|
||||
* -EDEADLK semantics. For simplicity this one will grab all modeset locks after
|
||||
* the slowpath completed.
|
||||
* the slowpath completed.
|
||||
*/
|
||||
void drm_atomic_legacy_backoff(struct drm_atomic_state *state)
|
||||
{
|
||||
@ -949,36 +967,28 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *dev = state->dev;
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
int nplanes = config->num_total_plane;
|
||||
int ncrtcs = config->num_crtc;
|
||||
struct drm_plane *plane;
|
||||
struct drm_plane_state *plane_state;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
int i, ret = 0;
|
||||
|
||||
DRM_DEBUG_KMS("checking %p\n", state);
|
||||
DRM_DEBUG_ATOMIC("checking %p\n", state);
|
||||
|
||||
for (i = 0; i < nplanes; i++) {
|
||||
struct drm_plane *plane = state->planes[i];
|
||||
|
||||
if (!plane)
|
||||
continue;
|
||||
|
||||
ret = drm_atomic_plane_check(plane, state->plane_states[i]);
|
||||
for_each_plane_in_state(state, plane, plane_state, i) {
|
||||
ret = drm_atomic_plane_check(plane, plane_state);
|
||||
if (ret) {
|
||||
DRM_DEBUG_KMS("[PLANE:%d] atomic core check failed\n",
|
||||
plane->base.id);
|
||||
DRM_DEBUG_ATOMIC("[PLANE:%d] atomic core check failed\n",
|
||||
plane->base.id);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ncrtcs; i++) {
|
||||
struct drm_crtc *crtc = state->crtcs[i];
|
||||
|
||||
if (!crtc)
|
||||
continue;
|
||||
|
||||
ret = drm_atomic_crtc_check(crtc, state->crtc_states[i]);
|
||||
for_each_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
ret = drm_atomic_crtc_check(crtc, crtc_state);
|
||||
if (ret) {
|
||||
DRM_DEBUG_KMS("[CRTC:%d] atomic core check failed\n",
|
||||
crtc->base.id);
|
||||
DRM_DEBUG_ATOMIC("[CRTC:%d] atomic core check failed\n",
|
||||
crtc->base.id);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -987,17 +997,11 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
|
||||
ret = config->funcs->atomic_check(state->dev, state);
|
||||
|
||||
if (!state->allow_modeset) {
|
||||
for (i = 0; i < ncrtcs; i++) {
|
||||
struct drm_crtc *crtc = state->crtcs[i];
|
||||
struct drm_crtc_state *crtc_state = state->crtc_states[i];
|
||||
|
||||
if (!crtc)
|
||||
continue;
|
||||
|
||||
for_each_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
if (crtc_state->mode_changed ||
|
||||
crtc_state->active_changed) {
|
||||
DRM_DEBUG_KMS("[CRTC:%d] requires full modeset\n",
|
||||
crtc->base.id);
|
||||
DRM_DEBUG_ATOMIC("[CRTC:%d] requires full modeset\n",
|
||||
crtc->base.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@ -1032,7 +1036,7 @@ int drm_atomic_commit(struct drm_atomic_state *state)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DRM_DEBUG_KMS("commiting %p\n", state);
|
||||
DRM_DEBUG_ATOMIC("commiting %p\n", state);
|
||||
|
||||
return config->funcs->atomic_commit(state->dev, state, false);
|
||||
}
|
||||
@ -1063,7 +1067,7 @@ int drm_atomic_async_commit(struct drm_atomic_state *state)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DRM_DEBUG_KMS("commiting %p asynchronously\n", state);
|
||||
DRM_DEBUG_ATOMIC("commiting %p asynchronously\n", state);
|
||||
|
||||
return config->funcs->atomic_commit(state->dev, state, true);
|
||||
}
|
||||
@ -1191,6 +1195,8 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_plane *plane;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
unsigned plane_mask = 0;
|
||||
int ret = 0;
|
||||
unsigned int i, j;
|
||||
@ -1294,15 +1300,9 @@ retry:
|
||||
}
|
||||
|
||||
if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
|
||||
int ncrtcs = dev->mode_config.num_crtc;
|
||||
|
||||
for (i = 0; i < ncrtcs; i++) {
|
||||
struct drm_crtc_state *crtc_state = state->crtc_states[i];
|
||||
for_each_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
struct drm_pending_vblank_event *e;
|
||||
|
||||
if (!crtc_state)
|
||||
continue;
|
||||
|
||||
e = create_vblank_event(dev, file_priv, arg->user_data);
|
||||
if (!e) {
|
||||
ret = -ENOMEM;
|
||||
@ -1354,14 +1354,7 @@ fail:
|
||||
goto backoff;
|
||||
|
||||
if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
|
||||
int ncrtcs = dev->mode_config.num_crtc;
|
||||
|
||||
for (i = 0; i < ncrtcs; i++) {
|
||||
struct drm_crtc_state *crtc_state = state->crtc_states[i];
|
||||
|
||||
if (!crtc_state)
|
||||
continue;
|
||||
|
||||
for_each_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
destroy_vblank_event(dev, file_priv, crtc_state->event);
|
||||
crtc_state->event = NULL;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -49,7 +49,7 @@ void drm_bridge_remove(struct drm_bridge *bridge)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_bridge_remove);
|
||||
|
||||
extern int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge)
|
||||
int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge)
|
||||
{
|
||||
if (!dev || !bridge)
|
||||
return -EINVAL;
|
||||
|
@ -660,6 +660,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
int ret;
|
||||
|
||||
WARN_ON(primary && primary->type != DRM_PLANE_TYPE_PRIMARY);
|
||||
WARN_ON(cursor && cursor->type != DRM_PLANE_TYPE_CURSOR);
|
||||
|
||||
crtc->dev = dev;
|
||||
crtc->funcs = funcs;
|
||||
crtc->invert_dimensions = false;
|
||||
@ -1999,21 +2002,32 @@ int drm_mode_getcrtc(struct drm_device *dev,
|
||||
return -ENOENT;
|
||||
|
||||
drm_modeset_lock_crtc(crtc, crtc->primary);
|
||||
crtc_resp->x = crtc->x;
|
||||
crtc_resp->y = crtc->y;
|
||||
crtc_resp->gamma_size = crtc->gamma_size;
|
||||
if (crtc->primary->fb)
|
||||
crtc_resp->fb_id = crtc->primary->fb->base.id;
|
||||
else
|
||||
crtc_resp->fb_id = 0;
|
||||
|
||||
if (crtc->enabled) {
|
||||
|
||||
drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode);
|
||||
crtc_resp->mode_valid = 1;
|
||||
if (crtc->state) {
|
||||
crtc_resp->x = crtc->primary->state->src_x >> 16;
|
||||
crtc_resp->y = crtc->primary->state->src_y >> 16;
|
||||
if (crtc->state->enable) {
|
||||
drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->state->mode);
|
||||
crtc_resp->mode_valid = 1;
|
||||
|
||||
} else {
|
||||
crtc_resp->mode_valid = 0;
|
||||
}
|
||||
} else {
|
||||
crtc_resp->mode_valid = 0;
|
||||
crtc_resp->x = crtc->x;
|
||||
crtc_resp->y = crtc->y;
|
||||
if (crtc->enabled) {
|
||||
drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode);
|
||||
crtc_resp->mode_valid = 1;
|
||||
|
||||
} else {
|
||||
crtc_resp->mode_valid = 0;
|
||||
}
|
||||
}
|
||||
drm_modeset_unlock_crtc(crtc);
|
||||
|
||||
@ -2266,8 +2280,6 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
|
||||
crtc = drm_encoder_get_crtc(encoder);
|
||||
if (crtc)
|
||||
enc_resp->crtc_id = crtc->base.id;
|
||||
else if (encoder->crtc)
|
||||
enc_resp->crtc_id = encoder->crtc->base.id;
|
||||
else
|
||||
enc_resp->crtc_id = 0;
|
||||
drm_modeset_unlock(&dev->mode_config.connection_mutex);
|
||||
@ -2402,6 +2414,27 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_plane_check_pixel_format - Check if the plane supports the pixel format
|
||||
* @plane: plane to check for format support
|
||||
* @format: the pixel format
|
||||
*
|
||||
* Returns:
|
||||
* Zero of @plane has @format in its list of supported pixel formats, -EINVAL
|
||||
* otherwise.
|
||||
*/
|
||||
int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < plane->format_count; i++) {
|
||||
if (format == plane->format_types[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* setplane_internal - setplane handler for internal callers
|
||||
*
|
||||
@ -2422,7 +2455,6 @@ static int __setplane_internal(struct drm_plane *plane,
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int fb_width, fb_height;
|
||||
unsigned int i;
|
||||
|
||||
/* No fb means shut it down */
|
||||
if (!fb) {
|
||||
@ -2445,16 +2477,24 @@ static int __setplane_internal(struct drm_plane *plane,
|
||||
}
|
||||
|
||||
/* Check whether this plane supports the fb pixel format. */
|
||||
for (i = 0; i < plane->format_count; i++)
|
||||
if (fb->pixel_format == plane->format_types[i])
|
||||
break;
|
||||
if (i == plane->format_count) {
|
||||
ret = drm_plane_check_pixel_format(plane, fb->pixel_format);
|
||||
if (ret) {
|
||||
DRM_DEBUG_KMS("Invalid pixel format %s\n",
|
||||
drm_get_format_name(fb->pixel_format));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Give drivers some help against integer overflows */
|
||||
if (crtc_w > INT_MAX ||
|
||||
crtc_x > INT_MAX - (int32_t) crtc_w ||
|
||||
crtc_h > INT_MAX ||
|
||||
crtc_y > INT_MAX - (int32_t) crtc_h) {
|
||||
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
|
||||
crtc_w, crtc_h, crtc_x, crtc_y);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
|
||||
fb_width = fb->width << 16;
|
||||
fb_height = fb->height << 16;
|
||||
|
||||
@ -2539,17 +2579,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
/* Give drivers some help against integer overflows */
|
||||
if (plane_req->crtc_w > INT_MAX ||
|
||||
plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
|
||||
plane_req->crtc_h > INT_MAX ||
|
||||
plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
|
||||
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
|
||||
plane_req->crtc_w, plane_req->crtc_h,
|
||||
plane_req->crtc_x, plane_req->crtc_y);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* First, find the plane, crtc, and fb objects. If not available,
|
||||
* we don't bother to call the driver.
|
||||
@ -2775,6 +2804,23 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
||||
|
||||
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
|
||||
|
||||
/*
|
||||
* Check whether the primary plane supports the fb pixel format.
|
||||
* Drivers not implementing the universal planes API use a
|
||||
* default formats list provided by the DRM core which doesn't
|
||||
* match real hardware capabilities. Skip the check in that
|
||||
* case.
|
||||
*/
|
||||
if (!crtc->primary->format_default) {
|
||||
ret = drm_plane_check_pixel_format(crtc->primary,
|
||||
fb->pixel_format);
|
||||
if (ret) {
|
||||
DRM_DEBUG_KMS("Invalid pixel format %s\n",
|
||||
drm_get_format_name(fb->pixel_format));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
|
||||
mode, fb);
|
||||
if (ret)
|
||||
@ -3252,6 +3298,12 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
|
||||
DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
|
||||
DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
|
||||
r->modifier[i], i);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -3266,7 +3318,7 @@ internal_framebuffer_create(struct drm_device *dev,
|
||||
struct drm_framebuffer *fb;
|
||||
int ret;
|
||||
|
||||
if (r->flags & ~DRM_MODE_FB_INTERLACED) {
|
||||
if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) {
|
||||
DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
@ -3282,6 +3334,12 @@ internal_framebuffer_create(struct drm_device *dev,
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (r->flags & DRM_MODE_FB_MODIFIERS &&
|
||||
!dev->mode_config.allow_fb_modifiers) {
|
||||
DRM_DEBUG_KMS("driver does not support fb modifiers\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
ret = framebuffer_check(r);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
@ -5543,6 +5601,7 @@ struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev,
|
||||
mutex_unlock(&dev->mode_config.idr_mutex);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_get_tile_group);
|
||||
|
||||
/**
|
||||
* drm_mode_create_tile_group - create a tile group from a displayid description
|
||||
@ -5581,3 +5640,4 @@ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,
|
||||
mutex_unlock(&dev->mode_config.idr_mutex);
|
||||
return tg;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_create_tile_group);
|
||||
|
@ -161,7 +161,7 @@ EXPORT_SYMBOL(drm_helper_crtc_in_use);
|
||||
static void
|
||||
drm_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
|
||||
const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
|
||||
|
||||
if (encoder->bridge)
|
||||
encoder->bridge->funcs->disable(encoder->bridge);
|
||||
@ -191,7 +191,7 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev)
|
||||
}
|
||||
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
crtc->enabled = drm_helper_crtc_in_use(crtc);
|
||||
if (!crtc->enabled) {
|
||||
if (crtc_funcs->disable)
|
||||
@ -229,7 +229,7 @@ EXPORT_SYMBOL(drm_helper_disable_unused_functions);
|
||||
static void
|
||||
drm_crtc_prepare_encoders(struct drm_device *dev)
|
||||
{
|
||||
struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
const struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
@ -270,9 +270,9 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_display_mode *adjusted_mode, saved_mode;
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
const struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
int saved_x, saved_y;
|
||||
bool saved_enabled;
|
||||
struct drm_encoder *encoder;
|
||||
@ -292,6 +292,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
|
||||
}
|
||||
|
||||
saved_mode = crtc->mode;
|
||||
saved_hwmode = crtc->hwmode;
|
||||
saved_x = crtc->x;
|
||||
saved_y = crtc->y;
|
||||
|
||||
@ -334,6 +335,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
|
||||
}
|
||||
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
|
||||
|
||||
crtc->hwmode = *adjusted_mode;
|
||||
|
||||
/* Prepare the encoders and CRTCs before setting the mode. */
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
|
||||
@ -396,9 +399,6 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
|
||||
encoder->bridge->funcs->enable(encoder->bridge);
|
||||
}
|
||||
|
||||
/* Store real post-adjustment hardware mode. */
|
||||
crtc->hwmode = *adjusted_mode;
|
||||
|
||||
/* Calculate and store various constants which
|
||||
* are later needed by vblank and swap-completion
|
||||
* timestamping. They are derived from true hwmode.
|
||||
@ -411,6 +411,7 @@ done:
|
||||
if (!ret) {
|
||||
crtc->enabled = saved_enabled;
|
||||
crtc->mode = saved_mode;
|
||||
crtc->hwmode = saved_hwmode;
|
||||
crtc->x = saved_x;
|
||||
crtc->y = saved_y;
|
||||
}
|
||||
@ -472,7 +473,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
|
||||
bool fb_changed = false; /* if true and !mode_changed just do a flip */
|
||||
struct drm_connector *save_connectors, *connector;
|
||||
int count = 0, ro, fail = 0;
|
||||
struct drm_crtc_helper_funcs *crtc_funcs;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs;
|
||||
struct drm_mode_set save_set;
|
||||
int ret;
|
||||
int i;
|
||||
@ -572,7 +573,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
|
||||
/* a) traverse passed in connector list and get encoders for them */
|
||||
count = 0;
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
struct drm_connector_helper_funcs *connector_funcs =
|
||||
const struct drm_connector_helper_funcs *connector_funcs =
|
||||
connector->helper_private;
|
||||
new_encoder = connector->encoder;
|
||||
for (ro = 0; ro < set->num_connectors; ro++) {
|
||||
@ -732,7 +733,7 @@ static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder)
|
||||
static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct drm_bridge *bridge = encoder->bridge;
|
||||
struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
const struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
|
||||
if (bridge) {
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
@ -794,7 +795,7 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
|
||||
/* from off to on, do crtc then encoder */
|
||||
if (mode < old_dpms) {
|
||||
if (crtc) {
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
if (crtc_funcs->dpms)
|
||||
(*crtc_funcs->dpms) (crtc,
|
||||
drm_helper_choose_crtc_dpms(crtc));
|
||||
@ -808,7 +809,7 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
|
||||
if (encoder)
|
||||
drm_helper_encoder_dpms(encoder, encoder_dpms);
|
||||
if (crtc) {
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
if (crtc_funcs->dpms)
|
||||
(*crtc_funcs->dpms) (crtc,
|
||||
drm_helper_choose_crtc_dpms(crtc));
|
||||
@ -837,6 +838,7 @@ void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
|
||||
for (i = 0; i < 4; i++) {
|
||||
fb->pitches[i] = mode_cmd->pitches[i];
|
||||
fb->offsets[i] = mode_cmd->offsets[i];
|
||||
fb->modifier[i] = mode_cmd->modifier[i];
|
||||
}
|
||||
drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
|
||||
&fb->bits_per_pixel);
|
||||
@ -869,7 +871,7 @@ void drm_helper_resume_force_mode(struct drm_device *dev)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_crtc_helper_funcs *crtc_funcs;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs;
|
||||
int encoder_dpms;
|
||||
bool ret;
|
||||
|
||||
@ -934,7 +936,7 @@ int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mod
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
int ret;
|
||||
|
||||
if (crtc->funcs->atomic_duplicate_state)
|
||||
|
@ -427,11 +427,13 @@ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
|
||||
* retrying the transaction as appropriate. It is assumed that the
|
||||
* aux->transfer function does not modify anything in the msg other than the
|
||||
* reply field.
|
||||
*
|
||||
* Returns bytes transferred on success, or a negative error code on failure.
|
||||
*/
|
||||
static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
unsigned int retry;
|
||||
int err;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device
|
||||
@ -440,14 +442,14 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
*/
|
||||
for (retry = 0; retry < 7; retry++) {
|
||||
mutex_lock(&aux->hw_mutex);
|
||||
err = aux->transfer(aux, msg);
|
||||
ret = aux->transfer(aux, msg);
|
||||
mutex_unlock(&aux->hw_mutex);
|
||||
if (err < 0) {
|
||||
if (err == -EBUSY)
|
||||
if (ret < 0) {
|
||||
if (ret == -EBUSY)
|
||||
continue;
|
||||
|
||||
DRM_DEBUG_KMS("transaction failed: %d\n", err);
|
||||
return err;
|
||||
DRM_DEBUG_KMS("transaction failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -460,7 +462,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
break;
|
||||
|
||||
case DP_AUX_NATIVE_REPLY_NACK:
|
||||
DRM_DEBUG_KMS("native nack\n");
|
||||
DRM_DEBUG_KMS("native nack (result=%d, size=%zu)\n", ret, msg->size);
|
||||
return -EREMOTEIO;
|
||||
|
||||
case DP_AUX_NATIVE_REPLY_DEFER:
|
||||
@ -488,12 +490,10 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
* Both native ACK and I2C ACK replies received. We
|
||||
* can assume the transfer was successful.
|
||||
*/
|
||||
if (err < msg->size)
|
||||
return -EPROTO;
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
case DP_AUX_I2C_REPLY_NACK:
|
||||
DRM_DEBUG_KMS("I2C nack\n");
|
||||
DRM_DEBUG_KMS("I2C nack (result=%d, size=%zu\n", ret, msg->size);
|
||||
aux->i2c_nack_count++;
|
||||
return -EREMOTEIO;
|
||||
|
||||
@ -513,14 +513,55 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep retrying drm_dp_i2c_do_msg until all data has been transferred.
|
||||
*
|
||||
* Returns an error code on failure, or a recommended transfer size on success.
|
||||
*/
|
||||
static int drm_dp_i2c_drain_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *orig_msg)
|
||||
{
|
||||
int err, ret = orig_msg->size;
|
||||
struct drm_dp_aux_msg msg = *orig_msg;
|
||||
|
||||
while (msg.size > 0) {
|
||||
err = drm_dp_i2c_do_msg(aux, &msg);
|
||||
if (err <= 0)
|
||||
return err == 0 ? -EPROTO : err;
|
||||
|
||||
if (err < msg.size && err < ret) {
|
||||
DRM_DEBUG_KMS("Partial I2C reply: requested %zu bytes got %d bytes\n",
|
||||
msg.size, err);
|
||||
ret = err;
|
||||
}
|
||||
|
||||
msg.size -= err;
|
||||
msg.buffer += err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bizlink designed DP->DVI-D Dual Link adapters require the I2C over AUX
|
||||
* packets to be as large as possible. If not, the I2C transactions never
|
||||
* succeed. Hence the default is maximum.
|
||||
*/
|
||||
static int dp_aux_i2c_transfer_size __read_mostly = DP_AUX_MAX_PAYLOAD_BYTES;
|
||||
module_param_unsafe(dp_aux_i2c_transfer_size, int, 0644);
|
||||
MODULE_PARM_DESC(dp_aux_i2c_transfer_size,
|
||||
"Number of bytes to transfer in a single I2C over DP AUX CH message, (1-16, default 16)");
|
||||
|
||||
static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct drm_dp_aux *aux = adapter->algo_data;
|
||||
unsigned int i, j;
|
||||
unsigned transfer_size;
|
||||
struct drm_dp_aux_msg msg;
|
||||
int err = 0;
|
||||
|
||||
dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES);
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
@ -538,20 +579,19 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
|
||||
err = drm_dp_i2c_do_msg(aux, &msg);
|
||||
if (err < 0)
|
||||
break;
|
||||
/*
|
||||
* Many hardware implementations support FIFOs larger than a
|
||||
* single byte, but it has been empirically determined that
|
||||
* transferring data in larger chunks can actually lead to
|
||||
* decreased performance. Therefore each message is simply
|
||||
* transferred byte-by-byte.
|
||||
/* We want each transaction to be as large as possible, but
|
||||
* we'll go to smaller sizes if the hardware gives us a
|
||||
* short reply.
|
||||
*/
|
||||
for (j = 0; j < msgs[i].len; j++) {
|
||||
transfer_size = dp_aux_i2c_transfer_size;
|
||||
for (j = 0; j < msgs[i].len; j += msg.size) {
|
||||
msg.buffer = msgs[i].buf + j;
|
||||
msg.size = 1;
|
||||
msg.size = min(transfer_size, msgs[i].len - j);
|
||||
|
||||
err = drm_dp_i2c_do_msg(aux, &msg);
|
||||
err = drm_dp_i2c_drain_msg(aux, &msg);
|
||||
if (err < 0)
|
||||
break;
|
||||
transfer_size = err;
|
||||
}
|
||||
if (err < 0)
|
||||
break;
|
||||
|
@ -2324,6 +2324,19 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi);
|
||||
|
||||
int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
|
||||
{
|
||||
int slots = 0;
|
||||
port = drm_dp_get_validated_port_ref(mgr, port);
|
||||
if (!port)
|
||||
return slots;
|
||||
|
||||
slots = port->vcpi.num_slots;
|
||||
drm_dp_put_port(port);
|
||||
return slots;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_mst_get_vcpi_slots);
|
||||
|
||||
/**
|
||||
* drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI
|
||||
* @mgr: manager for this port
|
||||
|
@ -70,7 +70,7 @@ void drm_err(const char *format, ...)
|
||||
vaf.fmt = format;
|
||||
vaf.va = &args;
|
||||
|
||||
printk(KERN_ERR "[" DRM_NAME ":%pf] *ERROR* %pV",
|
||||
printk(KERN_ERR "[" DRM_NAME ":%ps] *ERROR* %pV",
|
||||
__builtin_return_address(0), &vaf);
|
||||
|
||||
va_end(args);
|
||||
|
@ -304,7 +304,7 @@ static int drm_fbdev_cma_create(struct drm_fb_helper *helper,
|
||||
}
|
||||
|
||||
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
|
||||
drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
|
||||
drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
|
||||
|
||||
offset = fbi->var.xoffset * bytes_per_pixel;
|
||||
offset += fbi->var.yoffset * fb->pitches[0];
|
||||
|
@ -238,7 +238,7 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
|
||||
int drm_fb_helper_debug_enter(struct fb_info *info)
|
||||
{
|
||||
struct drm_fb_helper *helper = info->par;
|
||||
struct drm_crtc_helper_funcs *funcs;
|
||||
const struct drm_crtc_helper_funcs *funcs;
|
||||
int i;
|
||||
|
||||
list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
|
||||
@ -285,7 +285,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
|
||||
{
|
||||
struct drm_fb_helper *helper = info->par;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_helper_funcs *funcs;
|
||||
const struct drm_crtc_helper_funcs *funcs;
|
||||
struct drm_framebuffer *fb;
|
||||
int i;
|
||||
|
||||
@ -765,7 +765,7 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = info->par;
|
||||
struct drm_device *dev = fb_helper->dev;
|
||||
struct drm_crtc_helper_funcs *crtc_funcs;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs;
|
||||
u16 *red, *green, *blue, *transp;
|
||||
struct drm_crtc *crtc;
|
||||
int i, j, rc = 0;
|
||||
@ -1034,23 +1034,45 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
|
||||
crtc_count = 0;
|
||||
for (i = 0; i < fb_helper->crtc_count; i++) {
|
||||
struct drm_display_mode *desired_mode;
|
||||
int x, y;
|
||||
struct drm_mode_set *mode_set;
|
||||
int x, y, j;
|
||||
/* in case of tile group, are we the last tile vert or horiz?
|
||||
* If no tile group you are always the last one both vertically
|
||||
* and horizontally
|
||||
*/
|
||||
bool lastv = true, lasth = true;
|
||||
|
||||
desired_mode = fb_helper->crtc_info[i].desired_mode;
|
||||
mode_set = &fb_helper->crtc_info[i].mode_set;
|
||||
|
||||
if (!desired_mode)
|
||||
continue;
|
||||
|
||||
crtc_count++;
|
||||
|
||||
x = fb_helper->crtc_info[i].x;
|
||||
y = fb_helper->crtc_info[i].y;
|
||||
if (desired_mode) {
|
||||
if (gamma_size == 0)
|
||||
gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
|
||||
if (desired_mode->hdisplay + x < sizes.fb_width)
|
||||
sizes.fb_width = desired_mode->hdisplay + x;
|
||||
if (desired_mode->vdisplay + y < sizes.fb_height)
|
||||
sizes.fb_height = desired_mode->vdisplay + y;
|
||||
if (desired_mode->hdisplay + x > sizes.surface_width)
|
||||
sizes.surface_width = desired_mode->hdisplay + x;
|
||||
if (desired_mode->vdisplay + y > sizes.surface_height)
|
||||
sizes.surface_height = desired_mode->vdisplay + y;
|
||||
crtc_count++;
|
||||
|
||||
if (gamma_size == 0)
|
||||
gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
|
||||
|
||||
sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width);
|
||||
sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height);
|
||||
|
||||
for (j = 0; j < mode_set->num_connectors; j++) {
|
||||
struct drm_connector *connector = mode_set->connectors[j];
|
||||
if (connector->has_tile) {
|
||||
lasth = (connector->tile_h_loc == (connector->num_h_tile - 1));
|
||||
lastv = (connector->tile_v_loc == (connector->num_v_tile - 1));
|
||||
/* cloning to multiple tiles is just crazy-talk, so: */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lasth)
|
||||
sizes.fb_width = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width);
|
||||
if (lastv)
|
||||
sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);
|
||||
}
|
||||
|
||||
if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
|
||||
@ -1261,12 +1283,12 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f
|
||||
int width, int height)
|
||||
{
|
||||
struct drm_cmdline_mode *cmdline_mode;
|
||||
struct drm_display_mode *mode = NULL;
|
||||
struct drm_display_mode *mode;
|
||||
bool prefer_non_interlace;
|
||||
|
||||
cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
|
||||
if (cmdline_mode->specified == false)
|
||||
return mode;
|
||||
return NULL;
|
||||
|
||||
/* attempt to find a matching mode in the list of modes
|
||||
* we have gotten so far, if not add a CVT mode that conforms
|
||||
@ -1275,7 +1297,7 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f
|
||||
goto create_mode;
|
||||
|
||||
prefer_non_interlace = !cmdline_mode->interlace;
|
||||
again:
|
||||
again:
|
||||
list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
|
||||
/* check width/height */
|
||||
if (mode->hdisplay != cmdline_mode->xres ||
|
||||
@ -1529,7 +1551,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
|
||||
int c, o;
|
||||
struct drm_device *dev = fb_helper->dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_helper_funcs *connector_funcs;
|
||||
const struct drm_connector_helper_funcs *connector_funcs;
|
||||
struct drm_encoder *encoder;
|
||||
int my_score, best_score, score;
|
||||
struct drm_fb_helper_crtc **crtcs, *crtc;
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_gem.h>
|
||||
|
||||
#include "drm_internal.h"
|
||||
#include "drm_legacy.h"
|
||||
|
||||
/**
|
||||
|
@ -1016,7 +1016,7 @@ static int compat_drm_wait_vblank(struct file *file, unsigned int cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
drm_ioctl_compat_t *drm_compat_ioctls[] = {
|
||||
static drm_ioctl_compat_t *drm_compat_ioctls[] = {
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_VERSION32)] = compat_drm_version,
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE32)] = compat_drm_getunique,
|
||||
[DRM_IOCTL_NR(DRM_IOCTL_GET_MAP32)] = compat_drm_getmap,
|
||||
|
@ -321,6 +321,9 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_
|
||||
else
|
||||
req->value = 64;
|
||||
break;
|
||||
case DRM_CAP_ADDFB2_MODIFIERS:
|
||||
req->value = dev->mode_config.allow_fb_modifiers;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -521,8 +524,13 @@ static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DRM_IOCTL_DEF(ioctl, _func, _flags) \
|
||||
[DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0, .name = #ioctl}
|
||||
#define DRM_IOCTL_DEF(ioctl, _func, _flags) \
|
||||
[DRM_IOCTL_NR(ioctl)] = { \
|
||||
.cmd = ioctl, \
|
||||
.func = _func, \
|
||||
.flags = _flags, \
|
||||
.name = #ioctl \
|
||||
}
|
||||
|
||||
/** Ioctl table */
|
||||
static const struct drm_ioctl_desc drm_ioctls[] = {
|
||||
@ -660,39 +668,29 @@ long drm_ioctl(struct file *filp,
|
||||
int retcode = -EINVAL;
|
||||
char stack_kdata[128];
|
||||
char *kdata = NULL;
|
||||
unsigned int usize, asize;
|
||||
unsigned int usize, asize, drv_size;
|
||||
|
||||
dev = file_priv->minor->dev;
|
||||
|
||||
if (drm_device_is_unplugged(dev))
|
||||
return -ENODEV;
|
||||
|
||||
if ((nr >= DRM_CORE_IOCTL_COUNT) &&
|
||||
((nr < DRM_COMMAND_BASE) || (nr >= DRM_COMMAND_END)))
|
||||
goto err_i1;
|
||||
if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) &&
|
||||
(nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) {
|
||||
u32 drv_size;
|
||||
if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
|
||||
/* driver ioctl */
|
||||
if (nr - DRM_COMMAND_BASE >= dev->driver->num_ioctls)
|
||||
goto err_i1;
|
||||
ioctl = &dev->driver->ioctls[nr - DRM_COMMAND_BASE];
|
||||
drv_size = _IOC_SIZE(ioctl->cmd_drv);
|
||||
usize = asize = _IOC_SIZE(cmd);
|
||||
if (drv_size > asize)
|
||||
asize = drv_size;
|
||||
cmd = ioctl->cmd_drv;
|
||||
}
|
||||
else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) {
|
||||
u32 drv_size;
|
||||
|
||||
} else {
|
||||
/* core ioctl */
|
||||
if (nr >= DRM_CORE_IOCTL_COUNT)
|
||||
goto err_i1;
|
||||
ioctl = &drm_ioctls[nr];
|
||||
}
|
||||
|
||||
drv_size = _IOC_SIZE(ioctl->cmd);
|
||||
usize = asize = _IOC_SIZE(cmd);
|
||||
if (drv_size > asize)
|
||||
asize = drv_size;
|
||||
|
||||
cmd = ioctl->cmd;
|
||||
} else
|
||||
goto err_i1;
|
||||
drv_size = _IOC_SIZE(ioctl->cmd);
|
||||
usize = _IOC_SIZE(cmd);
|
||||
asize = max(usize, drv_size);
|
||||
cmd = ioctl->cmd;
|
||||
|
||||
DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n",
|
||||
task_pid_nr(current),
|
||||
@ -773,12 +771,13 @@ EXPORT_SYMBOL(drm_ioctl);
|
||||
*/
|
||||
bool drm_ioctl_flags(unsigned int nr, unsigned int *flags)
|
||||
{
|
||||
if ((nr >= DRM_COMMAND_END && nr < DRM_CORE_IOCTL_COUNT) ||
|
||||
(nr < DRM_COMMAND_BASE)) {
|
||||
*flags = drm_ioctls[nr].flags;
|
||||
return true;
|
||||
}
|
||||
if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END)
|
||||
return false;
|
||||
|
||||
return false;
|
||||
if (nr >= DRM_CORE_IOCTL_COUNT)
|
||||
return false;
|
||||
|
||||
*flags = drm_ioctls[nr].flags;
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_ioctl_flags);
|
||||
|
@ -276,7 +276,6 @@ static void vblank_disable_fn(unsigned long arg)
|
||||
void drm_vblank_cleanup(struct drm_device *dev)
|
||||
{
|
||||
int crtc;
|
||||
unsigned long irqflags;
|
||||
|
||||
/* Bail if the driver didn't call drm_vblank_init() */
|
||||
if (dev->num_crtcs == 0)
|
||||
@ -285,11 +284,10 @@ void drm_vblank_cleanup(struct drm_device *dev)
|
||||
for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
|
||||
|
||||
del_timer_sync(&vblank->disable_timer);
|
||||
WARN_ON(vblank->enabled &&
|
||||
drm_core_check_feature(dev, DRIVER_MODESET));
|
||||
|
||||
spin_lock_irqsave(&dev->vbl_lock, irqflags);
|
||||
vblank_disable_and_save(dev, crtc);
|
||||
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
|
||||
del_timer_sync(&vblank->disable_timer);
|
||||
}
|
||||
|
||||
kfree(dev->vblank);
|
||||
@ -475,17 +473,23 @@ int drm_irq_uninstall(struct drm_device *dev)
|
||||
dev->irq_enabled = false;
|
||||
|
||||
/*
|
||||
* Wake up any waiters so they don't hang.
|
||||
* Wake up any waiters so they don't hang. This is just to paper over
|
||||
* isssues for UMS drivers which aren't in full control of their
|
||||
* vblank/irq handling. KMS drivers must ensure that vblanks are all
|
||||
* disabled when uninstalling the irq handler.
|
||||
*/
|
||||
if (dev->num_crtcs) {
|
||||
spin_lock_irqsave(&dev->vbl_lock, irqflags);
|
||||
for (i = 0; i < dev->num_crtcs; i++) {
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[i];
|
||||
|
||||
if (!vblank->enabled)
|
||||
continue;
|
||||
|
||||
WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET));
|
||||
|
||||
vblank_disable_and_save(dev, i);
|
||||
wake_up(&vblank->queue);
|
||||
vblank->enabled = false;
|
||||
vblank->last =
|
||||
dev->driver->get_vblank_counter(dev, i);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
|
||||
}
|
||||
@ -1052,7 +1056,7 @@ EXPORT_SYMBOL(drm_vblank_get);
|
||||
* Acquire a reference count on vblank events to avoid having them disabled
|
||||
* while in use.
|
||||
*
|
||||
* This is the native kms version of drm_vblank_off().
|
||||
* This is the native kms version of drm_vblank_get().
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, nonzero on failure.
|
||||
@ -1232,6 +1236,38 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_vblank_off);
|
||||
|
||||
/**
|
||||
* drm_crtc_vblank_reset - reset vblank state to off on a CRTC
|
||||
* @crtc: CRTC in question
|
||||
*
|
||||
* Drivers can use this function to reset the vblank state to off at load time.
|
||||
* Drivers should use this together with the drm_crtc_vblank_off() and
|
||||
* drm_crtc_vblank_on() functions. The difference compared to
|
||||
* drm_crtc_vblank_off() is that this function doesn't save the vblank counter
|
||||
* and hence doesn't need to call any driver hooks.
|
||||
*/
|
||||
void drm_crtc_vblank_reset(struct drm_crtc *drm_crtc)
|
||||
{
|
||||
struct drm_device *dev = drm_crtc->dev;
|
||||
unsigned long irqflags;
|
||||
int crtc = drm_crtc_index(drm_crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
|
||||
|
||||
spin_lock_irqsave(&dev->vbl_lock, irqflags);
|
||||
/*
|
||||
* Prevent subsequent drm_vblank_get() from enabling the vblank
|
||||
* interrupt by bumping the refcount.
|
||||
*/
|
||||
if (!vblank->inmodeset) {
|
||||
atomic_inc(&vblank->refcount);
|
||||
vblank->inmodeset = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
|
||||
|
||||
WARN_ON(!list_empty(&dev->vblank_event_list));
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_vblank_reset);
|
||||
|
||||
/**
|
||||
* drm_vblank_on - enable vblank events on a CRTC
|
||||
* @dev: DRM device
|
||||
@ -1653,7 +1689,7 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
|
||||
struct timeval tvblank;
|
||||
unsigned long irqflags;
|
||||
|
||||
if (!dev->num_crtcs)
|
||||
if (WARN_ON_ONCE(!dev->num_crtcs))
|
||||
return false;
|
||||
|
||||
if (WARN_ON(crtc >= dev->num_crtcs))
|
||||
|
@ -278,7 +278,7 @@ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay,
|
||||
hblank = drm_mode->hdisplay * hblank_percentage /
|
||||
(100 * HV_FACTOR - hblank_percentage);
|
||||
hblank -= hblank % (2 * CVT_H_GRANULARITY);
|
||||
/* 14. find the total pixes per line */
|
||||
/* 14. find the total pixels per line */
|
||||
drm_mode->htotal = drm_mode->hdisplay + hblank;
|
||||
drm_mode->hsync_end = drm_mode->hdisplay + hblank / 2;
|
||||
drm_mode->hsync_start = drm_mode->hsync_end -
|
||||
@ -903,6 +903,12 @@ EXPORT_SYMBOL(drm_mode_duplicate);
|
||||
*/
|
||||
bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2)
|
||||
{
|
||||
if (!mode1 && !mode2)
|
||||
return true;
|
||||
|
||||
if (!mode1 || !mode2)
|
||||
return false;
|
||||
|
||||
/* do clock check convert to PICOS so fb modes get matched
|
||||
* the same */
|
||||
if (mode1->clock && mode2->clock) {
|
||||
@ -1148,7 +1154,7 @@ EXPORT_SYMBOL(drm_mode_sort);
|
||||
/**
|
||||
* drm_mode_connector_list_update - update the mode list for the connector
|
||||
* @connector: the connector to update
|
||||
* @merge_type_bits: whether to merge or overright type bits.
|
||||
* @merge_type_bits: whether to merge or overwrite type bits
|
||||
*
|
||||
* This moves the modes from the @connector probed_modes list
|
||||
* to the actual mode list. It compares the probed mode against the current
|
||||
@ -1209,7 +1215,7 @@ EXPORT_SYMBOL(drm_mode_connector_list_update);
|
||||
* <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
|
||||
*
|
||||
* The intermediate drm_cmdline_mode structure is required to store additional
|
||||
* options from the command line modline like the force-enabel/disable flag.
|
||||
* options from the command line modline like the force-enable/disable flag.
|
||||
*
|
||||
* Returns:
|
||||
* True if a valid modeline has been parsed, false otherwise.
|
||||
|
@ -43,14 +43,10 @@ static uint32_t drm_crtc_port_mask(struct drm_device *dev,
|
||||
uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
|
||||
struct device_node *port)
|
||||
{
|
||||
struct device_node *remote_port, *ep = NULL;
|
||||
struct device_node *remote_port, *ep;
|
||||
uint32_t possible_crtcs = 0;
|
||||
|
||||
do {
|
||||
ep = of_graph_get_next_endpoint(port, ep);
|
||||
if (!ep)
|
||||
break;
|
||||
|
||||
for_each_endpoint_of_node(port, ep) {
|
||||
remote_port = of_graph_get_remote_port(ep);
|
||||
if (!remote_port) {
|
||||
of_node_put(ep);
|
||||
@ -60,7 +56,7 @@ uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
|
||||
possible_crtcs |= drm_crtc_port_mask(dev, remote_port);
|
||||
|
||||
of_node_put(remote_port);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
return possible_crtcs;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/export.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "drm_internal.h"
|
||||
#include "drm_legacy.h"
|
||||
|
||||
/**
|
||||
|
@ -344,20 +344,7 @@ const struct drm_plane_funcs drm_primary_helper_funcs = {
|
||||
};
|
||||
EXPORT_SYMBOL(drm_primary_helper_funcs);
|
||||
|
||||
/**
|
||||
* drm_primary_helper_create_plane() - Create a generic primary plane
|
||||
* @dev: drm device
|
||||
* @formats: pixel formats supported, or NULL for a default safe list
|
||||
* @num_formats: size of @formats; ignored if @formats is NULL
|
||||
*
|
||||
* Allocates and initializes a primary plane that can be used with the primary
|
||||
* plane helpers. Drivers that wish to use driver-specific plane structures or
|
||||
* provide custom handler functions may perform their own allocation and
|
||||
* initialization rather than calling this function.
|
||||
*/
|
||||
struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev,
|
||||
const uint32_t *formats,
|
||||
int num_formats)
|
||||
static struct drm_plane *create_primary_plane(struct drm_device *dev)
|
||||
{
|
||||
struct drm_plane *primary;
|
||||
int ret;
|
||||
@ -368,15 +355,17 @@ struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (formats == NULL) {
|
||||
formats = safe_modeset_formats;
|
||||
num_formats = ARRAY_SIZE(safe_modeset_formats);
|
||||
}
|
||||
/*
|
||||
* Remove the format_default field from drm_plane when dropping
|
||||
* this helper.
|
||||
*/
|
||||
primary->format_default = true;
|
||||
|
||||
/* possible_crtc's will be filled in later by crtc_init */
|
||||
ret = drm_universal_plane_init(dev, primary, 0,
|
||||
&drm_primary_helper_funcs,
|
||||
formats, num_formats,
|
||||
safe_modeset_formats,
|
||||
ARRAY_SIZE(safe_modeset_formats),
|
||||
DRM_PLANE_TYPE_PRIMARY);
|
||||
if (ret) {
|
||||
kfree(primary);
|
||||
@ -385,7 +374,6 @@ struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev,
|
||||
|
||||
return primary;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_primary_helper_create_plane);
|
||||
|
||||
/**
|
||||
* drm_crtc_init - Legacy CRTC initialization function
|
||||
@ -404,7 +392,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
|
||||
{
|
||||
struct drm_plane *primary;
|
||||
|
||||
primary = drm_primary_helper_create_plane(dev, NULL, 0);
|
||||
primary = create_primary_plane(dev);
|
||||
return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_init);
|
||||
@ -413,9 +401,9 @@ int drm_plane_helper_commit(struct drm_plane *plane,
|
||||
struct drm_plane_state *plane_state,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct drm_plane_helper_funcs *plane_funcs;
|
||||
const struct drm_plane_helper_funcs *plane_funcs;
|
||||
struct drm_crtc *crtc[2];
|
||||
struct drm_crtc_helper_funcs *crtc_funcs[2];
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs[2];
|
||||
int i, ret = 0;
|
||||
|
||||
plane_funcs = plane->helper_private;
|
||||
@ -437,7 +425,8 @@ int drm_plane_helper_commit(struct drm_plane *plane,
|
||||
|
||||
if (plane_funcs->prepare_fb && plane_state->fb &&
|
||||
plane_state->fb != old_fb) {
|
||||
ret = plane_funcs->prepare_fb(plane, plane_state->fb);
|
||||
ret = plane_funcs->prepare_fb(plane, plane_state->fb,
|
||||
plane_state);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
@ -487,7 +476,7 @@ int drm_plane_helper_commit(struct drm_plane *plane,
|
||||
}
|
||||
|
||||
if (plane_funcs->cleanup_fb && old_fb)
|
||||
plane_funcs->cleanup_fb(plane, old_fb);
|
||||
plane_funcs->cleanup_fb(plane, old_fb, plane_state);
|
||||
out:
|
||||
if (plane_state) {
|
||||
if (plane->funcs->atomic_destroy_state)
|
||||
|
@ -98,7 +98,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_display_mode *mode;
|
||||
struct drm_connector_helper_funcs *connector_funcs =
|
||||
const struct drm_connector_helper_funcs *connector_funcs =
|
||||
connector->helper_private;
|
||||
int count = 0;
|
||||
int mode_flags = 0;
|
||||
|
@ -166,23 +166,68 @@ void drm_sysfs_destroy(void)
|
||||
/*
|
||||
* Connector properties
|
||||
*/
|
||||
static ssize_t status_store(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct drm_connector *connector = to_drm_connector(device);
|
||||
struct drm_device *dev = connector->dev;
|
||||
enum drm_connector_status old_status;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->mode_config.mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
old_status = connector->status;
|
||||
|
||||
if (sysfs_streq(buf, "detect")) {
|
||||
connector->force = 0;
|
||||
connector->status = connector->funcs->detect(connector, true);
|
||||
} else if (sysfs_streq(buf, "on")) {
|
||||
connector->force = DRM_FORCE_ON;
|
||||
} else if (sysfs_streq(buf, "on-digital")) {
|
||||
connector->force = DRM_FORCE_ON_DIGITAL;
|
||||
} else if (sysfs_streq(buf, "off")) {
|
||||
connector->force = DRM_FORCE_OFF;
|
||||
} else
|
||||
ret = -EINVAL;
|
||||
|
||||
if (ret == 0 && connector->force) {
|
||||
if (connector->force == DRM_FORCE_ON ||
|
||||
connector->force == DRM_FORCE_ON_DIGITAL)
|
||||
connector->status = connector_status_connected;
|
||||
else
|
||||
connector->status = connector_status_disconnected;
|
||||
if (connector->funcs->force)
|
||||
connector->funcs->force(connector);
|
||||
}
|
||||
|
||||
if (old_status != connector->status) {
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
|
||||
connector->base.id,
|
||||
connector->name,
|
||||
old_status, connector->status);
|
||||
|
||||
dev->mode_config.delayed_event = true;
|
||||
if (dev->mode_config.poll_enabled)
|
||||
schedule_delayed_work(&dev->mode_config.output_poll_work,
|
||||
0);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t status_show(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct drm_connector *connector = to_drm_connector(device);
|
||||
enum drm_connector_status status;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&connector->dev->mode_config.mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
status = connector->funcs->detect(connector, true);
|
||||
mutex_unlock(&connector->dev->mode_config.mutex);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
drm_get_connector_status_name(status));
|
||||
drm_get_connector_status_name(connector->status));
|
||||
}
|
||||
|
||||
static ssize_t dpms_show(struct device *device,
|
||||
@ -339,7 +384,7 @@ static ssize_t select_subconnector_show(struct device *device,
|
||||
drm_get_dvi_i_select_name((int)subconnector));
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(status);
|
||||
static DEVICE_ATTR_RW(status);
|
||||
static DEVICE_ATTR_RO(enabled);
|
||||
static DEVICE_ATTR_RO(dpms);
|
||||
static DEVICE_ATTR_RO(modes);
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include <linux/slab.h>
|
||||
#endif
|
||||
#include <asm/pgtable.h>
|
||||
#include "drm_internal.h"
|
||||
#include "drm_legacy.h"
|
||||
|
||||
struct drm_vma_entry {
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <video/exynos7_decon.h>
|
||||
|
||||
#include "exynos_drm_crtc.h"
|
||||
#include "exynos_drm_plane.h"
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_fbdev.h"
|
||||
#include "exynos_drm_iommu.h"
|
||||
@ -41,32 +42,16 @@
|
||||
|
||||
#define WINDOWS_NR 2
|
||||
|
||||
struct decon_win_data {
|
||||
unsigned int ovl_x;
|
||||
unsigned int ovl_y;
|
||||
unsigned int offset_x;
|
||||
unsigned int offset_y;
|
||||
unsigned int ovl_width;
|
||||
unsigned int ovl_height;
|
||||
unsigned int fb_width;
|
||||
unsigned int fb_height;
|
||||
unsigned int bpp;
|
||||
unsigned int pixel_format;
|
||||
dma_addr_t dma_addr;
|
||||
bool enabled;
|
||||
bool resume;
|
||||
};
|
||||
|
||||
struct decon_context {
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
struct exynos_drm_crtc *crtc;
|
||||
struct exynos_drm_plane planes[WINDOWS_NR];
|
||||
struct clk *pclk;
|
||||
struct clk *aclk;
|
||||
struct clk *eclk;
|
||||
struct clk *vclk;
|
||||
void __iomem *regs;
|
||||
struct decon_win_data win_data[WINDOWS_NR];
|
||||
unsigned int default_win;
|
||||
unsigned long irq_flags;
|
||||
bool i80_if;
|
||||
@ -296,59 +281,16 @@ static void decon_disable_vblank(struct exynos_drm_crtc *crtc)
|
||||
}
|
||||
}
|
||||
|
||||
static void decon_win_mode_set(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
{
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
struct decon_win_data *win_data;
|
||||
int win, padding;
|
||||
|
||||
if (!plane) {
|
||||
DRM_ERROR("plane is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
win = plane->zpos;
|
||||
if (win == DEFAULT_ZPOS)
|
||||
win = ctx->default_win;
|
||||
|
||||
if (win < 0 || win >= WINDOWS_NR)
|
||||
return;
|
||||
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
|
||||
padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width;
|
||||
win_data->offset_x = plane->fb_x;
|
||||
win_data->offset_y = plane->fb_y;
|
||||
win_data->fb_width = plane->fb_width + padding;
|
||||
win_data->fb_height = plane->fb_height;
|
||||
win_data->ovl_x = plane->crtc_x;
|
||||
win_data->ovl_y = plane->crtc_y;
|
||||
win_data->ovl_width = plane->crtc_width;
|
||||
win_data->ovl_height = plane->crtc_height;
|
||||
win_data->dma_addr = plane->dma_addr[0];
|
||||
win_data->bpp = plane->bpp;
|
||||
win_data->pixel_format = plane->pixel_format;
|
||||
|
||||
DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
|
||||
win_data->offset_x, win_data->offset_y);
|
||||
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
|
||||
win_data->ovl_width, win_data->ovl_height);
|
||||
DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
|
||||
DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
|
||||
plane->fb_width, plane->crtc_width);
|
||||
}
|
||||
|
||||
static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
|
||||
{
|
||||
struct decon_win_data *win_data = &ctx->win_data[win];
|
||||
struct exynos_drm_plane *plane = &ctx->planes[win];
|
||||
unsigned long val;
|
||||
int padding;
|
||||
|
||||
val = readl(ctx->regs + WINCON(win));
|
||||
val &= ~WINCONx_BPPMODE_MASK;
|
||||
|
||||
switch (win_data->pixel_format) {
|
||||
switch (plane->pixel_format) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
val |= WINCONx_BPPMODE_16BPP_565;
|
||||
val |= WINCONx_BURSTLEN_16WORD;
|
||||
@ -397,7 +339,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
|
||||
break;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
|
||||
DRM_DEBUG_KMS("bpp = %d\n", plane->bpp);
|
||||
|
||||
/*
|
||||
* In case of exynos, setting dma-burst to 16Word causes permanent
|
||||
@ -407,7 +349,8 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
|
||||
* movement causes unstable DMA which results into iommu crash/tear.
|
||||
*/
|
||||
|
||||
if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
|
||||
padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width;
|
||||
if (plane->fb_width + padding < MIN_FB_WIDTH_FOR_16WORD_BURST) {
|
||||
val &= ~WINCONx_BURSTLEN_MASK;
|
||||
val |= WINCONx_BURSTLEN_8WORD;
|
||||
}
|
||||
@ -435,7 +378,7 @@ static void decon_win_set_colkey(struct decon_context *ctx, unsigned int win)
|
||||
* @protect: 1 to protect (disable updates)
|
||||
*/
|
||||
static void decon_shadow_protect_win(struct decon_context *ctx,
|
||||
int win, bool protect)
|
||||
unsigned int win, bool protect)
|
||||
{
|
||||
u32 bits, val;
|
||||
|
||||
@ -449,12 +392,12 @@ static void decon_shadow_protect_win(struct decon_context *ctx,
|
||||
writel(val, ctx->regs + SHADOWCON);
|
||||
}
|
||||
|
||||
static void decon_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
|
||||
{
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
struct drm_display_mode *mode = &crtc->base.mode;
|
||||
struct decon_win_data *win_data;
|
||||
int win = zpos;
|
||||
struct exynos_drm_plane *plane;
|
||||
int padding;
|
||||
unsigned long val, alpha;
|
||||
unsigned int last_x;
|
||||
unsigned int last_y;
|
||||
@ -462,17 +405,14 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
if (win == DEFAULT_ZPOS)
|
||||
win = ctx->default_win;
|
||||
|
||||
if (win < 0 || win >= WINDOWS_NR)
|
||||
return;
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
plane = &ctx->planes[win];
|
||||
|
||||
/* If suspended, enable this on resume */
|
||||
if (ctx->suspended) {
|
||||
win_data->resume = true;
|
||||
plane->resume = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -490,39 +430,41 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
decon_shadow_protect_win(ctx, win, true);
|
||||
|
||||
/* buffer start address */
|
||||
val = (unsigned long)win_data->dma_addr;
|
||||
val = (unsigned long)plane->dma_addr[0];
|
||||
writel(val, ctx->regs + VIDW_BUF_START(win));
|
||||
|
||||
padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width;
|
||||
|
||||
/* buffer size */
|
||||
writel(win_data->fb_width, ctx->regs + VIDW_WHOLE_X(win));
|
||||
writel(win_data->fb_height, ctx->regs + VIDW_WHOLE_Y(win));
|
||||
writel(plane->fb_width + padding, ctx->regs + VIDW_WHOLE_X(win));
|
||||
writel(plane->fb_height, ctx->regs + VIDW_WHOLE_Y(win));
|
||||
|
||||
/* offset from the start of the buffer to read */
|
||||
writel(win_data->offset_x, ctx->regs + VIDW_OFFSET_X(win));
|
||||
writel(win_data->offset_y, ctx->regs + VIDW_OFFSET_Y(win));
|
||||
writel(plane->src_x, ctx->regs + VIDW_OFFSET_X(win));
|
||||
writel(plane->src_y, ctx->regs + VIDW_OFFSET_Y(win));
|
||||
|
||||
DRM_DEBUG_KMS("start addr = 0x%lx\n",
|
||||
(unsigned long)win_data->dma_addr);
|
||||
(unsigned long)val);
|
||||
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
|
||||
win_data->ovl_width, win_data->ovl_height);
|
||||
plane->crtc_width, plane->crtc_height);
|
||||
|
||||
/*
|
||||
* OSD position.
|
||||
* In case the window layout goes of LCD layout, DECON fails.
|
||||
*/
|
||||
if ((win_data->ovl_x + win_data->ovl_width) > mode->hdisplay)
|
||||
win_data->ovl_x = mode->hdisplay - win_data->ovl_width;
|
||||
if ((win_data->ovl_y + win_data->ovl_height) > mode->vdisplay)
|
||||
win_data->ovl_y = mode->vdisplay - win_data->ovl_height;
|
||||
if ((plane->crtc_x + plane->crtc_width) > mode->hdisplay)
|
||||
plane->crtc_x = mode->hdisplay - plane->crtc_width;
|
||||
if ((plane->crtc_y + plane->crtc_height) > mode->vdisplay)
|
||||
plane->crtc_y = mode->vdisplay - plane->crtc_height;
|
||||
|
||||
val = VIDOSDxA_TOPLEFT_X(win_data->ovl_x) |
|
||||
VIDOSDxA_TOPLEFT_Y(win_data->ovl_y);
|
||||
val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) |
|
||||
VIDOSDxA_TOPLEFT_Y(plane->crtc_y);
|
||||
writel(val, ctx->regs + VIDOSD_A(win));
|
||||
|
||||
last_x = win_data->ovl_x + win_data->ovl_width;
|
||||
last_x = plane->crtc_x + plane->crtc_width;
|
||||
if (last_x)
|
||||
last_x--;
|
||||
last_y = win_data->ovl_y + win_data->ovl_height;
|
||||
last_y = plane->crtc_y + plane->crtc_height;
|
||||
if (last_y)
|
||||
last_y--;
|
||||
|
||||
@ -531,7 +473,7 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
writel(val, ctx->regs + VIDOSD_B(win));
|
||||
|
||||
DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
|
||||
win_data->ovl_x, win_data->ovl_y, last_x, last_y);
|
||||
plane->crtc_x, plane->crtc_y, last_x, last_y);
|
||||
|
||||
/* OSD alpha */
|
||||
alpha = VIDOSDxC_ALPHA0_R_F(0x0) |
|
||||
@ -565,27 +507,23 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
val |= DECON_UPDATE_STANDALONE_F;
|
||||
writel(val, ctx->regs + DECON_UPDATE);
|
||||
|
||||
win_data->enabled = true;
|
||||
plane->enabled = true;
|
||||
}
|
||||
|
||||
static void decon_win_disable(struct exynos_drm_crtc *crtc, int zpos)
|
||||
static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
|
||||
{
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
struct decon_win_data *win_data;
|
||||
int win = zpos;
|
||||
struct exynos_drm_plane *plane;
|
||||
u32 val;
|
||||
|
||||
if (win == DEFAULT_ZPOS)
|
||||
win = ctx->default_win;
|
||||
|
||||
if (win < 0 || win >= WINDOWS_NR)
|
||||
return;
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
plane = &ctx->planes[win];
|
||||
|
||||
if (ctx->suspended) {
|
||||
/* do not resume this window*/
|
||||
win_data->resume = false;
|
||||
plane->resume = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -604,42 +542,42 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, int zpos)
|
||||
val |= DECON_UPDATE_STANDALONE_F;
|
||||
writel(val, ctx->regs + DECON_UPDATE);
|
||||
|
||||
win_data->enabled = false;
|
||||
plane->enabled = false;
|
||||
}
|
||||
|
||||
static void decon_window_suspend(struct decon_context *ctx)
|
||||
{
|
||||
struct decon_win_data *win_data;
|
||||
struct exynos_drm_plane *plane;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WINDOWS_NR; i++) {
|
||||
win_data = &ctx->win_data[i];
|
||||
win_data->resume = win_data->enabled;
|
||||
if (win_data->enabled)
|
||||
plane = &ctx->planes[i];
|
||||
plane->resume = plane->enabled;
|
||||
if (plane->enabled)
|
||||
decon_win_disable(ctx->crtc, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void decon_window_resume(struct decon_context *ctx)
|
||||
{
|
||||
struct decon_win_data *win_data;
|
||||
struct exynos_drm_plane *plane;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WINDOWS_NR; i++) {
|
||||
win_data = &ctx->win_data[i];
|
||||
win_data->enabled = win_data->resume;
|
||||
win_data->resume = false;
|
||||
plane = &ctx->planes[i];
|
||||
plane->enabled = plane->resume;
|
||||
plane->resume = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void decon_apply(struct decon_context *ctx)
|
||||
{
|
||||
struct decon_win_data *win_data;
|
||||
struct exynos_drm_plane *plane;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WINDOWS_NR; i++) {
|
||||
win_data = &ctx->win_data[i];
|
||||
if (win_data->enabled)
|
||||
plane = &ctx->planes[i];
|
||||
if (plane->enabled)
|
||||
decon_win_commit(ctx->crtc, i);
|
||||
else
|
||||
decon_win_disable(ctx->crtc, i);
|
||||
@ -779,7 +717,6 @@ static struct exynos_drm_crtc_ops decon_crtc_ops = {
|
||||
.enable_vblank = decon_enable_vblank,
|
||||
.disable_vblank = decon_disable_vblank,
|
||||
.wait_for_vblank = decon_wait_for_vblank,
|
||||
.win_mode_set = decon_win_mode_set,
|
||||
.win_commit = decon_win_commit,
|
||||
.win_disable = decon_win_disable,
|
||||
};
|
||||
@ -818,6 +755,9 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct decon_context *ctx = dev_get_drvdata(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
struct exynos_drm_plane *exynos_plane;
|
||||
enum drm_plane_type type;
|
||||
unsigned int zpos;
|
||||
int ret;
|
||||
|
||||
ret = decon_ctx_initialize(ctx, drm_dev);
|
||||
@ -826,8 +766,18 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->crtc = exynos_drm_crtc_create(drm_dev, ctx->pipe,
|
||||
EXYNOS_DISPLAY_TYPE_LCD,
|
||||
for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
|
||||
type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
|
||||
DRM_PLANE_TYPE_OVERLAY;
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
|
||||
1 << ctx->pipe, type, zpos);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
exynos_plane = &ctx->planes[ctx->default_win];
|
||||
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
|
||||
ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD,
|
||||
&decon_crtc_ops, ctx);
|
||||
if (IS_ERR(ctx->crtc)) {
|
||||
decon_ctx_remove(ctx);
|
||||
|
@ -32,10 +32,16 @@
|
||||
#include <drm/bridge/ptn3460.h>
|
||||
|
||||
#include "exynos_dp_core.h"
|
||||
#include "exynos_drm_fimd.h"
|
||||
|
||||
#define ctx_from_connector(c) container_of(c, struct exynos_dp_device, \
|
||||
connector)
|
||||
|
||||
static inline struct exynos_drm_crtc *dp_to_crtc(struct exynos_dp_device *dp)
|
||||
{
|
||||
return to_exynos_crtc(dp->encoder->crtc);
|
||||
}
|
||||
|
||||
static inline struct exynos_dp_device *
|
||||
display_to_dp(struct exynos_drm_display *d)
|
||||
{
|
||||
@ -1070,6 +1076,8 @@ static void exynos_dp_poweron(struct exynos_dp_device *dp)
|
||||
}
|
||||
}
|
||||
|
||||
fimd_dp_clock_enable(dp_to_crtc(dp), true);
|
||||
|
||||
clk_prepare_enable(dp->clock);
|
||||
exynos_dp_phy_init(dp);
|
||||
exynos_dp_init_dp(dp);
|
||||
@ -1094,6 +1102,8 @@ static void exynos_dp_poweroff(struct exynos_dp_device *dp)
|
||||
exynos_dp_phy_exit(dp);
|
||||
clk_disable_unprepare(dp->clock);
|
||||
|
||||
fimd_dp_clock_enable(dp_to_crtc(dp), false);
|
||||
|
||||
if (dp->panel) {
|
||||
if (drm_panel_unprepare(dp->panel))
|
||||
DRM_ERROR("failed to turnoff the panel\n");
|
||||
|
@ -34,9 +34,8 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||
if (mode > DRM_MODE_DPMS_ON) {
|
||||
/* wait for the completion of page flip. */
|
||||
if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
|
||||
!atomic_read(&exynos_crtc->pending_flip),
|
||||
HZ/20))
|
||||
atomic_set(&exynos_crtc->pending_flip, 0);
|
||||
(exynos_crtc->event == NULL), HZ/20))
|
||||
exynos_crtc->event = NULL;
|
||||
drm_crtc_vblank_off(crtc);
|
||||
}
|
||||
|
||||
@ -164,11 +163,10 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
|
||||
uint32_t page_flip_flags)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct exynos_drm_private *dev_priv = dev->dev_private;
|
||||
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
|
||||
struct drm_framebuffer *old_fb = crtc->primary->fb;
|
||||
unsigned int crtc_w, crtc_h;
|
||||
int ret = -EINVAL;
|
||||
int ret;
|
||||
|
||||
/* when the page flip is requested, crtc's dpms should be on */
|
||||
if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
|
||||
@ -176,48 +174,49 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
if (!event)
|
||||
return -EINVAL;
|
||||
|
||||
if (event) {
|
||||
/*
|
||||
* the pipe from user always is 0 so we can set pipe number
|
||||
* of current owner to event.
|
||||
*/
|
||||
event->pipe = exynos_crtc->pipe;
|
||||
|
||||
ret = drm_vblank_get(dev, exynos_crtc->pipe);
|
||||
if (ret) {
|
||||
DRM_DEBUG("failed to acquire vblank counter\n");
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_irq(&dev->event_lock);
|
||||
list_add_tail(&event->base.link,
|
||||
&dev_priv->pageflip_event_list);
|
||||
atomic_set(&exynos_crtc->pending_flip, 1);
|
||||
spin_unlock_irq(&dev->event_lock);
|
||||
|
||||
crtc->primary->fb = fb;
|
||||
crtc_w = fb->width - crtc->x;
|
||||
crtc_h = fb->height - crtc->y;
|
||||
ret = exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
|
||||
crtc_w, crtc_h, crtc->x, crtc->y,
|
||||
crtc_w, crtc_h);
|
||||
if (ret) {
|
||||
crtc->primary->fb = old_fb;
|
||||
|
||||
spin_lock_irq(&dev->event_lock);
|
||||
drm_vblank_put(dev, exynos_crtc->pipe);
|
||||
list_del(&event->base.link);
|
||||
atomic_set(&exynos_crtc->pending_flip, 0);
|
||||
spin_unlock_irq(&dev->event_lock);
|
||||
|
||||
goto out;
|
||||
}
|
||||
spin_lock_irq(&dev->event_lock);
|
||||
if (exynos_crtc->event) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = drm_vblank_get(dev, exynos_crtc->pipe);
|
||||
if (ret) {
|
||||
DRM_DEBUG("failed to acquire vblank counter\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
exynos_crtc->event = event;
|
||||
spin_unlock_irq(&dev->event_lock);
|
||||
|
||||
/*
|
||||
* the pipe from user always is 0 so we can set pipe number
|
||||
* of current owner to event.
|
||||
*/
|
||||
event->pipe = exynos_crtc->pipe;
|
||||
|
||||
crtc->primary->fb = fb;
|
||||
crtc_w = fb->width - crtc->x;
|
||||
crtc_h = fb->height - crtc->y;
|
||||
ret = exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
|
||||
crtc_w, crtc_h, crtc->x, crtc->y,
|
||||
crtc_w, crtc_h);
|
||||
if (ret) {
|
||||
crtc->primary->fb = old_fb;
|
||||
spin_lock_irq(&dev->event_lock);
|
||||
exynos_crtc->event = NULL;
|
||||
drm_vblank_put(dev, exynos_crtc->pipe);
|
||||
spin_unlock_irq(&dev->event_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
spin_unlock_irq(&dev->event_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -239,13 +238,13 @@ static struct drm_crtc_funcs exynos_crtc_funcs = {
|
||||
};
|
||||
|
||||
struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
|
||||
struct drm_plane *plane,
|
||||
int pipe,
|
||||
enum exynos_drm_output_type type,
|
||||
struct exynos_drm_crtc_ops *ops,
|
||||
void *ctx)
|
||||
{
|
||||
struct exynos_drm_crtc *exynos_crtc;
|
||||
struct drm_plane *plane;
|
||||
struct exynos_drm_private *private = drm_dev->dev_private;
|
||||
struct drm_crtc *crtc;
|
||||
int ret;
|
||||
@ -255,19 +254,12 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init_waitqueue_head(&exynos_crtc->pending_flip_queue);
|
||||
atomic_set(&exynos_crtc->pending_flip, 0);
|
||||
|
||||
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
|
||||
exynos_crtc->pipe = pipe;
|
||||
exynos_crtc->type = type;
|
||||
exynos_crtc->ops = ops;
|
||||
exynos_crtc->ctx = ctx;
|
||||
plane = exynos_plane_init(drm_dev, 1 << pipe,
|
||||
DRM_PLANE_TYPE_PRIMARY);
|
||||
if (IS_ERR(plane)) {
|
||||
ret = PTR_ERR(plane);
|
||||
goto err_plane;
|
||||
}
|
||||
|
||||
crtc = &exynos_crtc->base;
|
||||
|
||||
@ -284,7 +276,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
|
||||
|
||||
err_crtc:
|
||||
plane->funcs->destroy(plane);
|
||||
err_plane:
|
||||
kfree(exynos_crtc);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
@ -320,26 +311,20 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
|
||||
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
|
||||
{
|
||||
struct exynos_drm_private *dev_priv = dev->dev_private;
|
||||
struct drm_pending_vblank_event *e, *t;
|
||||
struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
|
||||
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
if (exynos_crtc->event) {
|
||||
|
||||
list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
|
||||
base.link) {
|
||||
/* if event's pipe isn't same as crtc then ignore it. */
|
||||
if (pipe != e->pipe)
|
||||
continue;
|
||||
|
||||
list_del(&e->base.link);
|
||||
drm_send_vblank_event(dev, -1, e);
|
||||
drm_send_vblank_event(dev, -1, exynos_crtc->event);
|
||||
drm_vblank_put(dev, pipe);
|
||||
atomic_set(&exynos_crtc->pending_flip, 0);
|
||||
wake_up(&exynos_crtc->pending_flip_queue);
|
||||
|
||||
}
|
||||
|
||||
exynos_crtc->event = NULL;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "exynos_drm_drv.h"
|
||||
|
||||
struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
|
||||
struct drm_plane *plane,
|
||||
int pipe,
|
||||
enum exynos_drm_output_type type,
|
||||
struct exynos_drm_crtc_ops *ops,
|
||||
@ -27,12 +28,6 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
|
||||
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
|
||||
void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb);
|
||||
|
||||
void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane);
|
||||
void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos);
|
||||
void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos);
|
||||
void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
|
||||
|
||||
/* This function gets pipe value to crtc device matched with out_type. */
|
||||
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
|
||||
unsigned int out_type);
|
||||
|
@ -55,13 +55,11 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
{
|
||||
struct exynos_drm_private *private;
|
||||
int ret;
|
||||
int nr;
|
||||
|
||||
private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL);
|
||||
if (!private)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&private->pageflip_event_list);
|
||||
dev_set_drvdata(dev->dev, dev);
|
||||
dev->dev_private = (void *)private;
|
||||
|
||||
@ -81,19 +79,6 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
|
||||
exynos_drm_mode_config_init(dev);
|
||||
|
||||
for (nr = 0; nr < MAX_PLANE; nr++) {
|
||||
struct drm_plane *plane;
|
||||
unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
|
||||
|
||||
plane = exynos_plane_init(dev, possible_crtcs,
|
||||
DRM_PLANE_TYPE_OVERLAY);
|
||||
if (!IS_ERR(plane))
|
||||
continue;
|
||||
|
||||
ret = PTR_ERR(plane);
|
||||
goto err_mode_config_cleanup;
|
||||
}
|
||||
|
||||
/* setup possible_clones. */
|
||||
exynos_drm_encoder_setup(dev);
|
||||
|
||||
@ -237,25 +222,13 @@ static void exynos_drm_preclose(struct drm_device *dev,
|
||||
|
||||
static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct exynos_drm_private *private = dev->dev_private;
|
||||
struct drm_pending_vblank_event *v, *vt;
|
||||
struct drm_pending_event *e, *et;
|
||||
unsigned long flags;
|
||||
|
||||
if (!file->driver_priv)
|
||||
return;
|
||||
|
||||
/* Release all events not unhandled by page flip handler. */
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
list_for_each_entry_safe(v, vt, &private->pageflip_event_list,
|
||||
base.link) {
|
||||
if (v->base.file_priv == file) {
|
||||
list_del(&v->base.link);
|
||||
drm_vblank_put(dev, v->pipe);
|
||||
v->base.destroy(&v->base);
|
||||
}
|
||||
}
|
||||
|
||||
/* Release all events handled by page flip handler but not freed. */
|
||||
list_for_each_entry_safe(e, et, &file->event_list, link) {
|
||||
list_del(&e->link);
|
||||
|
@ -21,7 +21,6 @@
|
||||
#define MAX_CRTC 3
|
||||
#define MAX_PLANE 5
|
||||
#define MAX_FB_BUFFER 4
|
||||
#define DEFAULT_ZPOS -1
|
||||
|
||||
#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc, base)
|
||||
#define to_exynos_plane(x) container_of(x, struct exynos_drm_plane, base)
|
||||
@ -48,20 +47,22 @@ enum exynos_drm_output_type {
|
||||
* Exynos drm common overlay structure.
|
||||
*
|
||||
* @base: plane object
|
||||
* @fb_x: offset x on a framebuffer to be displayed.
|
||||
* @src_x: offset x on a framebuffer to be displayed.
|
||||
* - the unit is screen coordinates.
|
||||
* @fb_y: offset y on a framebuffer to be displayed.
|
||||
* @src_y: offset y on a framebuffer to be displayed.
|
||||
* - the unit is screen coordinates.
|
||||
* @fb_width: width of a framebuffer.
|
||||
* @fb_height: height of a framebuffer.
|
||||
* @src_width: width of a partial image to be displayed from framebuffer.
|
||||
* @src_height: height of a partial image to be displayed from framebuffer.
|
||||
* @fb_width: width of a framebuffer.
|
||||
* @fb_height: height of a framebuffer.
|
||||
* @crtc_x: offset x on hardware screen.
|
||||
* @crtc_y: offset y on hardware screen.
|
||||
* @crtc_width: window width to be displayed (hardware screen).
|
||||
* @crtc_height: window height to be displayed (hardware screen).
|
||||
* @mode_width: width of screen mode.
|
||||
* @mode_height: height of screen mode.
|
||||
* @h_ratio: horizontal scaling ratio, 16.16 fixed point
|
||||
* @v_ratio: vertical scaling ratio, 16.16 fixed point
|
||||
* @refresh: refresh rate.
|
||||
* @scan_flag: interlace or progressive way.
|
||||
* (it could be DRM_MODE_FLAG_*)
|
||||
@ -78,6 +79,7 @@ enum exynos_drm_output_type {
|
||||
* @transparency: transparency on or off.
|
||||
* @activated: activated or not.
|
||||
* @enabled: enabled or not.
|
||||
* @resume: to resume or not.
|
||||
*
|
||||
* this structure is common to exynos SoC and its contents would be copied
|
||||
* to hardware specific overlay info.
|
||||
@ -85,25 +87,27 @@ enum exynos_drm_output_type {
|
||||
|
||||
struct exynos_drm_plane {
|
||||
struct drm_plane base;
|
||||
unsigned int fb_x;
|
||||
unsigned int fb_y;
|
||||
unsigned int fb_width;
|
||||
unsigned int fb_height;
|
||||
unsigned int src_x;
|
||||
unsigned int src_y;
|
||||
unsigned int src_width;
|
||||
unsigned int src_height;
|
||||
unsigned int fb_width;
|
||||
unsigned int fb_height;
|
||||
unsigned int crtc_x;
|
||||
unsigned int crtc_y;
|
||||
unsigned int crtc_width;
|
||||
unsigned int crtc_height;
|
||||
unsigned int mode_width;
|
||||
unsigned int mode_height;
|
||||
unsigned int h_ratio;
|
||||
unsigned int v_ratio;
|
||||
unsigned int refresh;
|
||||
unsigned int scan_flag;
|
||||
unsigned int bpp;
|
||||
unsigned int pitch;
|
||||
uint32_t pixel_format;
|
||||
dma_addr_t dma_addr[MAX_FB_BUFFER];
|
||||
int zpos;
|
||||
unsigned int zpos;
|
||||
unsigned int index_color;
|
||||
|
||||
bool default_win:1;
|
||||
@ -112,6 +116,7 @@ struct exynos_drm_plane {
|
||||
bool transparency:1;
|
||||
bool activated:1;
|
||||
bool enabled:1;
|
||||
bool resume:1;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -172,9 +177,7 @@ struct exynos_drm_display {
|
||||
* @disable_vblank: specific driver callback for disabling vblank interrupt.
|
||||
* @wait_for_vblank: wait for vblank interrupt to make sure that
|
||||
* hardware overlay is updated.
|
||||
* @win_mode_set: copy drm overlay info to hw specific overlay info.
|
||||
* @win_commit: apply hardware specific overlay data to registers.
|
||||
* @win_enable: enable hardware specific overlay.
|
||||
* @win_disable: disable hardware specific overlay.
|
||||
* @te_handler: trigger to transfer video image at the tearing effect
|
||||
* synchronization signal if there is a page flip request.
|
||||
@ -189,11 +192,8 @@ struct exynos_drm_crtc_ops {
|
||||
int (*enable_vblank)(struct exynos_drm_crtc *crtc);
|
||||
void (*disable_vblank)(struct exynos_drm_crtc *crtc);
|
||||
void (*wait_for_vblank)(struct exynos_drm_crtc *crtc);
|
||||
void (*win_mode_set)(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane);
|
||||
void (*win_commit)(struct exynos_drm_crtc *crtc, int zpos);
|
||||
void (*win_enable)(struct exynos_drm_crtc *crtc, int zpos);
|
||||
void (*win_disable)(struct exynos_drm_crtc *crtc, int zpos);
|
||||
void (*win_commit)(struct exynos_drm_crtc *crtc, unsigned int zpos);
|
||||
void (*win_disable)(struct exynos_drm_crtc *crtc, unsigned int zpos);
|
||||
void (*te_handler)(struct exynos_drm_crtc *crtc);
|
||||
};
|
||||
|
||||
@ -210,6 +210,7 @@ struct exynos_drm_crtc_ops {
|
||||
* we can refer to the crtc to current hardware interrupt occurred through
|
||||
* this pipe value.
|
||||
* @dpms: store the crtc dpms value
|
||||
* @event: vblank event that is currently queued for flip
|
||||
* @ops: pointer to callbacks for exynos drm specific functionality
|
||||
* @ctx: A pointer to the crtc's implementation specific context
|
||||
*/
|
||||
@ -219,7 +220,7 @@ struct exynos_drm_crtc {
|
||||
unsigned int pipe;
|
||||
unsigned int dpms;
|
||||
wait_queue_head_t pending_flip_queue;
|
||||
atomic_t pending_flip;
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct exynos_drm_crtc_ops *ops;
|
||||
void *ctx;
|
||||
};
|
||||
@ -249,9 +250,6 @@ struct drm_exynos_file_private {
|
||||
struct exynos_drm_private {
|
||||
struct drm_fb_helper *fb_helper;
|
||||
|
||||
/* list head for new event to be added. */
|
||||
struct list_head pageflip_event_list;
|
||||
|
||||
/*
|
||||
* created crtc object would be contained at this array and
|
||||
* this array is used to be aware of which crtc did it request vblank.
|
||||
|
@ -1473,12 +1473,6 @@ static int exynos_dsi_get_modes(struct drm_connector *connector)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_dsi_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static struct drm_encoder *
|
||||
exynos_dsi_best_encoder(struct drm_connector *connector)
|
||||
{
|
||||
@ -1489,7 +1483,6 @@ exynos_dsi_best_encoder(struct drm_connector *connector)
|
||||
|
||||
static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
|
||||
.get_modes = exynos_dsi_get_modes,
|
||||
.mode_valid = exynos_dsi_mode_valid,
|
||||
.best_encoder = exynos_dsi_best_encoder,
|
||||
};
|
||||
|
||||
|
@ -151,10 +151,8 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
|
||||
exynos_gem_obj = to_exynos_gem_obj(obj);
|
||||
|
||||
ret = check_fb_gem_memory_type(dev, exynos_gem_obj);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("cannot use this gem memory type for fb.\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL);
|
||||
if (!exynos_fb)
|
||||
@ -250,10 +248,8 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
|
||||
exynos_fb->exynos_gem_obj[i] = exynos_gem_obj;
|
||||
|
||||
ret = check_fb_gem_memory_type(dev, exynos_gem_obj);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("cannot use this gem memory type for fb.\n");
|
||||
if (ret < 0)
|
||||
goto err_unreference;
|
||||
}
|
||||
}
|
||||
|
||||
ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs);
|
||||
|
@ -76,6 +76,7 @@ static struct fb_ops exynos_drm_fb_ops = {
|
||||
};
|
||||
|
||||
static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
|
||||
struct drm_fb_helper_surface_size *sizes,
|
||||
struct drm_framebuffer *fb)
|
||||
{
|
||||
struct fb_info *fbi = helper->fbdev;
|
||||
@ -85,7 +86,7 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
|
||||
unsigned long offset;
|
||||
|
||||
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
|
||||
drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
|
||||
drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
|
||||
|
||||
/* RGB formats use only one buffer */
|
||||
buffer = exynos_drm_fb_buffer(fb, 0);
|
||||
@ -189,7 +190,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
|
||||
goto err_destroy_framebuffer;
|
||||
}
|
||||
|
||||
ret = exynos_drm_fbdev_update(helper, helper->fb);
|
||||
ret = exynos_drm_fbdev_update(helper, sizes, helper->fb);
|
||||
if (ret < 0)
|
||||
goto err_dealloc_cmap;
|
||||
|
||||
|
@ -31,7 +31,9 @@
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_fbdev.h"
|
||||
#include "exynos_drm_crtc.h"
|
||||
#include "exynos_drm_plane.h"
|
||||
#include "exynos_drm_iommu.h"
|
||||
#include "exynos_drm_fimd.h"
|
||||
|
||||
/*
|
||||
* FIMD stands for Fully Interactive Mobile Display and
|
||||
@ -54,6 +56,9 @@
|
||||
/* size control register for hardware windows 1 ~ 2. */
|
||||
#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16)
|
||||
|
||||
#define VIDWnALPHA0(win) (VIDW_ALPHA + 0x00 + (win) * 8)
|
||||
#define VIDWnALPHA1(win) (VIDW_ALPHA + 0x04 + (win) * 8)
|
||||
|
||||
#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
|
||||
#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
|
||||
#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
|
||||
@ -140,32 +145,15 @@ static struct fimd_driver_data exynos5_fimd_driver_data = {
|
||||
.has_vtsel = 1,
|
||||
};
|
||||
|
||||
struct fimd_win_data {
|
||||
unsigned int offset_x;
|
||||
unsigned int offset_y;
|
||||
unsigned int ovl_width;
|
||||
unsigned int ovl_height;
|
||||
unsigned int fb_width;
|
||||
unsigned int fb_height;
|
||||
unsigned int fb_pitch;
|
||||
unsigned int bpp;
|
||||
unsigned int pixel_format;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned int buf_offsize;
|
||||
unsigned int line_size; /* bytes */
|
||||
bool enabled;
|
||||
bool resume;
|
||||
};
|
||||
|
||||
struct fimd_context {
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
struct exynos_drm_crtc *crtc;
|
||||
struct exynos_drm_plane planes[WINDOWS_NR];
|
||||
struct clk *bus_clk;
|
||||
struct clk *lcd_clk;
|
||||
void __iomem *regs;
|
||||
struct regmap *sysreg;
|
||||
struct fimd_win_data win_data[WINDOWS_NR];
|
||||
unsigned int default_win;
|
||||
unsigned long irq_flags;
|
||||
u32 vidcon0;
|
||||
@ -502,59 +490,9 @@ static void fimd_disable_vblank(struct exynos_drm_crtc *crtc)
|
||||
}
|
||||
}
|
||||
|
||||
static void fimd_win_mode_set(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
{
|
||||
struct fimd_context *ctx = crtc->ctx;
|
||||
struct fimd_win_data *win_data;
|
||||
int win;
|
||||
unsigned long offset;
|
||||
|
||||
if (!plane) {
|
||||
DRM_ERROR("plane is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
win = plane->zpos;
|
||||
if (win == DEFAULT_ZPOS)
|
||||
win = ctx->default_win;
|
||||
|
||||
if (win < 0 || win >= WINDOWS_NR)
|
||||
return;
|
||||
|
||||
offset = plane->fb_x * (plane->bpp >> 3);
|
||||
offset += plane->fb_y * plane->pitch;
|
||||
|
||||
DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, plane->pitch);
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
|
||||
win_data->offset_x = plane->crtc_x;
|
||||
win_data->offset_y = plane->crtc_y;
|
||||
win_data->ovl_width = plane->crtc_width;
|
||||
win_data->ovl_height = plane->crtc_height;
|
||||
win_data->fb_pitch = plane->pitch;
|
||||
win_data->fb_width = plane->fb_width;
|
||||
win_data->fb_height = plane->fb_height;
|
||||
win_data->dma_addr = plane->dma_addr[0] + offset;
|
||||
win_data->bpp = plane->bpp;
|
||||
win_data->pixel_format = plane->pixel_format;
|
||||
win_data->buf_offsize =
|
||||
plane->pitch - (plane->crtc_width * (plane->bpp >> 3));
|
||||
win_data->line_size = plane->crtc_width * (plane->bpp >> 3);
|
||||
|
||||
DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
|
||||
win_data->offset_x, win_data->offset_y);
|
||||
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
|
||||
win_data->ovl_width, win_data->ovl_height);
|
||||
DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
|
||||
DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
|
||||
plane->fb_width, plane->crtc_width);
|
||||
}
|
||||
|
||||
static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
|
||||
{
|
||||
struct fimd_win_data *win_data = &ctx->win_data[win];
|
||||
struct exynos_drm_plane *plane = &ctx->planes[win];
|
||||
unsigned long val;
|
||||
|
||||
val = WINCONx_ENWIN;
|
||||
@ -564,11 +502,11 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
|
||||
* So the request format is ARGB8888 then change it to XRGB8888.
|
||||
*/
|
||||
if (ctx->driver_data->has_limited_fmt && !win) {
|
||||
if (win_data->pixel_format == DRM_FORMAT_ARGB8888)
|
||||
win_data->pixel_format = DRM_FORMAT_XRGB8888;
|
||||
if (plane->pixel_format == DRM_FORMAT_ARGB8888)
|
||||
plane->pixel_format = DRM_FORMAT_XRGB8888;
|
||||
}
|
||||
|
||||
switch (win_data->pixel_format) {
|
||||
switch (plane->pixel_format) {
|
||||
case DRM_FORMAT_C8:
|
||||
val |= WINCON0_BPPMODE_8BPP_PALETTE;
|
||||
val |= WINCONx_BURSTLEN_8WORD;
|
||||
@ -604,7 +542,7 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
|
||||
break;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
|
||||
DRM_DEBUG_KMS("bpp = %d\n", plane->bpp);
|
||||
|
||||
/*
|
||||
* In case of exynos, setting dma-burst to 16Word causes permanent
|
||||
@ -614,12 +552,30 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
|
||||
* movement causes unstable DMA which results into iommu crash/tear.
|
||||
*/
|
||||
|
||||
if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
|
||||
if (plane->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
|
||||
val &= ~WINCONx_BURSTLEN_MASK;
|
||||
val |= WINCONx_BURSTLEN_4WORD;
|
||||
}
|
||||
|
||||
writel(val, ctx->regs + WINCON(win));
|
||||
|
||||
/* hardware window 0 doesn't support alpha channel. */
|
||||
if (win != 0) {
|
||||
/* OSD alpha */
|
||||
val = VIDISD14C_ALPHA0_R(0xf) |
|
||||
VIDISD14C_ALPHA0_G(0xf) |
|
||||
VIDISD14C_ALPHA0_B(0xf) |
|
||||
VIDISD14C_ALPHA1_R(0xf) |
|
||||
VIDISD14C_ALPHA1_G(0xf) |
|
||||
VIDISD14C_ALPHA1_B(0xf);
|
||||
|
||||
writel(val, ctx->regs + VIDOSD_C(win));
|
||||
|
||||
val = VIDW_ALPHA_R(0xf) | VIDW_ALPHA_G(0xf) |
|
||||
VIDW_ALPHA_G(0xf);
|
||||
writel(val, ctx->regs + VIDWnALPHA0(win));
|
||||
writel(val, ctx->regs + VIDWnALPHA1(win));
|
||||
}
|
||||
}
|
||||
|
||||
static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
|
||||
@ -642,7 +598,7 @@ static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
|
||||
* @protect: 1 to protect (disable updates)
|
||||
*/
|
||||
static void fimd_shadow_protect_win(struct fimd_context *ctx,
|
||||
int win, bool protect)
|
||||
unsigned int win, bool protect)
|
||||
{
|
||||
u32 reg, bits, val;
|
||||
|
||||
@ -662,29 +618,25 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,
|
||||
writel(val, ctx->regs + reg);
|
||||
}
|
||||
|
||||
static void fimd_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
|
||||
{
|
||||
struct fimd_context *ctx = crtc->ctx;
|
||||
struct fimd_win_data *win_data;
|
||||
int win = zpos;
|
||||
unsigned long val, alpha, size;
|
||||
unsigned int last_x;
|
||||
unsigned int last_y;
|
||||
struct exynos_drm_plane *plane;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned long val, size, offset;
|
||||
unsigned int last_x, last_y, buf_offsize, line_size;
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
if (win == DEFAULT_ZPOS)
|
||||
win = ctx->default_win;
|
||||
|
||||
if (win < 0 || win >= WINDOWS_NR)
|
||||
return;
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
plane = &ctx->planes[win];
|
||||
|
||||
/* If suspended, enable this on resume */
|
||||
if (ctx->suspended) {
|
||||
win_data->resume = true;
|
||||
plane->resume = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -701,38 +653,45 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
/* protect windows */
|
||||
fimd_shadow_protect_win(ctx, win, true);
|
||||
|
||||
|
||||
offset = plane->src_x * (plane->bpp >> 3);
|
||||
offset += plane->src_y * plane->pitch;
|
||||
|
||||
/* buffer start address */
|
||||
val = (unsigned long)win_data->dma_addr;
|
||||
dma_addr = plane->dma_addr[0] + offset;
|
||||
val = (unsigned long)dma_addr;
|
||||
writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
|
||||
|
||||
/* buffer end address */
|
||||
size = win_data->fb_pitch * win_data->ovl_height * (win_data->bpp >> 3);
|
||||
val = (unsigned long)(win_data->dma_addr + size);
|
||||
size = plane->pitch * plane->crtc_height;
|
||||
val = (unsigned long)(dma_addr + size);
|
||||
writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
|
||||
|
||||
DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
|
||||
(unsigned long)win_data->dma_addr, val, size);
|
||||
(unsigned long)dma_addr, val, size);
|
||||
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
|
||||
win_data->ovl_width, win_data->ovl_height);
|
||||
plane->crtc_width, plane->crtc_height);
|
||||
|
||||
/* buffer size */
|
||||
val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
|
||||
VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) |
|
||||
VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) |
|
||||
VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size);
|
||||
buf_offsize = plane->pitch - (plane->crtc_width * (plane->bpp >> 3));
|
||||
line_size = plane->crtc_width * (plane->bpp >> 3);
|
||||
val = VIDW_BUF_SIZE_OFFSET(buf_offsize) |
|
||||
VIDW_BUF_SIZE_PAGEWIDTH(line_size) |
|
||||
VIDW_BUF_SIZE_OFFSET_E(buf_offsize) |
|
||||
VIDW_BUF_SIZE_PAGEWIDTH_E(line_size);
|
||||
writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
|
||||
|
||||
/* OSD position */
|
||||
val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
|
||||
VIDOSDxA_TOPLEFT_Y(win_data->offset_y) |
|
||||
VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) |
|
||||
VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y);
|
||||
val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) |
|
||||
VIDOSDxA_TOPLEFT_Y(plane->crtc_y) |
|
||||
VIDOSDxA_TOPLEFT_X_E(plane->crtc_x) |
|
||||
VIDOSDxA_TOPLEFT_Y_E(plane->crtc_y);
|
||||
writel(val, ctx->regs + VIDOSD_A(win));
|
||||
|
||||
last_x = win_data->offset_x + win_data->ovl_width;
|
||||
last_x = plane->crtc_x + plane->crtc_width;
|
||||
if (last_x)
|
||||
last_x--;
|
||||
last_y = win_data->offset_y + win_data->ovl_height;
|
||||
last_y = plane->crtc_y + plane->crtc_height;
|
||||
if (last_y)
|
||||
last_y--;
|
||||
|
||||
@ -742,24 +701,14 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
writel(val, ctx->regs + VIDOSD_B(win));
|
||||
|
||||
DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
|
||||
win_data->offset_x, win_data->offset_y, last_x, last_y);
|
||||
|
||||
/* hardware window 0 doesn't support alpha channel. */
|
||||
if (win != 0) {
|
||||
/* OSD alpha */
|
||||
alpha = VIDISD14C_ALPHA1_R(0xf) |
|
||||
VIDISD14C_ALPHA1_G(0xf) |
|
||||
VIDISD14C_ALPHA1_B(0xf);
|
||||
|
||||
writel(alpha, ctx->regs + VIDOSD_C(win));
|
||||
}
|
||||
plane->crtc_x, plane->crtc_y, last_x, last_y);
|
||||
|
||||
/* OSD size */
|
||||
if (win != 3 && win != 4) {
|
||||
u32 offset = VIDOSD_D(win);
|
||||
if (win == 0)
|
||||
offset = VIDOSD_C(win);
|
||||
val = win_data->ovl_width * win_data->ovl_height;
|
||||
val = plane->crtc_width * plane->crtc_height;
|
||||
writel(val, ctx->regs + offset);
|
||||
|
||||
DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
|
||||
@ -779,29 +728,25 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
/* Enable DMA channel and unprotect windows */
|
||||
fimd_shadow_protect_win(ctx, win, false);
|
||||
|
||||
win_data->enabled = true;
|
||||
plane->enabled = true;
|
||||
|
||||
if (ctx->i80_if)
|
||||
atomic_set(&ctx->win_updated, 1);
|
||||
}
|
||||
|
||||
static void fimd_win_disable(struct exynos_drm_crtc *crtc, int zpos)
|
||||
static void fimd_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
|
||||
{
|
||||
struct fimd_context *ctx = crtc->ctx;
|
||||
struct fimd_win_data *win_data;
|
||||
int win = zpos;
|
||||
|
||||
if (win == DEFAULT_ZPOS)
|
||||
win = ctx->default_win;
|
||||
struct exynos_drm_plane *plane;
|
||||
|
||||
if (win < 0 || win >= WINDOWS_NR)
|
||||
return;
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
plane = &ctx->planes[win];
|
||||
|
||||
if (ctx->suspended) {
|
||||
/* do not resume this window*/
|
||||
win_data->resume = false;
|
||||
plane->resume = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -816,42 +761,42 @@ static void fimd_win_disable(struct exynos_drm_crtc *crtc, int zpos)
|
||||
/* unprotect windows */
|
||||
fimd_shadow_protect_win(ctx, win, false);
|
||||
|
||||
win_data->enabled = false;
|
||||
plane->enabled = false;
|
||||
}
|
||||
|
||||
static void fimd_window_suspend(struct fimd_context *ctx)
|
||||
{
|
||||
struct fimd_win_data *win_data;
|
||||
struct exynos_drm_plane *plane;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WINDOWS_NR; i++) {
|
||||
win_data = &ctx->win_data[i];
|
||||
win_data->resume = win_data->enabled;
|
||||
if (win_data->enabled)
|
||||
plane = &ctx->planes[i];
|
||||
plane->resume = plane->enabled;
|
||||
if (plane->enabled)
|
||||
fimd_win_disable(ctx->crtc, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void fimd_window_resume(struct fimd_context *ctx)
|
||||
{
|
||||
struct fimd_win_data *win_data;
|
||||
struct exynos_drm_plane *plane;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WINDOWS_NR; i++) {
|
||||
win_data = &ctx->win_data[i];
|
||||
win_data->enabled = win_data->resume;
|
||||
win_data->resume = false;
|
||||
plane = &ctx->planes[i];
|
||||
plane->enabled = plane->resume;
|
||||
plane->resume = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void fimd_apply(struct fimd_context *ctx)
|
||||
{
|
||||
struct fimd_win_data *win_data;
|
||||
struct exynos_drm_plane *plane;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WINDOWS_NR; i++) {
|
||||
win_data = &ctx->win_data[i];
|
||||
if (win_data->enabled)
|
||||
plane = &ctx->planes[i];
|
||||
if (plane->enabled)
|
||||
fimd_win_commit(ctx->crtc, i);
|
||||
else
|
||||
fimd_win_disable(ctx->crtc, i);
|
||||
@ -1008,7 +953,6 @@ static struct exynos_drm_crtc_ops fimd_crtc_ops = {
|
||||
.enable_vblank = fimd_enable_vblank,
|
||||
.disable_vblank = fimd_disable_vblank,
|
||||
.wait_for_vblank = fimd_wait_for_vblank,
|
||||
.win_mode_set = fimd_win_mode_set,
|
||||
.win_commit = fimd_win_commit,
|
||||
.win_disable = fimd_win_disable,
|
||||
.te_handler = fimd_te_handler,
|
||||
@ -1054,14 +998,29 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
|
||||
struct fimd_context *ctx = dev_get_drvdata(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
struct exynos_drm_private *priv = drm_dev->dev_private;
|
||||
struct exynos_drm_plane *exynos_plane;
|
||||
enum drm_plane_type type;
|
||||
unsigned int zpos;
|
||||
int ret;
|
||||
|
||||
ctx->drm_dev = drm_dev;
|
||||
ctx->pipe = priv->pipe++;
|
||||
|
||||
ctx->crtc = exynos_drm_crtc_create(drm_dev, ctx->pipe,
|
||||
EXYNOS_DISPLAY_TYPE_LCD,
|
||||
for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
|
||||
type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
|
||||
DRM_PLANE_TYPE_OVERLAY;
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
|
||||
1 << ctx->pipe, type, zpos);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
exynos_plane = &ctx->planes[ctx->default_win];
|
||||
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
|
||||
ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD,
|
||||
&fimd_crtc_ops, ctx);
|
||||
if (IS_ERR(ctx->crtc))
|
||||
return PTR_ERR(ctx->crtc);
|
||||
|
||||
if (ctx->display)
|
||||
exynos_drm_create_enc_conn(drm_dev, ctx->display);
|
||||
@ -1233,6 +1192,24 @@ static int fimd_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable)
|
||||
{
|
||||
struct fimd_context *ctx = crtc->ctx;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* Only Exynos 5250, 5260, 5410 and 542x requires enabling DP/MIE
|
||||
* clock. On these SoCs the bootloader may enable it but any
|
||||
* power domain off/on will reset it to disable state.
|
||||
*/
|
||||
if (ctx->driver_data != &exynos5_fimd_driver_data)
|
||||
return;
|
||||
|
||||
val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE;
|
||||
writel(DP_MIE_CLK_DP_ENABLE, ctx->regs + DP_MIE_CLKCON);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fimd_dp_clock_enable);
|
||||
|
||||
struct platform_driver fimd_driver = {
|
||||
.probe = fimd_probe,
|
||||
.remove = fimd_remove,
|
||||
|
15
drivers/gpu/drm/exynos/exynos_drm_fimd.h
Normal file
15
drivers/gpu/drm/exynos/exynos_drm_fimd.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _EXYNOS_DRM_FIMD_H_
|
||||
#define _EXYNOS_DRM_FIMD_H_
|
||||
|
||||
extern void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable);
|
||||
|
||||
#endif /* _EXYNOS_DRM_FIMD_H_ */
|
@ -476,6 +476,45 @@ err_clear:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipp_validate_mem_node(struct drm_device *drm_dev,
|
||||
struct drm_exynos_ipp_mem_node *m_node,
|
||||
struct drm_exynos_ipp_cmd_node *c_node)
|
||||
{
|
||||
struct drm_exynos_ipp_config *ipp_cfg;
|
||||
unsigned int num_plane;
|
||||
unsigned long min_size, size;
|
||||
unsigned int bpp;
|
||||
int i;
|
||||
|
||||
/* The property id should already be varified */
|
||||
ipp_cfg = &c_node->property.config[m_node->prop_id];
|
||||
num_plane = drm_format_num_planes(ipp_cfg->fmt);
|
||||
|
||||
/**
|
||||
* This is a rather simplified validation of a memory node.
|
||||
* It basically verifies provided gem object handles
|
||||
* and the buffer sizes with respect to current configuration.
|
||||
* This is not the best that can be done
|
||||
* but it seems more than enough
|
||||
*/
|
||||
for (i = 0; i < num_plane; ++i) {
|
||||
if (!m_node->buf_info.handles[i]) {
|
||||
DRM_ERROR("invalid handle for plane %d\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
bpp = drm_format_plane_cpp(ipp_cfg->fmt, i);
|
||||
min_size = (ipp_cfg->sz.hsize * ipp_cfg->sz.vsize * bpp) >> 3;
|
||||
size = exynos_drm_gem_get_size(drm_dev,
|
||||
m_node->buf_info.handles[i],
|
||||
c_node->filp);
|
||||
if (min_size > size) {
|
||||
DRM_ERROR("invalid size for plane %d\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipp_put_mem_node(struct drm_device *drm_dev,
|
||||
struct drm_exynos_ipp_cmd_node *c_node,
|
||||
struct drm_exynos_ipp_mem_node *m_node)
|
||||
@ -552,6 +591,11 @@ static struct drm_exynos_ipp_mem_node
|
||||
}
|
||||
|
||||
mutex_lock(&c_node->mem_lock);
|
||||
if (ipp_validate_mem_node(drm_dev, m_node, c_node)) {
|
||||
ipp_put_mem_node(drm_dev, c_node, m_node);
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
return ERR_PTR(-EFAULT);
|
||||
}
|
||||
list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]);
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
|
||||
|
@ -92,7 +92,6 @@ void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
uint32_t src_w, uint32_t src_h)
|
||||
{
|
||||
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
|
||||
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
|
||||
unsigned int actual_w;
|
||||
unsigned int actual_h;
|
||||
|
||||
@ -111,13 +110,17 @@ void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
crtc_y = 0;
|
||||
}
|
||||
|
||||
/* set ratio */
|
||||
exynos_plane->h_ratio = (src_w << 16) / crtc_w;
|
||||
exynos_plane->v_ratio = (src_h << 16) / crtc_h;
|
||||
|
||||
/* set drm framebuffer data. */
|
||||
exynos_plane->fb_x = src_x;
|
||||
exynos_plane->fb_y = src_y;
|
||||
exynos_plane->src_x = src_x;
|
||||
exynos_plane->src_y = src_y;
|
||||
exynos_plane->src_width = (actual_w * exynos_plane->h_ratio) >> 16;
|
||||
exynos_plane->src_height = (actual_h * exynos_plane->v_ratio) >> 16;
|
||||
exynos_plane->fb_width = fb->width;
|
||||
exynos_plane->fb_height = fb->height;
|
||||
exynos_plane->src_width = src_w;
|
||||
exynos_plane->src_height = src_h;
|
||||
exynos_plane->bpp = fb->bits_per_pixel;
|
||||
exynos_plane->pitch = fb->pitches[0];
|
||||
exynos_plane->pixel_format = fb->pixel_format;
|
||||
@ -139,9 +142,6 @@ void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
exynos_plane->crtc_width, exynos_plane->crtc_height);
|
||||
|
||||
plane->crtc = crtc;
|
||||
|
||||
if (exynos_crtc->ops->win_mode_set)
|
||||
exynos_crtc->ops->win_mode_set(exynos_crtc, exynos_plane);
|
||||
}
|
||||
|
||||
int
|
||||
@ -182,39 +182,14 @@ static int exynos_disable_plane(struct drm_plane *plane)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_plane_destroy(struct drm_plane *plane)
|
||||
{
|
||||
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
|
||||
|
||||
exynos_disable_plane(plane);
|
||||
drm_plane_cleanup(plane);
|
||||
kfree(exynos_plane);
|
||||
}
|
||||
|
||||
static int exynos_plane_set_property(struct drm_plane *plane,
|
||||
struct drm_property *property,
|
||||
uint64_t val)
|
||||
{
|
||||
struct drm_device *dev = plane->dev;
|
||||
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
|
||||
struct exynos_drm_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (property == dev_priv->plane_zpos_property) {
|
||||
exynos_plane->zpos = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct drm_plane_funcs exynos_plane_funcs = {
|
||||
.update_plane = exynos_update_plane,
|
||||
.disable_plane = exynos_disable_plane,
|
||||
.destroy = exynos_plane_destroy,
|
||||
.set_property = exynos_plane_set_property,
|
||||
.destroy = drm_plane_cleanup,
|
||||
};
|
||||
|
||||
static void exynos_plane_attach_zpos_property(struct drm_plane *plane)
|
||||
static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
|
||||
unsigned int zpos)
|
||||
{
|
||||
struct drm_device *dev = plane->dev;
|
||||
struct exynos_drm_private *dev_priv = dev->dev_private;
|
||||
@ -222,41 +197,36 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane)
|
||||
|
||||
prop = dev_priv->plane_zpos_property;
|
||||
if (!prop) {
|
||||
prop = drm_property_create_range(dev, 0, "zpos", 0,
|
||||
MAX_PLANE - 1);
|
||||
prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
|
||||
"zpos", 0, MAX_PLANE - 1);
|
||||
if (!prop)
|
||||
return;
|
||||
|
||||
dev_priv->plane_zpos_property = prop;
|
||||
}
|
||||
|
||||
drm_object_attach_property(&plane->base, prop, 0);
|
||||
drm_object_attach_property(&plane->base, prop, zpos);
|
||||
}
|
||||
|
||||
struct drm_plane *exynos_plane_init(struct drm_device *dev,
|
||||
unsigned long possible_crtcs,
|
||||
enum drm_plane_type type)
|
||||
int exynos_plane_init(struct drm_device *dev,
|
||||
struct exynos_drm_plane *exynos_plane,
|
||||
unsigned long possible_crtcs, enum drm_plane_type type,
|
||||
unsigned int zpos)
|
||||
{
|
||||
struct exynos_drm_plane *exynos_plane;
|
||||
int err;
|
||||
|
||||
exynos_plane = kzalloc(sizeof(struct exynos_drm_plane), GFP_KERNEL);
|
||||
if (!exynos_plane)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs,
|
||||
&exynos_plane_funcs, formats,
|
||||
ARRAY_SIZE(formats), type);
|
||||
if (err) {
|
||||
DRM_ERROR("failed to initialize plane\n");
|
||||
kfree(exynos_plane);
|
||||
return ERR_PTR(err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (type == DRM_PLANE_TYPE_PRIMARY)
|
||||
exynos_plane->zpos = DEFAULT_ZPOS;
|
||||
else
|
||||
exynos_plane_attach_zpos_property(&exynos_plane->base);
|
||||
exynos_plane->zpos = zpos;
|
||||
|
||||
return &exynos_plane->base;
|
||||
if (type == DRM_PLANE_TYPE_OVERLAY)
|
||||
exynos_plane_attach_zpos_property(&exynos_plane->base, zpos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ int exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h);
|
||||
struct drm_plane *exynos_plane_init(struct drm_device *dev,
|
||||
unsigned long possible_crtcs,
|
||||
enum drm_plane_type type);
|
||||
int exynos_plane_init(struct drm_device *dev,
|
||||
struct exynos_drm_plane *exynos_plane,
|
||||
unsigned long possible_crtcs, enum drm_plane_type type,
|
||||
unsigned int zpos);
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_crtc.h"
|
||||
#include "exynos_drm_plane.h"
|
||||
#include "exynos_drm_encoder.h"
|
||||
#include "exynos_drm_vidi.h"
|
||||
|
||||
@ -32,20 +33,6 @@
|
||||
#define ctx_from_connector(c) container_of(c, struct vidi_context, \
|
||||
connector)
|
||||
|
||||
struct vidi_win_data {
|
||||
unsigned int offset_x;
|
||||
unsigned int offset_y;
|
||||
unsigned int ovl_width;
|
||||
unsigned int ovl_height;
|
||||
unsigned int fb_width;
|
||||
unsigned int fb_height;
|
||||
unsigned int bpp;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned int buf_offsize;
|
||||
unsigned int line_size; /* bytes */
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct vidi_context {
|
||||
struct exynos_drm_display display;
|
||||
struct platform_device *pdev;
|
||||
@ -53,7 +40,7 @@ struct vidi_context {
|
||||
struct exynos_drm_crtc *crtc;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_connector connector;
|
||||
struct vidi_win_data win_data[WINDOWS_NR];
|
||||
struct exynos_drm_plane planes[WINDOWS_NR];
|
||||
struct edid *raw_edid;
|
||||
unsigned int clkdiv;
|
||||
unsigned int default_win;
|
||||
@ -97,19 +84,6 @@ static const char fake_edid_info[] = {
|
||||
0x00, 0x00, 0x00, 0x06
|
||||
};
|
||||
|
||||
static void vidi_apply(struct vidi_context *ctx)
|
||||
{
|
||||
struct exynos_drm_crtc_ops *crtc_ops = ctx->crtc->ops;
|
||||
struct vidi_win_data *win_data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WINDOWS_NR; i++) {
|
||||
win_data = &ctx->win_data[i];
|
||||
if (win_data->enabled && (crtc_ops && crtc_ops->win_commit))
|
||||
crtc_ops->win_commit(ctx->crtc, i);
|
||||
}
|
||||
}
|
||||
|
||||
static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)
|
||||
{
|
||||
struct vidi_context *ctx = crtc->ctx;
|
||||
@ -143,104 +117,46 @@ static void vidi_disable_vblank(struct exynos_drm_crtc *crtc)
|
||||
ctx->vblank_on = false;
|
||||
}
|
||||
|
||||
static void vidi_win_mode_set(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
static void vidi_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
|
||||
{
|
||||
struct vidi_context *ctx = crtc->ctx;
|
||||
struct vidi_win_data *win_data;
|
||||
int win;
|
||||
unsigned long offset;
|
||||
|
||||
if (!plane) {
|
||||
DRM_ERROR("plane is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
win = plane->zpos;
|
||||
if (win == DEFAULT_ZPOS)
|
||||
win = ctx->default_win;
|
||||
|
||||
if (win < 0 || win >= WINDOWS_NR)
|
||||
return;
|
||||
|
||||
offset = plane->fb_x * (plane->bpp >> 3);
|
||||
offset += plane->fb_y * plane->pitch;
|
||||
|
||||
DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, plane->pitch);
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
|
||||
win_data->offset_x = plane->crtc_x;
|
||||
win_data->offset_y = plane->crtc_y;
|
||||
win_data->ovl_width = plane->crtc_width;
|
||||
win_data->ovl_height = plane->crtc_height;
|
||||
win_data->fb_width = plane->fb_width;
|
||||
win_data->fb_height = plane->fb_height;
|
||||
win_data->dma_addr = plane->dma_addr[0] + offset;
|
||||
win_data->bpp = plane->bpp;
|
||||
win_data->buf_offsize = (plane->fb_width - plane->crtc_width) *
|
||||
(plane->bpp >> 3);
|
||||
win_data->line_size = plane->crtc_width * (plane->bpp >> 3);
|
||||
|
||||
/*
|
||||
* some parts of win_data should be transferred to user side
|
||||
* through specific ioctl.
|
||||
*/
|
||||
|
||||
DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
|
||||
win_data->offset_x, win_data->offset_y);
|
||||
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
|
||||
win_data->ovl_width, win_data->ovl_height);
|
||||
DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
|
||||
DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
|
||||
plane->fb_width, plane->crtc_width);
|
||||
}
|
||||
|
||||
static void vidi_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
{
|
||||
struct vidi_context *ctx = crtc->ctx;
|
||||
struct vidi_win_data *win_data;
|
||||
int win = zpos;
|
||||
struct exynos_drm_plane *plane;
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
if (win == DEFAULT_ZPOS)
|
||||
win = ctx->default_win;
|
||||
|
||||
if (win < 0 || win >= WINDOWS_NR)
|
||||
return;
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
plane = &ctx->planes[win];
|
||||
|
||||
win_data->enabled = true;
|
||||
plane->enabled = true;
|
||||
|
||||
DRM_DEBUG_KMS("dma_addr = %pad\n", &win_data->dma_addr);
|
||||
DRM_DEBUG_KMS("dma_addr = %pad\n", plane->dma_addr);
|
||||
|
||||
if (ctx->vblank_on)
|
||||
schedule_work(&ctx->work);
|
||||
}
|
||||
|
||||
static void vidi_win_disable(struct exynos_drm_crtc *crtc, int zpos)
|
||||
static void vidi_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
|
||||
{
|
||||
struct vidi_context *ctx = crtc->ctx;
|
||||
struct vidi_win_data *win_data;
|
||||
int win = zpos;
|
||||
|
||||
if (win == DEFAULT_ZPOS)
|
||||
win = ctx->default_win;
|
||||
struct exynos_drm_plane *plane;
|
||||
|
||||
if (win < 0 || win >= WINDOWS_NR)
|
||||
return;
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
win_data->enabled = false;
|
||||
plane = &ctx->planes[win];
|
||||
plane->enabled = false;
|
||||
|
||||
/* TODO. */
|
||||
}
|
||||
|
||||
static int vidi_power_on(struct vidi_context *ctx, bool enable)
|
||||
{
|
||||
struct exynos_drm_plane *plane;
|
||||
int i;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (enable != false && enable != true)
|
||||
@ -253,7 +169,11 @@ static int vidi_power_on(struct vidi_context *ctx, bool enable)
|
||||
if (test_and_clear_bit(0, &ctx->irq_flags))
|
||||
vidi_enable_vblank(ctx->crtc);
|
||||
|
||||
vidi_apply(ctx);
|
||||
for (i = 0; i < WINDOWS_NR; i++) {
|
||||
plane = &ctx->planes[i];
|
||||
if (plane->enabled)
|
||||
vidi_win_commit(ctx->crtc, i);
|
||||
}
|
||||
} else {
|
||||
ctx->suspended = true;
|
||||
}
|
||||
@ -301,7 +221,6 @@ static struct exynos_drm_crtc_ops vidi_crtc_ops = {
|
||||
.dpms = vidi_dpms,
|
||||
.enable_vblank = vidi_enable_vblank,
|
||||
.disable_vblank = vidi_disable_vblank,
|
||||
.win_mode_set = vidi_win_mode_set,
|
||||
.win_commit = vidi_win_commit,
|
||||
.win_disable = vidi_win_disable,
|
||||
};
|
||||
@ -543,12 +462,25 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct vidi_context *ctx = dev_get_drvdata(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
struct exynos_drm_plane *exynos_plane;
|
||||
enum drm_plane_type type;
|
||||
unsigned int zpos;
|
||||
int ret;
|
||||
|
||||
vidi_ctx_initialize(ctx, drm_dev);
|
||||
|
||||
ctx->crtc = exynos_drm_crtc_create(drm_dev, ctx->pipe,
|
||||
EXYNOS_DISPLAY_TYPE_VIDI,
|
||||
for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
|
||||
type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
|
||||
DRM_PLANE_TYPE_OVERLAY;
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
|
||||
1 << ctx->pipe, type, zpos);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
exynos_plane = &ctx->planes[ctx->default_win];
|
||||
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
|
||||
ctx->pipe, EXYNOS_DISPLAY_TYPE_VIDI,
|
||||
&vidi_crtc_ops, ctx);
|
||||
if (IS_ERR(ctx->crtc)) {
|
||||
DRM_ERROR("failed to create crtc.\n");
|
||||
|
@ -2007,7 +2007,7 @@ static void hdmi_mode_set(struct exynos_drm_display *display,
|
||||
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
|
||||
m->hdisplay, m->vdisplay,
|
||||
m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
|
||||
"INTERLACED" : "PROGERESSIVE");
|
||||
"INTERLACED" : "PROGRESSIVE");
|
||||
|
||||
/* preserve mode information for later use. */
|
||||
drm_mode_copy(&hdata->current_mode, mode);
|
||||
@ -2101,7 +2101,7 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode)
|
||||
struct hdmi_context *hdata = display_to_hdmi(display);
|
||||
struct drm_encoder *encoder = hdata->encoder;
|
||||
struct drm_crtc *crtc = encoder->crtc;
|
||||
struct drm_crtc_helper_funcs *funcs = NULL;
|
||||
const struct drm_crtc_helper_funcs *funcs = NULL;
|
||||
|
||||
DRM_DEBUG_KMS("mode %d\n", mode);
|
||||
|
||||
|
@ -37,35 +37,13 @@
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_crtc.h"
|
||||
#include "exynos_drm_plane.h"
|
||||
#include "exynos_drm_iommu.h"
|
||||
#include "exynos_mixer.h"
|
||||
|
||||
#define MIXER_WIN_NR 3
|
||||
#define MIXER_DEFAULT_WIN 0
|
||||
|
||||
struct hdmi_win_data {
|
||||
dma_addr_t dma_addr;
|
||||
dma_addr_t chroma_dma_addr;
|
||||
uint32_t pixel_format;
|
||||
unsigned int bpp;
|
||||
unsigned int crtc_x;
|
||||
unsigned int crtc_y;
|
||||
unsigned int crtc_width;
|
||||
unsigned int crtc_height;
|
||||
unsigned int fb_x;
|
||||
unsigned int fb_y;
|
||||
unsigned int fb_width;
|
||||
unsigned int fb_pitch;
|
||||
unsigned int fb_height;
|
||||
unsigned int src_width;
|
||||
unsigned int src_height;
|
||||
unsigned int mode_width;
|
||||
unsigned int mode_height;
|
||||
unsigned int scan_flags;
|
||||
bool enabled;
|
||||
bool resume;
|
||||
};
|
||||
|
||||
struct mixer_resources {
|
||||
int irq;
|
||||
void __iomem *mixer_regs;
|
||||
@ -90,6 +68,7 @@ struct mixer_context {
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
struct exynos_drm_crtc *crtc;
|
||||
struct exynos_drm_plane planes[MIXER_WIN_NR];
|
||||
int pipe;
|
||||
bool interlace;
|
||||
bool powered;
|
||||
@ -99,7 +78,6 @@ struct mixer_context {
|
||||
|
||||
struct mutex mixer_mutex;
|
||||
struct mixer_resources mixer_res;
|
||||
struct hdmi_win_data win_data[MIXER_WIN_NR];
|
||||
enum mixer_version_id mxr_ver;
|
||||
wait_queue_head_t wait_vsync_queue;
|
||||
atomic_t wait_vsync_event;
|
||||
@ -289,7 +267,7 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
|
||||
|
||||
/* choosing between interlace and progressive mode */
|
||||
val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
|
||||
MXR_CFG_SCAN_PROGRASSIVE);
|
||||
MXR_CFG_SCAN_PROGRESSIVE);
|
||||
|
||||
if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
|
||||
/* choosing between proper HD and SD mode */
|
||||
@ -403,17 +381,16 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
|
||||
{
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
unsigned long flags;
|
||||
struct hdmi_win_data *win_data;
|
||||
unsigned int x_ratio, y_ratio;
|
||||
struct exynos_drm_plane *plane;
|
||||
unsigned int buf_num = 1;
|
||||
dma_addr_t luma_addr[2], chroma_addr[2];
|
||||
bool tiled_mode = false;
|
||||
bool crcb_mode = false;
|
||||
u32 val;
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
plane = &ctx->planes[win];
|
||||
|
||||
switch (win_data->pixel_format) {
|
||||
switch (plane->pixel_format) {
|
||||
case DRM_FORMAT_NV12:
|
||||
crcb_mode = false;
|
||||
buf_num = 2;
|
||||
@ -421,35 +398,31 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
|
||||
/* TODO: single buffer format NV12, NV21 */
|
||||
default:
|
||||
/* ignore pixel format at disable time */
|
||||
if (!win_data->dma_addr)
|
||||
if (!plane->dma_addr[0])
|
||||
break;
|
||||
|
||||
DRM_ERROR("pixel format for vp is wrong [%d].\n",
|
||||
win_data->pixel_format);
|
||||
plane->pixel_format);
|
||||
return;
|
||||
}
|
||||
|
||||
/* scaling feature: (src << 16) / dst */
|
||||
x_ratio = (win_data->src_width << 16) / win_data->crtc_width;
|
||||
y_ratio = (win_data->src_height << 16) / win_data->crtc_height;
|
||||
|
||||
if (buf_num == 2) {
|
||||
luma_addr[0] = win_data->dma_addr;
|
||||
chroma_addr[0] = win_data->chroma_dma_addr;
|
||||
luma_addr[0] = plane->dma_addr[0];
|
||||
chroma_addr[0] = plane->dma_addr[1];
|
||||
} else {
|
||||
luma_addr[0] = win_data->dma_addr;
|
||||
chroma_addr[0] = win_data->dma_addr
|
||||
+ (win_data->fb_pitch * win_data->fb_height);
|
||||
luma_addr[0] = plane->dma_addr[0];
|
||||
chroma_addr[0] = plane->dma_addr[0]
|
||||
+ (plane->pitch * plane->fb_height);
|
||||
}
|
||||
|
||||
if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
if (plane->scan_flag & DRM_MODE_FLAG_INTERLACE) {
|
||||
ctx->interlace = true;
|
||||
if (tiled_mode) {
|
||||
luma_addr[1] = luma_addr[0] + 0x40;
|
||||
chroma_addr[1] = chroma_addr[0] + 0x40;
|
||||
} else {
|
||||
luma_addr[1] = luma_addr[0] + win_data->fb_pitch;
|
||||
chroma_addr[1] = chroma_addr[0] + win_data->fb_pitch;
|
||||
luma_addr[1] = luma_addr[0] + plane->pitch;
|
||||
chroma_addr[1] = chroma_addr[0] + plane->pitch;
|
||||
}
|
||||
} else {
|
||||
ctx->interlace = false;
|
||||
@ -470,30 +443,30 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
|
||||
vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
|
||||
|
||||
/* setting size of input image */
|
||||
vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_pitch) |
|
||||
VP_IMG_VSIZE(win_data->fb_height));
|
||||
vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(plane->pitch) |
|
||||
VP_IMG_VSIZE(plane->fb_height));
|
||||
/* chroma height has to reduced by 2 to avoid chroma distorions */
|
||||
vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_pitch) |
|
||||
VP_IMG_VSIZE(win_data->fb_height / 2));
|
||||
vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(plane->pitch) |
|
||||
VP_IMG_VSIZE(plane->fb_height / 2));
|
||||
|
||||
vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width);
|
||||
vp_reg_write(res, VP_SRC_HEIGHT, win_data->src_height);
|
||||
vp_reg_write(res, VP_SRC_WIDTH, plane->src_width);
|
||||
vp_reg_write(res, VP_SRC_HEIGHT, plane->src_height);
|
||||
vp_reg_write(res, VP_SRC_H_POSITION,
|
||||
VP_SRC_H_POSITION_VAL(win_data->fb_x));
|
||||
vp_reg_write(res, VP_SRC_V_POSITION, win_data->fb_y);
|
||||
VP_SRC_H_POSITION_VAL(plane->src_x));
|
||||
vp_reg_write(res, VP_SRC_V_POSITION, plane->src_y);
|
||||
|
||||
vp_reg_write(res, VP_DST_WIDTH, win_data->crtc_width);
|
||||
vp_reg_write(res, VP_DST_H_POSITION, win_data->crtc_x);
|
||||
vp_reg_write(res, VP_DST_WIDTH, plane->crtc_width);
|
||||
vp_reg_write(res, VP_DST_H_POSITION, plane->crtc_x);
|
||||
if (ctx->interlace) {
|
||||
vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height / 2);
|
||||
vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y / 2);
|
||||
vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_height / 2);
|
||||
vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y / 2);
|
||||
} else {
|
||||
vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height);
|
||||
vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y);
|
||||
vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_height);
|
||||
vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y);
|
||||
}
|
||||
|
||||
vp_reg_write(res, VP_H_RATIO, x_ratio);
|
||||
vp_reg_write(res, VP_V_RATIO, y_ratio);
|
||||
vp_reg_write(res, VP_H_RATIO, plane->h_ratio);
|
||||
vp_reg_write(res, VP_V_RATIO, plane->v_ratio);
|
||||
|
||||
vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
|
||||
|
||||
@ -503,8 +476,8 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
|
||||
vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
|
||||
vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
|
||||
|
||||
mixer_cfg_scan(ctx, win_data->mode_height);
|
||||
mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
|
||||
mixer_cfg_scan(ctx, plane->mode_height);
|
||||
mixer_cfg_rgb_fmt(ctx, plane->mode_height);
|
||||
mixer_cfg_layer(ctx, win, true);
|
||||
mixer_run(ctx);
|
||||
|
||||
@ -521,25 +494,49 @@ static void mixer_layer_update(struct mixer_context *ctx)
|
||||
mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
|
||||
}
|
||||
|
||||
static int mixer_setup_scale(const struct exynos_drm_plane *plane,
|
||||
unsigned int *x_ratio, unsigned int *y_ratio)
|
||||
{
|
||||
if (plane->crtc_width != plane->src_width) {
|
||||
if (plane->crtc_width == 2 * plane->src_width)
|
||||
*x_ratio = 1;
|
||||
else
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (plane->crtc_height != plane->src_height) {
|
||||
if (plane->crtc_height == 2 * plane->src_height)
|
||||
*y_ratio = 1;
|
||||
else
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
DRM_DEBUG_KMS("only 2x width/height scaling of plane supported\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static void mixer_graph_buffer(struct mixer_context *ctx, int win)
|
||||
{
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
unsigned long flags;
|
||||
struct hdmi_win_data *win_data;
|
||||
unsigned int x_ratio, y_ratio;
|
||||
struct exynos_drm_plane *plane;
|
||||
unsigned int x_ratio = 0, y_ratio = 0;
|
||||
unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned int fmt;
|
||||
u32 val;
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
plane = &ctx->planes[win];
|
||||
|
||||
#define RGB565 4
|
||||
#define ARGB1555 5
|
||||
#define ARGB4444 6
|
||||
#define ARGB8888 7
|
||||
|
||||
switch (win_data->bpp) {
|
||||
switch (plane->bpp) {
|
||||
case 16:
|
||||
fmt = ARGB4444;
|
||||
break;
|
||||
@ -550,21 +547,21 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
|
||||
fmt = ARGB8888;
|
||||
}
|
||||
|
||||
/* 2x scaling feature */
|
||||
x_ratio = 0;
|
||||
y_ratio = 0;
|
||||
/* check if mixer supports requested scaling setup */
|
||||
if (mixer_setup_scale(plane, &x_ratio, &y_ratio))
|
||||
return;
|
||||
|
||||
dst_x_offset = win_data->crtc_x;
|
||||
dst_y_offset = win_data->crtc_y;
|
||||
dst_x_offset = plane->crtc_x;
|
||||
dst_y_offset = plane->crtc_y;
|
||||
|
||||
/* converting dma address base and source offset */
|
||||
dma_addr = win_data->dma_addr
|
||||
+ (win_data->fb_x * win_data->bpp >> 3)
|
||||
+ (win_data->fb_y * win_data->fb_pitch);
|
||||
dma_addr = plane->dma_addr[0]
|
||||
+ (plane->src_x * plane->bpp >> 3)
|
||||
+ (plane->src_y * plane->pitch);
|
||||
src_x_offset = 0;
|
||||
src_y_offset = 0;
|
||||
|
||||
if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE)
|
||||
if (plane->scan_flag & DRM_MODE_FLAG_INTERLACE)
|
||||
ctx->interlace = true;
|
||||
else
|
||||
ctx->interlace = false;
|
||||
@ -578,18 +575,18 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
|
||||
|
||||
/* setup geometry */
|
||||
mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
|
||||
win_data->fb_pitch / (win_data->bpp >> 3));
|
||||
plane->pitch / (plane->bpp >> 3));
|
||||
|
||||
/* setup display size */
|
||||
if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
|
||||
win == MIXER_DEFAULT_WIN) {
|
||||
val = MXR_MXR_RES_HEIGHT(win_data->mode_height);
|
||||
val |= MXR_MXR_RES_WIDTH(win_data->mode_width);
|
||||
val = MXR_MXR_RES_HEIGHT(plane->mode_height);
|
||||
val |= MXR_MXR_RES_WIDTH(plane->mode_width);
|
||||
mixer_reg_write(res, MXR_RESOLUTION, val);
|
||||
}
|
||||
|
||||
val = MXR_GRP_WH_WIDTH(win_data->crtc_width);
|
||||
val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height);
|
||||
val = MXR_GRP_WH_WIDTH(plane->src_width);
|
||||
val |= MXR_GRP_WH_HEIGHT(plane->src_height);
|
||||
val |= MXR_GRP_WH_H_SCALE(x_ratio);
|
||||
val |= MXR_GRP_WH_V_SCALE(y_ratio);
|
||||
mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
|
||||
@ -607,8 +604,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
|
||||
/* set buffer address to mixer */
|
||||
mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
|
||||
|
||||
mixer_cfg_scan(ctx, win_data->mode_height);
|
||||
mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
|
||||
mixer_cfg_scan(ctx, plane->mode_height);
|
||||
mixer_cfg_rgb_fmt(ctx, plane->mode_height);
|
||||
mixer_cfg_layer(ctx, win, true);
|
||||
|
||||
/* layer update mandatory for mixer 16.0.33.0 */
|
||||
@ -920,63 +917,9 @@ static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
|
||||
mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
|
||||
}
|
||||
|
||||
static void mixer_win_mode_set(struct exynos_drm_crtc *crtc,
|
||||
struct exynos_drm_plane *plane)
|
||||
static void mixer_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = crtc->ctx;
|
||||
struct hdmi_win_data *win_data;
|
||||
int win;
|
||||
|
||||
if (!plane) {
|
||||
DRM_ERROR("plane is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("set [%d]x[%d] at (%d,%d) to [%d]x[%d] at (%d,%d)\n",
|
||||
plane->fb_width, plane->fb_height,
|
||||
plane->fb_x, plane->fb_y,
|
||||
plane->crtc_width, plane->crtc_height,
|
||||
plane->crtc_x, plane->crtc_y);
|
||||
|
||||
win = plane->zpos;
|
||||
if (win == DEFAULT_ZPOS)
|
||||
win = MIXER_DEFAULT_WIN;
|
||||
|
||||
if (win < 0 || win >= MIXER_WIN_NR) {
|
||||
DRM_ERROR("mixer window[%d] is wrong\n", win);
|
||||
return;
|
||||
}
|
||||
|
||||
win_data = &mixer_ctx->win_data[win];
|
||||
|
||||
win_data->dma_addr = plane->dma_addr[0];
|
||||
win_data->chroma_dma_addr = plane->dma_addr[1];
|
||||
win_data->pixel_format = plane->pixel_format;
|
||||
win_data->bpp = plane->bpp;
|
||||
|
||||
win_data->crtc_x = plane->crtc_x;
|
||||
win_data->crtc_y = plane->crtc_y;
|
||||
win_data->crtc_width = plane->crtc_width;
|
||||
win_data->crtc_height = plane->crtc_height;
|
||||
|
||||
win_data->fb_x = plane->fb_x;
|
||||
win_data->fb_y = plane->fb_y;
|
||||
win_data->fb_width = plane->fb_width;
|
||||
win_data->fb_height = plane->fb_height;
|
||||
win_data->fb_pitch = plane->pitch;
|
||||
win_data->src_width = plane->src_width;
|
||||
win_data->src_height = plane->src_height;
|
||||
|
||||
win_data->mode_width = plane->mode_width;
|
||||
win_data->mode_height = plane->mode_height;
|
||||
|
||||
win_data->scan_flags = plane->scan_flag;
|
||||
}
|
||||
|
||||
static void mixer_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = crtc->ctx;
|
||||
int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
|
||||
|
||||
DRM_DEBUG_KMS("win: %d\n", win);
|
||||
|
||||
@ -992,14 +935,13 @@ static void mixer_win_commit(struct exynos_drm_crtc *crtc, int zpos)
|
||||
else
|
||||
mixer_graph_buffer(mixer_ctx, win);
|
||||
|
||||
mixer_ctx->win_data[win].enabled = true;
|
||||
mixer_ctx->planes[win].enabled = true;
|
||||
}
|
||||
|
||||
static void mixer_win_disable(struct exynos_drm_crtc *crtc, int zpos)
|
||||
static void mixer_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = crtc->ctx;
|
||||
struct mixer_resources *res = &mixer_ctx->mixer_res;
|
||||
int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
|
||||
unsigned long flags;
|
||||
|
||||
DRM_DEBUG_KMS("win: %d\n", win);
|
||||
@ -1007,7 +949,7 @@ static void mixer_win_disable(struct exynos_drm_crtc *crtc, int zpos)
|
||||
mutex_lock(&mixer_ctx->mixer_mutex);
|
||||
if (!mixer_ctx->powered) {
|
||||
mutex_unlock(&mixer_ctx->mixer_mutex);
|
||||
mixer_ctx->win_data[win].resume = false;
|
||||
mixer_ctx->planes[win].resume = false;
|
||||
return;
|
||||
}
|
||||
mutex_unlock(&mixer_ctx->mixer_mutex);
|
||||
@ -1020,7 +962,7 @@ static void mixer_win_disable(struct exynos_drm_crtc *crtc, int zpos)
|
||||
mixer_vsync_set_update(mixer_ctx, true);
|
||||
spin_unlock_irqrestore(&res->reg_slock, flags);
|
||||
|
||||
mixer_ctx->win_data[win].enabled = false;
|
||||
mixer_ctx->planes[win].enabled = false;
|
||||
}
|
||||
|
||||
static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
|
||||
@ -1057,12 +999,12 @@ static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
|
||||
|
||||
static void mixer_window_suspend(struct mixer_context *ctx)
|
||||
{
|
||||
struct hdmi_win_data *win_data;
|
||||
struct exynos_drm_plane *plane;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MIXER_WIN_NR; i++) {
|
||||
win_data = &ctx->win_data[i];
|
||||
win_data->resume = win_data->enabled;
|
||||
plane = &ctx->planes[i];
|
||||
plane->resume = plane->enabled;
|
||||
mixer_win_disable(ctx->crtc, i);
|
||||
}
|
||||
mixer_wait_for_vblank(ctx->crtc);
|
||||
@ -1070,14 +1012,14 @@ static void mixer_window_suspend(struct mixer_context *ctx)
|
||||
|
||||
static void mixer_window_resume(struct mixer_context *ctx)
|
||||
{
|
||||
struct hdmi_win_data *win_data;
|
||||
struct exynos_drm_plane *plane;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MIXER_WIN_NR; i++) {
|
||||
win_data = &ctx->win_data[i];
|
||||
win_data->enabled = win_data->resume;
|
||||
win_data->resume = false;
|
||||
if (win_data->enabled)
|
||||
plane = &ctx->planes[i];
|
||||
plane->enabled = plane->resume;
|
||||
plane->resume = false;
|
||||
if (plane->enabled)
|
||||
mixer_win_commit(ctx->crtc, i);
|
||||
}
|
||||
}
|
||||
@ -1189,7 +1131,6 @@ static struct exynos_drm_crtc_ops mixer_crtc_ops = {
|
||||
.enable_vblank = mixer_enable_vblank,
|
||||
.disable_vblank = mixer_disable_vblank,
|
||||
.wait_for_vblank = mixer_wait_for_vblank,
|
||||
.win_mode_set = mixer_win_mode_set,
|
||||
.win_commit = mixer_win_commit,
|
||||
.win_disable = mixer_win_disable,
|
||||
};
|
||||
@ -1253,15 +1194,28 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
|
||||
{
|
||||
struct mixer_context *ctx = dev_get_drvdata(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
struct exynos_drm_plane *exynos_plane;
|
||||
enum drm_plane_type type;
|
||||
unsigned int zpos;
|
||||
int ret;
|
||||
|
||||
ret = mixer_initialize(ctx, drm_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctx->crtc = exynos_drm_crtc_create(drm_dev, ctx->pipe,
|
||||
EXYNOS_DISPLAY_TYPE_HDMI,
|
||||
&mixer_crtc_ops, ctx);
|
||||
for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) {
|
||||
type = (zpos == MIXER_DEFAULT_WIN) ? DRM_PLANE_TYPE_PRIMARY :
|
||||
DRM_PLANE_TYPE_OVERLAY;
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
|
||||
1 << ctx->pipe, type, zpos);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
exynos_plane = &ctx->planes[MIXER_DEFAULT_WIN];
|
||||
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
|
||||
ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
|
||||
&mixer_crtc_ops, ctx);
|
||||
if (IS_ERR(ctx->crtc)) {
|
||||
mixer_ctx_remove(ctx);
|
||||
ret = PTR_ERR(ctx->crtc);
|
||||
|
@ -101,7 +101,7 @@
|
||||
#define MXR_CFG_GRP0_ENABLE (1 << 4)
|
||||
#define MXR_CFG_VP_ENABLE (1 << 3)
|
||||
#define MXR_CFG_SCAN_INTERLACE (0 << 2)
|
||||
#define MXR_CFG_SCAN_PROGRASSIVE (1 << 2)
|
||||
#define MXR_CFG_SCAN_PROGRESSIVE (1 << 2)
|
||||
#define MXR_CFG_SCAN_NTSC (0 << 1)
|
||||
#define MXR_CFG_SCAN_PAL (1 << 1)
|
||||
#define MXR_CFG_SCAN_SD (0 << 0)
|
||||
|
@ -823,7 +823,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
|
||||
|
||||
/* Flush the plane changes */
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs =
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs =
|
||||
crtc->helper_private;
|
||||
crtc_funcs->mode_set_base(crtc, x, y, old_fb);
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ static int cdv_hdmi_set_property(struct drm_connector *connector,
|
||||
encoder->crtc->x, encoder->crtc->y, encoder->crtc->primary->fb))
|
||||
return -1;
|
||||
} else {
|
||||
struct drm_encoder_helper_funcs *helpers
|
||||
const struct drm_encoder_helper_funcs *helpers
|
||||
= encoder->helper_private;
|
||||
helpers->mode_set(encoder, &crtc->saved_mode,
|
||||
&crtc->saved_adjusted_mode);
|
||||
|
@ -505,7 +505,7 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector,
|
||||
else
|
||||
gma_backlight_set(encoder->dev, value);
|
||||
} else if (!strcmp(property->name, "DPMS") && encoder) {
|
||||
struct drm_encoder_helper_funcs *helpers =
|
||||
const struct drm_encoder_helper_funcs *helpers =
|
||||
encoder->helper_private;
|
||||
helpers->dpms(encoder, value);
|
||||
}
|
||||
|
@ -501,20 +501,20 @@ bool gma_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
|
||||
void gma_crtc_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
void gma_crtc_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
void gma_crtc_disable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct gtt_range *gt;
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
|
||||
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
|
||||
|
||||
@ -656,7 +656,7 @@ void gma_crtc_restore(struct drm_crtc *crtc)
|
||||
|
||||
void gma_encoder_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_encoder_helper_funcs *encoder_funcs =
|
||||
const struct drm_encoder_helper_funcs *encoder_funcs =
|
||||
encoder->helper_private;
|
||||
/* lvds has its own version of prepare see psb_intel_lvds_prepare */
|
||||
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
|
||||
@ -664,7 +664,7 @@ void gma_encoder_prepare(struct drm_encoder *encoder)
|
||||
|
||||
void gma_encoder_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_encoder_helper_funcs *encoder_funcs =
|
||||
const struct drm_encoder_helper_funcs *encoder_funcs =
|
||||
encoder->helper_private;
|
||||
/* lvds has its own version of commit see psb_intel_lvds_commit */
|
||||
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
@ -290,7 +290,7 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
|
||||
encoder->crtc->primary->fb))
|
||||
goto set_prop_error;
|
||||
} else {
|
||||
struct drm_encoder_helper_funcs *funcs =
|
||||
const struct drm_encoder_helper_funcs *funcs =
|
||||
encoder->helper_private;
|
||||
funcs->mode_set(encoder,
|
||||
&gma_crtc->saved_mode,
|
||||
|
@ -849,7 +849,7 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc,
|
||||
|
||||
/* Flush the plane changes */
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs =
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs =
|
||||
crtc->helper_private;
|
||||
crtc_funcs->mode_set_base(crtc, x, y, old_fb);
|
||||
}
|
||||
|
@ -483,7 +483,7 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
|
||||
|
||||
/* Flush the plane changes */
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs =
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs =
|
||||
crtc->helper_private;
|
||||
crtc_funcs->mode_set_base(crtc, x, y, old_fb);
|
||||
}
|
||||
|
@ -347,7 +347,7 @@ int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc,
|
||||
|
||||
/* Flush the plane changes */
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
crtc_funcs->mode_set_base(crtc, x, y, old_fb);
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc,
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
int pipe = gma_crtc->pipe;
|
||||
const struct psb_offset *map = &dev_priv->regmap[pipe];
|
||||
int refclk;
|
||||
|
@ -625,7 +625,7 @@ int psb_intel_lvds_set_property(struct drm_connector *connector,
|
||||
else
|
||||
gma_backlight_set(encoder->dev, value);
|
||||
} else if (!strcmp(property->name, "DPMS")) {
|
||||
struct drm_encoder_helper_funcs *hfuncs
|
||||
const struct drm_encoder_helper_funcs *hfuncs
|
||||
= encoder->helper_private;
|
||||
hfuncs->dpms(encoder, value);
|
||||
}
|
||||
|
@ -27,12 +27,13 @@ struct adv7511 {
|
||||
struct regmap *regmap;
|
||||
struct regmap *packet_memory_regmap;
|
||||
enum drm_connector_status status;
|
||||
int dpms_mode;
|
||||
bool powered;
|
||||
|
||||
unsigned int f_tmds;
|
||||
|
||||
unsigned int current_edid_segment;
|
||||
uint8_t edid_buf[256];
|
||||
bool edid_read;
|
||||
|
||||
wait_queue_head_t wq;
|
||||
struct drm_encoder *encoder;
|
||||
@ -357,6 +358,48 @@ static void adv7511_set_link_config(struct adv7511 *adv7511,
|
||||
adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB;
|
||||
}
|
||||
|
||||
static void adv7511_power_on(struct adv7511 *adv7511)
|
||||
{
|
||||
adv7511->current_edid_segment = -1;
|
||||
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
|
||||
ADV7511_INT0_EDID_READY);
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT(1),
|
||||
ADV7511_INT1_DDC_ERROR);
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
|
||||
ADV7511_POWER_POWER_DOWN, 0);
|
||||
|
||||
/*
|
||||
* Per spec it is allowed to pulse the HDP signal to indicate that the
|
||||
* EDID information has changed. Some monitors do this when they wakeup
|
||||
* from standby or are enabled. When the HDP goes low the adv7511 is
|
||||
* reset and the outputs are disabled which might cause the monitor to
|
||||
* go to standby again. To avoid this we ignore the HDP pin for the
|
||||
* first few seconds after enabling the output.
|
||||
*/
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
|
||||
ADV7511_REG_POWER2_HDP_SRC_MASK,
|
||||
ADV7511_REG_POWER2_HDP_SRC_NONE);
|
||||
|
||||
/*
|
||||
* Most of the registers are reset during power down or when HPD is low.
|
||||
*/
|
||||
regcache_sync(adv7511->regmap);
|
||||
|
||||
adv7511->powered = true;
|
||||
}
|
||||
|
||||
static void adv7511_power_off(struct adv7511 *adv7511)
|
||||
{
|
||||
/* TODO: setup additional power down modes */
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
|
||||
ADV7511_POWER_POWER_DOWN,
|
||||
ADV7511_POWER_POWER_DOWN);
|
||||
regcache_mark_dirty(adv7511->regmap);
|
||||
|
||||
adv7511->powered = false;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Interrupt and hotplug detection
|
||||
*/
|
||||
@ -379,69 +422,71 @@ static bool adv7511_hpd(struct adv7511 *adv7511)
|
||||
return false;
|
||||
}
|
||||
|
||||
static irqreturn_t adv7511_irq_handler(int irq, void *devid)
|
||||
{
|
||||
struct adv7511 *adv7511 = devid;
|
||||
|
||||
if (adv7511_hpd(adv7511))
|
||||
drm_helper_hpd_irq_event(adv7511->encoder->dev);
|
||||
|
||||
wake_up_all(&adv7511->wq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int adv7511_is_interrupt_pending(struct adv7511 *adv7511,
|
||||
unsigned int irq)
|
||||
static int adv7511_irq_process(struct adv7511 *adv7511)
|
||||
{
|
||||
unsigned int irq0, irq1;
|
||||
unsigned int pending;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(1), &irq1);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
pending = (irq1 << 8) | irq0;
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0);
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1);
|
||||
|
||||
return pending & irq;
|
||||
}
|
||||
if (irq0 & ADV7511_INT0_HDP)
|
||||
drm_helper_hpd_irq_event(adv7511->encoder->dev);
|
||||
|
||||
static int adv7511_wait_for_interrupt(struct adv7511 *adv7511, int irq,
|
||||
int timeout)
|
||||
{
|
||||
unsigned int pending;
|
||||
int ret;
|
||||
if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) {
|
||||
adv7511->edid_read = true;
|
||||
|
||||
if (adv7511->i2c_main->irq) {
|
||||
ret = wait_event_interruptible_timeout(adv7511->wq,
|
||||
adv7511_is_interrupt_pending(adv7511, irq),
|
||||
msecs_to_jiffies(timeout));
|
||||
if (ret <= 0)
|
||||
return 0;
|
||||
pending = adv7511_is_interrupt_pending(adv7511, irq);
|
||||
} else {
|
||||
if (timeout < 25)
|
||||
timeout = 25;
|
||||
do {
|
||||
pending = adv7511_is_interrupt_pending(adv7511, irq);
|
||||
if (pending)
|
||||
break;
|
||||
msleep(25);
|
||||
timeout -= 25;
|
||||
} while (timeout >= 25);
|
||||
if (adv7511->i2c_main->irq)
|
||||
wake_up_all(&adv7511->wq);
|
||||
}
|
||||
|
||||
return pending;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t adv7511_irq_handler(int irq, void *devid)
|
||||
{
|
||||
struct adv7511 *adv7511 = devid;
|
||||
int ret;
|
||||
|
||||
ret = adv7511_irq_process(adv7511);
|
||||
return ret < 0 ? IRQ_NONE : IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* EDID retrieval
|
||||
*/
|
||||
|
||||
static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (adv7511->i2c_main->irq) {
|
||||
ret = wait_event_interruptible_timeout(adv7511->wq,
|
||||
adv7511->edid_read, msecs_to_jiffies(timeout));
|
||||
} else {
|
||||
for (; timeout > 0; timeout -= 25) {
|
||||
ret = adv7511_irq_process(adv7511);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
if (adv7511->edid_read)
|
||||
break;
|
||||
|
||||
msleep(25);
|
||||
}
|
||||
}
|
||||
|
||||
return adv7511->edid_read ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
|
||||
size_t len)
|
||||
{
|
||||
@ -463,19 +508,14 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
|
||||
return ret;
|
||||
|
||||
if (status != 2) {
|
||||
adv7511->edid_read = false;
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT,
|
||||
block);
|
||||
ret = adv7511_wait_for_interrupt(adv7511,
|
||||
ADV7511_INT0_EDID_READY |
|
||||
ADV7511_INT1_DDC_ERROR, 200);
|
||||
|
||||
if (!(ret & ADV7511_INT0_EDID_READY))
|
||||
return -EIO;
|
||||
ret = adv7511_wait_for_edid(adv7511, 200);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
|
||||
ADV7511_INT0_EDID_READY | ADV7511_INT1_DDC_ERROR);
|
||||
|
||||
/* Break this apart, hopefully more I2C controllers will
|
||||
* support 64 byte transfers than 256 byte transfers
|
||||
*/
|
||||
@ -526,9 +566,11 @@ static int adv7511_get_modes(struct drm_encoder *encoder,
|
||||
unsigned int count;
|
||||
|
||||
/* Reading the EDID only works if the device is powered */
|
||||
if (adv7511->dpms_mode != DRM_MODE_DPMS_ON) {
|
||||
if (!adv7511->powered) {
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
|
||||
ADV7511_INT0_EDID_READY | ADV7511_INT1_DDC_ERROR);
|
||||
ADV7511_INT0_EDID_READY);
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT(1),
|
||||
ADV7511_INT1_DDC_ERROR);
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
|
||||
ADV7511_POWER_POWER_DOWN, 0);
|
||||
adv7511->current_edid_segment = -1;
|
||||
@ -536,7 +578,7 @@ static int adv7511_get_modes(struct drm_encoder *encoder,
|
||||
|
||||
edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511);
|
||||
|
||||
if (adv7511->dpms_mode != DRM_MODE_DPMS_ON)
|
||||
if (!adv7511->powered)
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
|
||||
ADV7511_POWER_POWER_DOWN,
|
||||
ADV7511_POWER_POWER_DOWN);
|
||||
@ -558,41 +600,10 @@ static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
adv7511->current_edid_segment = -1;
|
||||
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
|
||||
ADV7511_INT0_EDID_READY | ADV7511_INT1_DDC_ERROR);
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
|
||||
ADV7511_POWER_POWER_DOWN, 0);
|
||||
/*
|
||||
* Per spec it is allowed to pulse the HDP signal to indicate
|
||||
* that the EDID information has changed. Some monitors do this
|
||||
* when they wakeup from standby or are enabled. When the HDP
|
||||
* goes low the adv7511 is reset and the outputs are disabled
|
||||
* which might cause the monitor to go to standby again. To
|
||||
* avoid this we ignore the HDP pin for the first few seconds
|
||||
* after enabeling the output.
|
||||
*/
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
|
||||
ADV7511_REG_POWER2_HDP_SRC_MASK,
|
||||
ADV7511_REG_POWER2_HDP_SRC_NONE);
|
||||
/* Most of the registers are reset during power down or
|
||||
* when HPD is low
|
||||
*/
|
||||
regcache_sync(adv7511->regmap);
|
||||
break;
|
||||
default:
|
||||
/* TODO: setup additional power down modes */
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
|
||||
ADV7511_POWER_POWER_DOWN,
|
||||
ADV7511_POWER_POWER_DOWN);
|
||||
regcache_mark_dirty(adv7511->regmap);
|
||||
break;
|
||||
}
|
||||
|
||||
adv7511->dpms_mode = mode;
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
adv7511_power_on(adv7511);
|
||||
else
|
||||
adv7511_power_off(adv7511);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
@ -620,10 +631,9 @@ adv7511_encoder_detect(struct drm_encoder *encoder,
|
||||
* there is a pending HPD interrupt and the cable is connected there was
|
||||
* at least one transition from disconnected to connected and the chip
|
||||
* has to be reinitialized. */
|
||||
if (status == connector_status_connected && hpd &&
|
||||
adv7511->dpms_mode == DRM_MODE_DPMS_ON) {
|
||||
if (status == connector_status_connected && hpd && adv7511->powered) {
|
||||
regcache_mark_dirty(adv7511->regmap);
|
||||
adv7511_encoder_dpms(encoder, adv7511->dpms_mode);
|
||||
adv7511_power_on(adv7511);
|
||||
adv7511_get_modes(encoder, connector);
|
||||
if (adv7511->status == connector_status_connected)
|
||||
status = connector_status_disconnected;
|
||||
@ -858,7 +868,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
if (!adv7511)
|
||||
return -ENOMEM;
|
||||
|
||||
adv7511->dpms_mode = DRM_MODE_DPMS_OFF;
|
||||
adv7511->powered = false;
|
||||
adv7511->status = connector_status_disconnected;
|
||||
|
||||
ret = adv7511_parse_dt(dev->of_node, &link_config);
|
||||
@ -918,10 +928,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL,
|
||||
ADV7511_CEC_CTRL_POWER_DOWN);
|
||||
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
|
||||
ADV7511_POWER_POWER_DOWN, ADV7511_POWER_POWER_DOWN);
|
||||
|
||||
adv7511->current_edid_segment = -1;
|
||||
adv7511_power_off(adv7511);
|
||||
|
||||
i2c_set_clientdata(i2c, adv7511);
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/i2c/tda998x.h>
|
||||
|
||||
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
|
||||
@ -387,7 +388,7 @@ set_page(struct tda998x_priv *priv, uint16_t reg)
|
||||
};
|
||||
int ret = i2c_master_send(client, buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "setpage %04x err %d\n",
|
||||
dev_err(&client->dev, "%s %04x err %d\n", __func__,
|
||||
reg, ret);
|
||||
return ret;
|
||||
}
|
||||
@ -1035,8 +1036,9 @@ tda998x_encoder_detect(struct tda998x_priv *priv)
|
||||
connector_status_disconnected;
|
||||
}
|
||||
|
||||
static int read_edid_block(struct tda998x_priv *priv, uint8_t *buf, int blk)
|
||||
static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length)
|
||||
{
|
||||
struct tda998x_priv *priv = data;
|
||||
uint8_t offset, segptr;
|
||||
int ret, i;
|
||||
|
||||
@ -1080,8 +1082,8 @@ static int read_edid_block(struct tda998x_priv *priv, uint8_t *buf, int blk)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
ret = reg_read_range(priv, REG_EDID_DATA_0, buf, EDID_LENGTH);
|
||||
if (ret != EDID_LENGTH) {
|
||||
ret = reg_read_range(priv, REG_EDID_DATA_0, buf, length);
|
||||
if (ret != length) {
|
||||
dev_err(&priv->hdmi->dev, "failed to read edid block %d: %d\n",
|
||||
blk, ret);
|
||||
return ret;
|
||||
@ -1090,82 +1092,31 @@ static int read_edid_block(struct tda998x_priv *priv, uint8_t *buf, int blk)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t *do_get_edid(struct tda998x_priv *priv)
|
||||
{
|
||||
int j, valid_extensions = 0;
|
||||
uint8_t *block, *new;
|
||||
bool print_bad_edid = drm_debug & DRM_UT_KMS;
|
||||
|
||||
if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (priv->rev == TDA19988)
|
||||
reg_clear(priv, REG_TX4, TX4_PD_RAM);
|
||||
|
||||
/* base block fetch */
|
||||
if (read_edid_block(priv, block, 0))
|
||||
goto fail;
|
||||
|
||||
if (!drm_edid_block_valid(block, 0, print_bad_edid))
|
||||
goto fail;
|
||||
|
||||
/* if there's no extensions, we're done */
|
||||
if (block[0x7e] == 0)
|
||||
goto done;
|
||||
|
||||
new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL);
|
||||
if (!new)
|
||||
goto fail;
|
||||
block = new;
|
||||
|
||||
for (j = 1; j <= block[0x7e]; j++) {
|
||||
uint8_t *ext_block = block + (valid_extensions + 1) * EDID_LENGTH;
|
||||
if (read_edid_block(priv, ext_block, j))
|
||||
goto fail;
|
||||
|
||||
if (!drm_edid_block_valid(ext_block, j, print_bad_edid))
|
||||
goto fail;
|
||||
|
||||
valid_extensions++;
|
||||
}
|
||||
|
||||
if (valid_extensions != block[0x7e]) {
|
||||
block[EDID_LENGTH-1] += block[0x7e] - valid_extensions;
|
||||
block[0x7e] = valid_extensions;
|
||||
new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL);
|
||||
if (!new)
|
||||
goto fail;
|
||||
block = new;
|
||||
}
|
||||
|
||||
done:
|
||||
if (priv->rev == TDA19988)
|
||||
reg_set(priv, REG_TX4, TX4_PD_RAM);
|
||||
|
||||
return block;
|
||||
|
||||
fail:
|
||||
if (priv->rev == TDA19988)
|
||||
reg_set(priv, REG_TX4, TX4_PD_RAM);
|
||||
dev_warn(&priv->hdmi->dev, "failed to read EDID\n");
|
||||
kfree(block);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
tda998x_encoder_get_modes(struct tda998x_priv *priv,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct edid *edid = (struct edid *)do_get_edid(priv);
|
||||
int n = 0;
|
||||
struct edid *edid;
|
||||
int n;
|
||||
|
||||
if (edid) {
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
n = drm_add_edid_modes(connector, edid);
|
||||
priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
|
||||
kfree(edid);
|
||||
if (priv->rev == TDA19988)
|
||||
reg_clear(priv, REG_TX4, TX4_PD_RAM);
|
||||
|
||||
edid = drm_do_get_edid(connector, read_edid_block, priv);
|
||||
|
||||
if (priv->rev == TDA19988)
|
||||
reg_set(priv, REG_TX4, TX4_PD_RAM);
|
||||
|
||||
if (!edid) {
|
||||
dev_warn(&priv->hdmi->dev, "failed to read EDID\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
n = drm_add_edid_modes(connector, edid);
|
||||
priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
|
||||
kfree(edid);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
@ -1547,6 +1498,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct drm_device *drm = data;
|
||||
struct tda998x_priv2 *priv;
|
||||
uint32_t crtcs = 0;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
@ -1555,9 +1507,18 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
|
||||
if (dev->of_node)
|
||||
crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
|
||||
|
||||
/* If no CRTCs were found, fall back to our old behaviour */
|
||||
if (crtcs == 0) {
|
||||
dev_warn(dev, "Falling back to first CRTC\n");
|
||||
crtcs = 1 << 0;
|
||||
}
|
||||
|
||||
priv->base.encoder = &priv->encoder;
|
||||
priv->connector.interlace_allowed = 1;
|
||||
priv->encoder.possible_crtcs = 1 << 0;
|
||||
priv->encoder.possible_crtcs = crtcs;
|
||||
|
||||
ret = tda998x_create(client, &priv->base);
|
||||
if (ret)
|
||||
|
@ -28,6 +28,7 @@ i915-y += i915_cmd_parser.o \
|
||||
i915_gem_execbuffer.o \
|
||||
i915_gem_gtt.o \
|
||||
i915_gem.o \
|
||||
i915_gem_shrinker.o \
|
||||
i915_gem_stolen.o \
|
||||
i915_gem_tiling.o \
|
||||
i915_gem_userptr.o \
|
||||
@ -83,9 +84,11 @@ i915-y += dvo_ch7017.o \
|
||||
intel_sdvo.o \
|
||||
intel_tv.o
|
||||
|
||||
# virtual gpu code
|
||||
i915-y += i915_vgpu.o
|
||||
|
||||
# legacy horrors
|
||||
i915-y += i915_dma.o \
|
||||
i915_ums.o
|
||||
i915-y += i915_dma.o
|
||||
|
||||
obj-$(CONFIG_DRM_I915) += i915.o
|
||||
|
||||
|
@ -818,23 +818,28 @@ static bool valid_reg(const u32 *table, int count, u32 addr)
|
||||
return false;
|
||||
}
|
||||
|
||||
static u32 *vmap_batch(struct drm_i915_gem_object *obj)
|
||||
static u32 *vmap_batch(struct drm_i915_gem_object *obj,
|
||||
unsigned start, unsigned len)
|
||||
{
|
||||
int i;
|
||||
void *addr = NULL;
|
||||
struct sg_page_iter sg_iter;
|
||||
int first_page = start >> PAGE_SHIFT;
|
||||
int last_page = (len + start + 4095) >> PAGE_SHIFT;
|
||||
int npages = last_page - first_page;
|
||||
struct page **pages;
|
||||
|
||||
pages = drm_malloc_ab(obj->base.size >> PAGE_SHIFT, sizeof(*pages));
|
||||
pages = drm_malloc_ab(npages, sizeof(*pages));
|
||||
if (pages == NULL) {
|
||||
DRM_DEBUG_DRIVER("Failed to get space for pages\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) {
|
||||
pages[i] = sg_page_iter_page(&sg_iter);
|
||||
i++;
|
||||
for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, first_page) {
|
||||
pages[i++] = sg_page_iter_page(&sg_iter);
|
||||
if (i == npages)
|
||||
break;
|
||||
}
|
||||
|
||||
addr = vmap(pages, i, 0, PAGE_KERNEL);
|
||||
@ -855,61 +860,61 @@ static u32 *copy_batch(struct drm_i915_gem_object *dest_obj,
|
||||
u32 batch_start_offset,
|
||||
u32 batch_len)
|
||||
{
|
||||
int ret = 0;
|
||||
int needs_clflush = 0;
|
||||
u32 *src_base, *dest_base = NULL;
|
||||
u32 *src_addr, *dest_addr;
|
||||
u32 offset = batch_start_offset / sizeof(*dest_addr);
|
||||
u32 end = batch_start_offset + batch_len;
|
||||
void *src_base, *src;
|
||||
void *dst = NULL;
|
||||
int ret;
|
||||
|
||||
if (end > dest_obj->base.size || end > src_obj->base.size)
|
||||
if (batch_len > dest_obj->base.size ||
|
||||
batch_len + batch_start_offset > src_obj->base.size)
|
||||
return ERR_PTR(-E2BIG);
|
||||
|
||||
ret = i915_gem_obj_prepare_shmem_read(src_obj, &needs_clflush);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("CMD: failed to prep read\n");
|
||||
DRM_DEBUG_DRIVER("CMD: failed to prepare shadow batch\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
src_base = vmap_batch(src_obj);
|
||||
src_base = vmap_batch(src_obj, batch_start_offset, batch_len);
|
||||
if (!src_base) {
|
||||
DRM_DEBUG_DRIVER("CMD: Failed to vmap batch\n");
|
||||
ret = -ENOMEM;
|
||||
goto unpin_src;
|
||||
}
|
||||
|
||||
src_addr = src_base + offset;
|
||||
|
||||
if (needs_clflush)
|
||||
drm_clflush_virt_range((char *)src_addr, batch_len);
|
||||
ret = i915_gem_object_get_pages(dest_obj);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("CMD: Failed to get pages for shadow batch\n");
|
||||
goto unmap_src;
|
||||
}
|
||||
i915_gem_object_pin_pages(dest_obj);
|
||||
|
||||
ret = i915_gem_object_set_to_cpu_domain(dest_obj, true);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("CMD: Failed to set batch CPU domain\n");
|
||||
DRM_DEBUG_DRIVER("CMD: Failed to set shadow batch to CPU\n");
|
||||
goto unmap_src;
|
||||
}
|
||||
|
||||
dest_base = vmap_batch(dest_obj);
|
||||
if (!dest_base) {
|
||||
dst = vmap_batch(dest_obj, 0, batch_len);
|
||||
if (!dst) {
|
||||
DRM_DEBUG_DRIVER("CMD: Failed to vmap shadow batch\n");
|
||||
i915_gem_object_unpin_pages(dest_obj);
|
||||
ret = -ENOMEM;
|
||||
goto unmap_src;
|
||||
}
|
||||
|
||||
dest_addr = dest_base + offset;
|
||||
src = src_base + offset_in_page(batch_start_offset);
|
||||
if (needs_clflush)
|
||||
drm_clflush_virt_range(src, batch_len);
|
||||
|
||||
if (batch_start_offset != 0)
|
||||
memset((u8 *)dest_base, 0, batch_start_offset);
|
||||
|
||||
memcpy(dest_addr, src_addr, batch_len);
|
||||
memset((u8 *)dest_addr + batch_len, 0, dest_obj->base.size - end);
|
||||
memcpy(dst, src, batch_len);
|
||||
|
||||
unmap_src:
|
||||
vunmap(src_base);
|
||||
unpin_src:
|
||||
i915_gem_object_unpin_pages(src_obj);
|
||||
|
||||
return ret ? ERR_PTR(ret) : dest_base;
|
||||
return ret ? ERR_PTR(ret) : dst;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1046,34 +1051,26 @@ int i915_parse_cmds(struct intel_engine_cs *ring,
|
||||
u32 batch_len,
|
||||
bool is_master)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 *cmd, *batch_base, *batch_end;
|
||||
struct drm_i915_cmd_descriptor default_desc = { 0 };
|
||||
bool oacontrol_set = false; /* OACONTROL tracking. See check_cmd() */
|
||||
|
||||
ret = i915_gem_obj_ggtt_pin(shadow_batch_obj, 4096, 0);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("CMD: Failed to pin shadow batch\n");
|
||||
return -1;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
batch_base = copy_batch(shadow_batch_obj, batch_obj,
|
||||
batch_start_offset, batch_len);
|
||||
if (IS_ERR(batch_base)) {
|
||||
DRM_DEBUG_DRIVER("CMD: Failed to copy batch\n");
|
||||
i915_gem_object_ggtt_unpin(shadow_batch_obj);
|
||||
return PTR_ERR(batch_base);
|
||||
}
|
||||
|
||||
cmd = batch_base + (batch_start_offset / sizeof(*cmd));
|
||||
|
||||
/*
|
||||
* We use the batch length as size because the shadow object is as
|
||||
* large or larger and copy_batch() will write MI_NOPs to the extra
|
||||
* space. Parsing should be faster in some cases this way.
|
||||
*/
|
||||
batch_end = cmd + (batch_len / sizeof(*batch_end));
|
||||
batch_end = batch_base + (batch_len / sizeof(*batch_end));
|
||||
|
||||
cmd = batch_base;
|
||||
while (cmd < batch_end) {
|
||||
const struct drm_i915_cmd_descriptor *desc;
|
||||
u32 length;
|
||||
@ -1132,7 +1129,7 @@ int i915_parse_cmds(struct intel_engine_cs *ring,
|
||||
}
|
||||
|
||||
vunmap(batch_base);
|
||||
i915_gem_object_ggtt_unpin(shadow_batch_obj);
|
||||
i915_gem_object_unpin_pages(shadow_batch_obj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -139,10 +139,11 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
|
||||
obj->madv == I915_MADV_DONTNEED ? " purgeable" : "");
|
||||
if (obj->base.name)
|
||||
seq_printf(m, " (name: %d)", obj->base.name);
|
||||
list_for_each_entry(vma, &obj->vma_list, vma_link)
|
||||
list_for_each_entry(vma, &obj->vma_list, vma_link) {
|
||||
if (vma->pin_count > 0)
|
||||
pin_count++;
|
||||
seq_printf(m, " (pinned x %d)", pin_count);
|
||||
}
|
||||
seq_printf(m, " (pinned x %d)", pin_count);
|
||||
if (obj->pin_display)
|
||||
seq_printf(m, " (display)");
|
||||
if (obj->fence_reg != I915_FENCE_REG_NONE)
|
||||
@ -580,7 +581,7 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data)
|
||||
seq_printf(m, "Flip queued on frame %d, (was ready on frame %d), now %d\n",
|
||||
work->flip_queued_vblank,
|
||||
work->flip_ready_vblank,
|
||||
drm_vblank_count(dev, crtc->pipe));
|
||||
drm_crtc_vblank_count(&crtc->base));
|
||||
if (work->enable_stall_check)
|
||||
seq_puts(m, "Stall check enabled, ");
|
||||
else
|
||||
@ -1089,7 +1090,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
|
||||
seq_printf(m, "Current P-state: %d\n",
|
||||
(rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
|
||||
} else if (IS_GEN6(dev) || (IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) ||
|
||||
IS_BROADWELL(dev)) {
|
||||
IS_BROADWELL(dev) || IS_GEN9(dev)) {
|
||||
u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
|
||||
u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS);
|
||||
u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
|
||||
@ -1108,11 +1109,15 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
|
||||
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
|
||||
|
||||
reqf = I915_READ(GEN6_RPNSWREQ);
|
||||
reqf &= ~GEN6_TURBO_DISABLE;
|
||||
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
|
||||
reqf >>= 24;
|
||||
else
|
||||
reqf >>= 25;
|
||||
if (IS_GEN9(dev))
|
||||
reqf >>= 23;
|
||||
else {
|
||||
reqf &= ~GEN6_TURBO_DISABLE;
|
||||
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
|
||||
reqf >>= 24;
|
||||
else
|
||||
reqf >>= 25;
|
||||
}
|
||||
reqf = intel_gpu_freq(dev_priv, reqf);
|
||||
|
||||
rpmodectl = I915_READ(GEN6_RP_CONTROL);
|
||||
@ -1126,7 +1131,9 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
|
||||
rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI);
|
||||
rpcurdown = I915_READ(GEN6_RP_CUR_DOWN);
|
||||
rpprevdown = I915_READ(GEN6_RP_PREV_DOWN);
|
||||
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
|
||||
if (IS_GEN9(dev))
|
||||
cagf = (rpstat & GEN9_CAGF_MASK) >> GEN9_CAGF_SHIFT;
|
||||
else if (IS_HASWELL(dev) || IS_BROADWELL(dev))
|
||||
cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT;
|
||||
else
|
||||
cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT;
|
||||
@ -1152,7 +1159,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
|
||||
pm_ier, pm_imr, pm_isr, pm_iir, pm_mask);
|
||||
seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
|
||||
seq_printf(m, "Render p-state ratio: %d\n",
|
||||
(gt_perf_status & 0xff00) >> 8);
|
||||
(gt_perf_status & (IS_GEN9(dev) ? 0x1ff00 : 0xff00)) >> 8);
|
||||
seq_printf(m, "Render p-state VID: %d\n",
|
||||
gt_perf_status & 0xff);
|
||||
seq_printf(m, "Render p-state limit: %d\n",
|
||||
@ -1177,19 +1184,25 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
|
||||
GEN6_CURBSYTAVG_MASK);
|
||||
|
||||
max_freq = (rp_state_cap & 0xff0000) >> 16;
|
||||
max_freq *= (IS_SKYLAKE(dev) ? GEN9_FREQ_SCALER : 1);
|
||||
seq_printf(m, "Lowest (RPN) frequency: %dMHz\n",
|
||||
intel_gpu_freq(dev_priv, max_freq));
|
||||
|
||||
max_freq = (rp_state_cap & 0xff00) >> 8;
|
||||
max_freq *= (IS_SKYLAKE(dev) ? GEN9_FREQ_SCALER : 1);
|
||||
seq_printf(m, "Nominal (RP1) frequency: %dMHz\n",
|
||||
intel_gpu_freq(dev_priv, max_freq));
|
||||
|
||||
max_freq = rp_state_cap & 0xff;
|
||||
max_freq *= (IS_SKYLAKE(dev) ? GEN9_FREQ_SCALER : 1);
|
||||
seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n",
|
||||
intel_gpu_freq(dev_priv, max_freq));
|
||||
|
||||
seq_printf(m, "Max overclocked frequency: %dMHz\n",
|
||||
intel_gpu_freq(dev_priv, dev_priv->rps.max_freq));
|
||||
|
||||
seq_printf(m, "Idle freq: %d MHz\n",
|
||||
intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq));
|
||||
} else if (IS_VALLEYVIEW(dev)) {
|
||||
u32 freq_sts;
|
||||
|
||||
@ -1204,6 +1217,9 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
|
||||
seq_printf(m, "min GPU freq: %d MHz\n",
|
||||
intel_gpu_freq(dev_priv, dev_priv->rps.min_freq));
|
||||
|
||||
seq_printf(m, "idle GPU freq: %d MHz\n",
|
||||
intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq));
|
||||
|
||||
seq_printf(m,
|
||||
"efficient (RPe) frequency: %d MHz\n",
|
||||
intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq));
|
||||
@ -1778,11 +1794,12 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
|
||||
ifbdev = dev_priv->fbdev;
|
||||
fb = to_intel_framebuffer(ifbdev->helper.fb);
|
||||
|
||||
seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, refcount %d, obj ",
|
||||
seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ",
|
||||
fb->base.width,
|
||||
fb->base.height,
|
||||
fb->base.depth,
|
||||
fb->base.bits_per_pixel,
|
||||
fb->base.modifier[0],
|
||||
atomic_read(&fb->base.refcount.refcount));
|
||||
describe_obj(m, fb->obj);
|
||||
seq_putc(m, '\n');
|
||||
@ -1793,11 +1810,12 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
|
||||
if (ifbdev && &fb->base == ifbdev->helper.fb)
|
||||
continue;
|
||||
|
||||
seq_printf(m, "user size: %d x %d, depth %d, %d bpp, refcount %d, obj ",
|
||||
seq_printf(m, "user size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ",
|
||||
fb->base.width,
|
||||
fb->base.height,
|
||||
fb->base.depth,
|
||||
fb->base.bits_per_pixel,
|
||||
fb->base.modifier[0],
|
||||
atomic_read(&fb->base.refcount.refcount));
|
||||
describe_obj(m, fb->obj);
|
||||
seq_putc(m, '\n');
|
||||
@ -1828,18 +1846,6 @@ static int i915_context_status(struct seq_file *m, void *unused)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dev_priv->ips.pwrctx) {
|
||||
seq_puts(m, "power context ");
|
||||
describe_obj(m, dev_priv->ips.pwrctx);
|
||||
seq_putc(m, '\n');
|
||||
}
|
||||
|
||||
if (dev_priv->ips.renderctx) {
|
||||
seq_puts(m, "render context ");
|
||||
describe_obj(m, dev_priv->ips.renderctx);
|
||||
seq_putc(m, '\n');
|
||||
}
|
||||
|
||||
list_for_each_entry(ctx, &dev_priv->context_list, link) {
|
||||
if (!i915.enable_execlists &&
|
||||
ctx->legacy_hw_ctx.rcs_state == NULL)
|
||||
@ -2183,7 +2189,7 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
|
||||
struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
|
||||
|
||||
seq_puts(m, "aliasing PPGTT:\n");
|
||||
seq_printf(m, "pd gtt offset: 0x%08x\n", ppgtt->pd_offset);
|
||||
seq_printf(m, "pd gtt offset: 0x%08x\n", ppgtt->pd.pd_offset);
|
||||
|
||||
ppgtt->debug_dump(ppgtt, m);
|
||||
}
|
||||
@ -2243,6 +2249,11 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
|
||||
enum pipe pipe;
|
||||
bool enabled = false;
|
||||
|
||||
if (!HAS_PSR(dev)) {
|
||||
seq_puts(m, "PSR not supported\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
intel_runtime_pm_get(dev_priv);
|
||||
|
||||
mutex_lock(&dev_priv->psr.lock);
|
||||
@ -2255,17 +2266,15 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
|
||||
seq_printf(m, "Re-enable work scheduled: %s\n",
|
||||
yesno(work_busy(&dev_priv->psr.work.work)));
|
||||
|
||||
if (HAS_PSR(dev)) {
|
||||
if (HAS_DDI(dev))
|
||||
enabled = I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
|
||||
else {
|
||||
for_each_pipe(dev_priv, pipe) {
|
||||
stat[pipe] = I915_READ(VLV_PSRSTAT(pipe)) &
|
||||
VLV_EDP_PSR_CURR_STATE_MASK;
|
||||
if ((stat[pipe] == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
|
||||
(stat[pipe] == VLV_EDP_PSR_ACTIVE_SF_UPDATE))
|
||||
enabled = true;
|
||||
}
|
||||
if (HAS_DDI(dev))
|
||||
enabled = I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
|
||||
else {
|
||||
for_each_pipe(dev_priv, pipe) {
|
||||
stat[pipe] = I915_READ(VLV_PSRSTAT(pipe)) &
|
||||
VLV_EDP_PSR_CURR_STATE_MASK;
|
||||
if ((stat[pipe] == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
|
||||
(stat[pipe] == VLV_EDP_PSR_ACTIVE_SF_UPDATE))
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
seq_printf(m, "HW Enabled & Active bit: %s", yesno(enabled));
|
||||
@ -2282,7 +2291,7 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
|
||||
yesno((bool)dev_priv->psr.link_standby));
|
||||
|
||||
/* CHV PSR has no kind of performance counter */
|
||||
if (HAS_PSR(dev) && HAS_DDI(dev)) {
|
||||
if (HAS_DDI(dev)) {
|
||||
psrperf = I915_READ(EDP_PSR_PERF_CNT(dev)) &
|
||||
EDP_PSR_PERF_CNT_MASK;
|
||||
|
||||
@ -2305,8 +2314,7 @@ static int i915_sink_crc(struct seq_file *m, void *data)
|
||||
u8 crc[6];
|
||||
|
||||
drm_modeset_lock_all(dev);
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list,
|
||||
base.head) {
|
||||
for_each_intel_connector(dev, connector) {
|
||||
|
||||
if (connector->base.dpms != DRM_MODE_DPMS_ON)
|
||||
continue;
|
||||
@ -2674,7 +2682,8 @@ static int i915_display_info(struct seq_file *m, void *unused)
|
||||
active = cursor_position(dev, crtc->pipe, &x, &y);
|
||||
seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x, active? %s\n",
|
||||
yesno(crtc->cursor_base),
|
||||
x, y, crtc->cursor_width, crtc->cursor_height,
|
||||
x, y, crtc->base.cursor->state->crtc_w,
|
||||
crtc->base.cursor->state->crtc_h,
|
||||
crtc->cursor_addr, yesno(active));
|
||||
}
|
||||
|
||||
@ -2850,7 +2859,7 @@ static int i915_ddb_info(struct seq_file *m, void *unused)
|
||||
for_each_pipe(dev_priv, pipe) {
|
||||
seq_printf(m, "Pipe %c\n", pipe_name(pipe));
|
||||
|
||||
for_each_plane(pipe, plane) {
|
||||
for_each_plane(dev_priv, pipe, plane) {
|
||||
entry = &ddb->plane[pipe][plane];
|
||||
seq_printf(m, " Plane%-8d%8u%8u%8u\n", plane + 1,
|
||||
entry->start, entry->end,
|
||||
@ -2867,6 +2876,115 @@ static int i915_ddb_info(struct seq_file *m, void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drrs_status_per_crtc(struct seq_file *m,
|
||||
struct drm_device *dev, struct intel_crtc *intel_crtc)
|
||||
{
|
||||
struct intel_encoder *intel_encoder;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct i915_drrs *drrs = &dev_priv->drrs;
|
||||
int vrefresh = 0;
|
||||
|
||||
for_each_encoder_on_crtc(dev, &intel_crtc->base, intel_encoder) {
|
||||
/* Encoder connected on this CRTC */
|
||||
switch (intel_encoder->type) {
|
||||
case INTEL_OUTPUT_EDP:
|
||||
seq_puts(m, "eDP:\n");
|
||||
break;
|
||||
case INTEL_OUTPUT_DSI:
|
||||
seq_puts(m, "DSI:\n");
|
||||
break;
|
||||
case INTEL_OUTPUT_HDMI:
|
||||
seq_puts(m, "HDMI:\n");
|
||||
break;
|
||||
case INTEL_OUTPUT_DISPLAYPORT:
|
||||
seq_puts(m, "DP:\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(m, "Other encoder (id=%d).\n",
|
||||
intel_encoder->type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev_priv->vbt.drrs_type == STATIC_DRRS_SUPPORT)
|
||||
seq_puts(m, "\tVBT: DRRS_type: Static");
|
||||
else if (dev_priv->vbt.drrs_type == SEAMLESS_DRRS_SUPPORT)
|
||||
seq_puts(m, "\tVBT: DRRS_type: Seamless");
|
||||
else if (dev_priv->vbt.drrs_type == DRRS_NOT_SUPPORTED)
|
||||
seq_puts(m, "\tVBT: DRRS_type: None");
|
||||
else
|
||||
seq_puts(m, "\tVBT: DRRS_type: FIXME: Unrecognized Value");
|
||||
|
||||
seq_puts(m, "\n\n");
|
||||
|
||||
if (intel_crtc->config->has_drrs) {
|
||||
struct intel_panel *panel;
|
||||
|
||||
mutex_lock(&drrs->mutex);
|
||||
/* DRRS Supported */
|
||||
seq_puts(m, "\tDRRS Supported: Yes\n");
|
||||
|
||||
/* disable_drrs() will make drrs->dp NULL */
|
||||
if (!drrs->dp) {
|
||||
seq_puts(m, "Idleness DRRS: Disabled");
|
||||
mutex_unlock(&drrs->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
panel = &drrs->dp->attached_connector->panel;
|
||||
seq_printf(m, "\t\tBusy_frontbuffer_bits: 0x%X",
|
||||
drrs->busy_frontbuffer_bits);
|
||||
|
||||
seq_puts(m, "\n\t\t");
|
||||
if (drrs->refresh_rate_type == DRRS_HIGH_RR) {
|
||||
seq_puts(m, "DRRS_State: DRRS_HIGH_RR\n");
|
||||
vrefresh = panel->fixed_mode->vrefresh;
|
||||
} else if (drrs->refresh_rate_type == DRRS_LOW_RR) {
|
||||
seq_puts(m, "DRRS_State: DRRS_LOW_RR\n");
|
||||
vrefresh = panel->downclock_mode->vrefresh;
|
||||
} else {
|
||||
seq_printf(m, "DRRS_State: Unknown(%d)\n",
|
||||
drrs->refresh_rate_type);
|
||||
mutex_unlock(&drrs->mutex);
|
||||
return;
|
||||
}
|
||||
seq_printf(m, "\t\tVrefresh: %d", vrefresh);
|
||||
|
||||
seq_puts(m, "\n\t\t");
|
||||
mutex_unlock(&drrs->mutex);
|
||||
} else {
|
||||
/* DRRS not supported. Print the VBT parameter*/
|
||||
seq_puts(m, "\tDRRS Supported : No");
|
||||
}
|
||||
seq_puts(m, "\n");
|
||||
}
|
||||
|
||||
static int i915_drrs_status(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct drm_info_node *node = m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct intel_crtc *intel_crtc;
|
||||
int active_crtc_cnt = 0;
|
||||
|
||||
for_each_intel_crtc(dev, intel_crtc) {
|
||||
drm_modeset_lock(&intel_crtc->base.mutex, NULL);
|
||||
|
||||
if (intel_crtc->active) {
|
||||
active_crtc_cnt++;
|
||||
seq_printf(m, "\nCRTC %d: ", active_crtc_cnt);
|
||||
|
||||
drrs_status_per_crtc(m, dev, intel_crtc);
|
||||
}
|
||||
|
||||
drm_modeset_unlock(&intel_crtc->base.mutex);
|
||||
}
|
||||
|
||||
if (!active_crtc_cnt)
|
||||
seq_puts(m, "No active crtc found\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pipe_crc_info {
|
||||
const char *name;
|
||||
struct drm_device *dev;
|
||||
@ -4189,7 +4307,7 @@ i915_max_freq_set(void *data, u64 val)
|
||||
{
|
||||
struct drm_device *dev = data;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 rp_state_cap, hw_max, hw_min;
|
||||
u32 hw_max, hw_min;
|
||||
int ret;
|
||||
|
||||
if (INTEL_INFO(dev)->gen < 6)
|
||||
@ -4206,18 +4324,10 @@ i915_max_freq_set(void *data, u64 val)
|
||||
/*
|
||||
* Turbo will still be enabled, but won't go above the set value.
|
||||
*/
|
||||
if (IS_VALLEYVIEW(dev)) {
|
||||
val = intel_freq_opcode(dev_priv, val);
|
||||
val = intel_freq_opcode(dev_priv, val);
|
||||
|
||||
hw_max = dev_priv->rps.max_freq;
|
||||
hw_min = dev_priv->rps.min_freq;
|
||||
} else {
|
||||
val = intel_freq_opcode(dev_priv, val);
|
||||
|
||||
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
|
||||
hw_max = dev_priv->rps.max_freq;
|
||||
hw_min = (rp_state_cap >> 16) & 0xff;
|
||||
}
|
||||
hw_max = dev_priv->rps.max_freq;
|
||||
hw_min = dev_priv->rps.min_freq;
|
||||
|
||||
if (val < hw_min || val > hw_max || val < dev_priv->rps.min_freq_softlimit) {
|
||||
mutex_unlock(&dev_priv->rps.hw_lock);
|
||||
@ -4226,10 +4336,7 @@ i915_max_freq_set(void *data, u64 val)
|
||||
|
||||
dev_priv->rps.max_freq_softlimit = val;
|
||||
|
||||
if (IS_VALLEYVIEW(dev))
|
||||
valleyview_set_rps(dev, val);
|
||||
else
|
||||
gen6_set_rps(dev, val);
|
||||
intel_set_rps(dev, val);
|
||||
|
||||
mutex_unlock(&dev_priv->rps.hw_lock);
|
||||
|
||||
@ -4267,7 +4374,7 @@ i915_min_freq_set(void *data, u64 val)
|
||||
{
|
||||
struct drm_device *dev = data;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 rp_state_cap, hw_max, hw_min;
|
||||
u32 hw_max, hw_min;
|
||||
int ret;
|
||||
|
||||
if (INTEL_INFO(dev)->gen < 6)
|
||||
@ -4284,18 +4391,10 @@ i915_min_freq_set(void *data, u64 val)
|
||||
/*
|
||||
* Turbo will still be enabled, but won't go below the set value.
|
||||
*/
|
||||
if (IS_VALLEYVIEW(dev)) {
|
||||
val = intel_freq_opcode(dev_priv, val);
|
||||
val = intel_freq_opcode(dev_priv, val);
|
||||
|
||||
hw_max = dev_priv->rps.max_freq;
|
||||
hw_min = dev_priv->rps.min_freq;
|
||||
} else {
|
||||
val = intel_freq_opcode(dev_priv, val);
|
||||
|
||||
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
|
||||
hw_max = dev_priv->rps.max_freq;
|
||||
hw_min = (rp_state_cap >> 16) & 0xff;
|
||||
}
|
||||
hw_max = dev_priv->rps.max_freq;
|
||||
hw_min = dev_priv->rps.min_freq;
|
||||
|
||||
if (val < hw_min || val > hw_max || val > dev_priv->rps.max_freq_softlimit) {
|
||||
mutex_unlock(&dev_priv->rps.hw_lock);
|
||||
@ -4304,10 +4403,7 @@ i915_min_freq_set(void *data, u64 val)
|
||||
|
||||
dev_priv->rps.min_freq_softlimit = val;
|
||||
|
||||
if (IS_VALLEYVIEW(dev))
|
||||
valleyview_set_rps(dev, val);
|
||||
else
|
||||
gen6_set_rps(dev, val);
|
||||
intel_set_rps(dev, val);
|
||||
|
||||
mutex_unlock(&dev_priv->rps.hw_lock);
|
||||
|
||||
@ -4374,6 +4470,112 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_cache_sharing_fops,
|
||||
i915_cache_sharing_get, i915_cache_sharing_set,
|
||||
"%llu\n");
|
||||
|
||||
static int i915_sseu_status(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
unsigned int s_tot = 0, ss_tot = 0, ss_per = 0, eu_tot = 0, eu_per = 0;
|
||||
|
||||
if ((INTEL_INFO(dev)->gen < 8) || IS_BROADWELL(dev))
|
||||
return -ENODEV;
|
||||
|
||||
seq_puts(m, "SSEU Device Info\n");
|
||||
seq_printf(m, " Available Slice Total: %u\n",
|
||||
INTEL_INFO(dev)->slice_total);
|
||||
seq_printf(m, " Available Subslice Total: %u\n",
|
||||
INTEL_INFO(dev)->subslice_total);
|
||||
seq_printf(m, " Available Subslice Per Slice: %u\n",
|
||||
INTEL_INFO(dev)->subslice_per_slice);
|
||||
seq_printf(m, " Available EU Total: %u\n",
|
||||
INTEL_INFO(dev)->eu_total);
|
||||
seq_printf(m, " Available EU Per Subslice: %u\n",
|
||||
INTEL_INFO(dev)->eu_per_subslice);
|
||||
seq_printf(m, " Has Slice Power Gating: %s\n",
|
||||
yesno(INTEL_INFO(dev)->has_slice_pg));
|
||||
seq_printf(m, " Has Subslice Power Gating: %s\n",
|
||||
yesno(INTEL_INFO(dev)->has_subslice_pg));
|
||||
seq_printf(m, " Has EU Power Gating: %s\n",
|
||||
yesno(INTEL_INFO(dev)->has_eu_pg));
|
||||
|
||||
seq_puts(m, "SSEU Device Status\n");
|
||||
if (IS_CHERRYVIEW(dev)) {
|
||||
const int ss_max = 2;
|
||||
int ss;
|
||||
u32 sig1[ss_max], sig2[ss_max];
|
||||
|
||||
sig1[0] = I915_READ(CHV_POWER_SS0_SIG1);
|
||||
sig1[1] = I915_READ(CHV_POWER_SS1_SIG1);
|
||||
sig2[0] = I915_READ(CHV_POWER_SS0_SIG2);
|
||||
sig2[1] = I915_READ(CHV_POWER_SS1_SIG2);
|
||||
|
||||
for (ss = 0; ss < ss_max; ss++) {
|
||||
unsigned int eu_cnt;
|
||||
|
||||
if (sig1[ss] & CHV_SS_PG_ENABLE)
|
||||
/* skip disabled subslice */
|
||||
continue;
|
||||
|
||||
s_tot = 1;
|
||||
ss_per++;
|
||||
eu_cnt = ((sig1[ss] & CHV_EU08_PG_ENABLE) ? 0 : 2) +
|
||||
((sig1[ss] & CHV_EU19_PG_ENABLE) ? 0 : 2) +
|
||||
((sig1[ss] & CHV_EU210_PG_ENABLE) ? 0 : 2) +
|
||||
((sig2[ss] & CHV_EU311_PG_ENABLE) ? 0 : 2);
|
||||
eu_tot += eu_cnt;
|
||||
eu_per = max(eu_per, eu_cnt);
|
||||
}
|
||||
ss_tot = ss_per;
|
||||
} else if (IS_SKYLAKE(dev)) {
|
||||
const int s_max = 3, ss_max = 4;
|
||||
int s, ss;
|
||||
u32 s_reg[s_max], eu_reg[2*s_max], eu_mask[2];
|
||||
|
||||
s_reg[0] = I915_READ(GEN9_SLICE0_PGCTL_ACK);
|
||||
s_reg[1] = I915_READ(GEN9_SLICE1_PGCTL_ACK);
|
||||
s_reg[2] = I915_READ(GEN9_SLICE2_PGCTL_ACK);
|
||||
eu_reg[0] = I915_READ(GEN9_SLICE0_SS01_EU_PGCTL_ACK);
|
||||
eu_reg[1] = I915_READ(GEN9_SLICE0_SS23_EU_PGCTL_ACK);
|
||||
eu_reg[2] = I915_READ(GEN9_SLICE1_SS01_EU_PGCTL_ACK);
|
||||
eu_reg[3] = I915_READ(GEN9_SLICE1_SS23_EU_PGCTL_ACK);
|
||||
eu_reg[4] = I915_READ(GEN9_SLICE2_SS01_EU_PGCTL_ACK);
|
||||
eu_reg[5] = I915_READ(GEN9_SLICE2_SS23_EU_PGCTL_ACK);
|
||||
eu_mask[0] = GEN9_PGCTL_SSA_EU08_ACK |
|
||||
GEN9_PGCTL_SSA_EU19_ACK |
|
||||
GEN9_PGCTL_SSA_EU210_ACK |
|
||||
GEN9_PGCTL_SSA_EU311_ACK;
|
||||
eu_mask[1] = GEN9_PGCTL_SSB_EU08_ACK |
|
||||
GEN9_PGCTL_SSB_EU19_ACK |
|
||||
GEN9_PGCTL_SSB_EU210_ACK |
|
||||
GEN9_PGCTL_SSB_EU311_ACK;
|
||||
|
||||
for (s = 0; s < s_max; s++) {
|
||||
if ((s_reg[s] & GEN9_PGCTL_SLICE_ACK) == 0)
|
||||
/* skip disabled slice */
|
||||
continue;
|
||||
|
||||
s_tot++;
|
||||
ss_per = INTEL_INFO(dev)->subslice_per_slice;
|
||||
ss_tot += ss_per;
|
||||
for (ss = 0; ss < ss_max; ss++) {
|
||||
unsigned int eu_cnt;
|
||||
|
||||
eu_cnt = 2 * hweight32(eu_reg[2*s + ss/2] &
|
||||
eu_mask[ss%2]);
|
||||
eu_tot += eu_cnt;
|
||||
eu_per = max(eu_per, eu_cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
seq_printf(m, " Enabled Slice Total: %u\n", s_tot);
|
||||
seq_printf(m, " Enabled Subslice Total: %u\n", ss_tot);
|
||||
seq_printf(m, " Enabled Subslice Per Slice: %u\n", ss_per);
|
||||
seq_printf(m, " Enabled EU Total: %u\n", eu_tot);
|
||||
seq_printf(m, " Enabled EU Per Subslice: %u\n", eu_per);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i915_forcewake_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct drm_device *dev = inode->i_private;
|
||||
@ -4487,6 +4689,8 @@ static const struct drm_info_list i915_debugfs_list[] = {
|
||||
{"i915_dp_mst_info", i915_dp_mst_info, 0},
|
||||
{"i915_wa_registers", i915_wa_registers, 0},
|
||||
{"i915_ddb_info", i915_ddb_info, 0},
|
||||
{"i915_sseu_status", i915_sseu_status, 0},
|
||||
{"i915_drrs_status", i915_drrs_status, 0},
|
||||
};
|
||||
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "intel_drv.h"
|
||||
#include <drm/i915_drm.h>
|
||||
#include "i915_drv.h"
|
||||
#include "i915_vgpu.h"
|
||||
#include "i915_trace.h"
|
||||
#include <linux/pci.h>
|
||||
#include <linux/console.h>
|
||||
@ -67,6 +68,9 @@ static int i915_getparam(struct drm_device *dev, void *data,
|
||||
case I915_PARAM_CHIPSET_ID:
|
||||
value = dev->pdev->device;
|
||||
break;
|
||||
case I915_PARAM_REVISION:
|
||||
value = dev->pdev->revision;
|
||||
break;
|
||||
case I915_PARAM_HAS_GEM:
|
||||
value = 1;
|
||||
break;
|
||||
@ -149,6 +153,16 @@ static int i915_getparam(struct drm_device *dev, void *data,
|
||||
case I915_PARAM_MMAP_VERSION:
|
||||
value = 1;
|
||||
break;
|
||||
case I915_PARAM_SUBSLICE_TOTAL:
|
||||
value = INTEL_INFO(dev)->subslice_total;
|
||||
if (!value)
|
||||
return -ENODEV;
|
||||
break;
|
||||
case I915_PARAM_EU_TOTAL:
|
||||
value = INTEL_INFO(dev)->eu_total;
|
||||
if (!value)
|
||||
return -ENODEV;
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG("Unknown parameter %d\n", param->param);
|
||||
return -EINVAL;
|
||||
@ -605,16 +619,128 @@ static void intel_device_info_runtime_init(struct drm_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize slice/subslice/EU info */
|
||||
if (IS_CHERRYVIEW(dev)) {
|
||||
u32 fuse, mask_eu;
|
||||
u32 fuse, eu_dis;
|
||||
|
||||
fuse = I915_READ(CHV_FUSE_GT);
|
||||
mask_eu = fuse & (CHV_FGT_EU_DIS_SS0_R0_MASK |
|
||||
CHV_FGT_EU_DIS_SS0_R1_MASK |
|
||||
CHV_FGT_EU_DIS_SS1_R0_MASK |
|
||||
CHV_FGT_EU_DIS_SS1_R1_MASK);
|
||||
info->eu_total = 16 - hweight32(mask_eu);
|
||||
|
||||
info->slice_total = 1;
|
||||
|
||||
if (!(fuse & CHV_FGT_DISABLE_SS0)) {
|
||||
info->subslice_per_slice++;
|
||||
eu_dis = fuse & (CHV_FGT_EU_DIS_SS0_R0_MASK |
|
||||
CHV_FGT_EU_DIS_SS0_R1_MASK);
|
||||
info->eu_total += 8 - hweight32(eu_dis);
|
||||
}
|
||||
|
||||
if (!(fuse & CHV_FGT_DISABLE_SS1)) {
|
||||
info->subslice_per_slice++;
|
||||
eu_dis = fuse & (CHV_FGT_EU_DIS_SS1_R0_MASK |
|
||||
CHV_FGT_EU_DIS_SS1_R1_MASK);
|
||||
info->eu_total += 8 - hweight32(eu_dis);
|
||||
}
|
||||
|
||||
info->subslice_total = info->subslice_per_slice;
|
||||
/*
|
||||
* CHV expected to always have a uniform distribution of EU
|
||||
* across subslices.
|
||||
*/
|
||||
info->eu_per_subslice = info->subslice_total ?
|
||||
info->eu_total / info->subslice_total :
|
||||
0;
|
||||
/*
|
||||
* CHV supports subslice power gating on devices with more than
|
||||
* one subslice, and supports EU power gating on devices with
|
||||
* more than one EU pair per subslice.
|
||||
*/
|
||||
info->has_slice_pg = 0;
|
||||
info->has_subslice_pg = (info->subslice_total > 1);
|
||||
info->has_eu_pg = (info->eu_per_subslice > 2);
|
||||
} else if (IS_SKYLAKE(dev)) {
|
||||
const int s_max = 3, ss_max = 4, eu_max = 8;
|
||||
int s, ss;
|
||||
u32 fuse2, eu_disable[s_max], s_enable, ss_disable;
|
||||
|
||||
fuse2 = I915_READ(GEN8_FUSE2);
|
||||
s_enable = (fuse2 & GEN8_F2_S_ENA_MASK) >>
|
||||
GEN8_F2_S_ENA_SHIFT;
|
||||
ss_disable = (fuse2 & GEN9_F2_SS_DIS_MASK) >>
|
||||
GEN9_F2_SS_DIS_SHIFT;
|
||||
|
||||
eu_disable[0] = I915_READ(GEN8_EU_DISABLE0);
|
||||
eu_disable[1] = I915_READ(GEN8_EU_DISABLE1);
|
||||
eu_disable[2] = I915_READ(GEN8_EU_DISABLE2);
|
||||
|
||||
info->slice_total = hweight32(s_enable);
|
||||
/*
|
||||
* The subslice disable field is global, i.e. it applies
|
||||
* to each of the enabled slices.
|
||||
*/
|
||||
info->subslice_per_slice = ss_max - hweight32(ss_disable);
|
||||
info->subslice_total = info->slice_total *
|
||||
info->subslice_per_slice;
|
||||
|
||||
/*
|
||||
* Iterate through enabled slices and subslices to
|
||||
* count the total enabled EU.
|
||||
*/
|
||||
for (s = 0; s < s_max; s++) {
|
||||
if (!(s_enable & (0x1 << s)))
|
||||
/* skip disabled slice */
|
||||
continue;
|
||||
|
||||
for (ss = 0; ss < ss_max; ss++) {
|
||||
u32 n_disabled;
|
||||
|
||||
if (ss_disable & (0x1 << ss))
|
||||
/* skip disabled subslice */
|
||||
continue;
|
||||
|
||||
n_disabled = hweight8(eu_disable[s] >>
|
||||
(ss * eu_max));
|
||||
|
||||
/*
|
||||
* Record which subslice(s) has(have) 7 EUs. we
|
||||
* can tune the hash used to spread work among
|
||||
* subslices if they are unbalanced.
|
||||
*/
|
||||
if (eu_max - n_disabled == 7)
|
||||
info->subslice_7eu[s] |= 1 << ss;
|
||||
|
||||
info->eu_total += eu_max - n_disabled;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SKL is expected to always have a uniform distribution
|
||||
* of EU across subslices with the exception that any one
|
||||
* EU in any one subslice may be fused off for die
|
||||
* recovery.
|
||||
*/
|
||||
info->eu_per_subslice = info->subslice_total ?
|
||||
DIV_ROUND_UP(info->eu_total,
|
||||
info->subslice_total) : 0;
|
||||
/*
|
||||
* SKL supports slice power gating on devices with more than
|
||||
* one slice, and supports EU power gating on devices with
|
||||
* more than one EU pair per subslice.
|
||||
*/
|
||||
info->has_slice_pg = (info->slice_total > 1) ? 1 : 0;
|
||||
info->has_subslice_pg = 0;
|
||||
info->has_eu_pg = (info->eu_per_subslice > 2) ? 1 : 0;
|
||||
}
|
||||
DRM_DEBUG_DRIVER("slice total: %u\n", info->slice_total);
|
||||
DRM_DEBUG_DRIVER("subslice total: %u\n", info->subslice_total);
|
||||
DRM_DEBUG_DRIVER("subslice per slice: %u\n", info->subslice_per_slice);
|
||||
DRM_DEBUG_DRIVER("EU total: %u\n", info->eu_total);
|
||||
DRM_DEBUG_DRIVER("EU per subslice: %u\n", info->eu_per_subslice);
|
||||
DRM_DEBUG_DRIVER("has slice power gating: %s\n",
|
||||
info->has_slice_pg ? "y" : "n");
|
||||
DRM_DEBUG_DRIVER("has subslice power gating: %s\n",
|
||||
info->has_subslice_pg ? "y" : "n");
|
||||
DRM_DEBUG_DRIVER("has EU power gating: %s\n",
|
||||
info->has_eu_pg ? "y" : "n");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -637,17 +763,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
|
||||
info = (struct intel_device_info *) flags;
|
||||
|
||||
/* Refuse to load on gen6+ without kms enabled. */
|
||||
if (info->gen >= 6 && !drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
DRM_INFO("Your hardware requires kernel modesetting (KMS)\n");
|
||||
DRM_INFO("See CONFIG_DRM_I915_KMS, nomodeset, and i915.modeset parameters\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* UMS needs agp support. */
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET) && !dev->agp)
|
||||
return -EINVAL;
|
||||
|
||||
dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
|
||||
if (dev_priv == NULL)
|
||||
return -ENOMEM;
|
||||
@ -717,20 +832,18 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
if (ret)
|
||||
goto out_regs;
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
/* WARNING: Apparently we must kick fbdev drivers before vgacon,
|
||||
* otherwise the vga fbdev driver falls over. */
|
||||
ret = i915_kick_out_firmware_fb(dev_priv);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to remove conflicting framebuffer drivers\n");
|
||||
goto out_gtt;
|
||||
}
|
||||
/* WARNING: Apparently we must kick fbdev drivers before vgacon,
|
||||
* otherwise the vga fbdev driver falls over. */
|
||||
ret = i915_kick_out_firmware_fb(dev_priv);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to remove conflicting framebuffer drivers\n");
|
||||
goto out_gtt;
|
||||
}
|
||||
|
||||
ret = i915_kick_out_vgacon(dev_priv);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to remove conflicting VGA console\n");
|
||||
goto out_gtt;
|
||||
}
|
||||
ret = i915_kick_out_vgacon(dev_priv);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to remove conflicting VGA console\n");
|
||||
goto out_gtt;
|
||||
}
|
||||
|
||||
pci_set_master(dev->pdev);
|
||||
@ -834,14 +947,19 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
|
||||
intel_power_domains_init(dev_priv);
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
ret = i915_load_modeset_init(dev);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to init modeset\n");
|
||||
goto out_power_well;
|
||||
}
|
||||
ret = i915_load_modeset_init(dev);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to init modeset\n");
|
||||
goto out_power_well;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify a valid surface after modesetting,
|
||||
* when running inside a VM.
|
||||
*/
|
||||
if (intel_vgpu_active(dev))
|
||||
I915_WRITE(vgtif_reg(display_ready), VGT_DRV_DISPLAY_READY);
|
||||
|
||||
i915_setup_sysfs(dev);
|
||||
|
||||
if (INTEL_INFO(dev)->num_pipes) {
|
||||
@ -921,28 +1039,25 @@ int i915_driver_unload(struct drm_device *dev)
|
||||
|
||||
acpi_video_unregister();
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
intel_fbdev_fini(dev);
|
||||
intel_fbdev_fini(dev);
|
||||
|
||||
drm_vblank_cleanup(dev);
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
intel_modeset_cleanup(dev);
|
||||
intel_modeset_cleanup(dev);
|
||||
|
||||
/*
|
||||
* free the memory space allocated for the child device
|
||||
* config parsed from VBT
|
||||
*/
|
||||
if (dev_priv->vbt.child_dev && dev_priv->vbt.child_dev_num) {
|
||||
kfree(dev_priv->vbt.child_dev);
|
||||
dev_priv->vbt.child_dev = NULL;
|
||||
dev_priv->vbt.child_dev_num = 0;
|
||||
}
|
||||
|
||||
vga_switcheroo_unregister_client(dev->pdev);
|
||||
vga_client_register(dev->pdev, NULL, NULL, NULL);
|
||||
/*
|
||||
* free the memory space allocated for the child device
|
||||
* config parsed from VBT
|
||||
*/
|
||||
if (dev_priv->vbt.child_dev && dev_priv->vbt.child_dev_num) {
|
||||
kfree(dev_priv->vbt.child_dev);
|
||||
dev_priv->vbt.child_dev = NULL;
|
||||
dev_priv->vbt.child_dev_num = 0;
|
||||
}
|
||||
|
||||
vga_switcheroo_unregister_client(dev->pdev);
|
||||
vga_client_register(dev->pdev, NULL, NULL, NULL);
|
||||
|
||||
/* Free error state after interrupts are fully disabled. */
|
||||
cancel_delayed_work_sync(&dev_priv->gpu_error.hangcheck_work);
|
||||
i915_destroy_error_state(dev);
|
||||
@ -952,17 +1067,15 @@ int i915_driver_unload(struct drm_device *dev)
|
||||
|
||||
intel_opregion_fini(dev);
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
/* Flush any outstanding unpin_work. */
|
||||
flush_workqueue(dev_priv->wq);
|
||||
/* Flush any outstanding unpin_work. */
|
||||
flush_workqueue(dev_priv->wq);
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
i915_gem_cleanup_ringbuffer(dev);
|
||||
i915_gem_batch_pool_fini(&dev_priv->mm.batch_pool);
|
||||
i915_gem_context_fini(dev);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
i915_gem_cleanup_stolen(dev);
|
||||
}
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
i915_gem_cleanup_ringbuffer(dev);
|
||||
i915_gem_batch_pool_fini(&dev_priv->mm.batch_pool);
|
||||
i915_gem_context_fini(dev);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
i915_gem_cleanup_stolen(dev);
|
||||
|
||||
intel_teardown_gmbus(dev);
|
||||
intel_teardown_mchbar(dev);
|
||||
@ -1023,8 +1136,7 @@ void i915_driver_preclose(struct drm_device *dev, struct drm_file *file)
|
||||
i915_gem_release(dev, file);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
intel_modeset_preclose(dev, file);
|
||||
intel_modeset_preclose(dev, file);
|
||||
}
|
||||
|
||||
void i915_driver_postclose(struct drm_device *dev, struct drm_file *file)
|
||||
@ -1087,7 +1199,7 @@ const struct drm_ioctl_desc i915_ioctls[] = {
|
||||
DRM_IOCTL_DEF_DRV(I915_OVERLAY_PUT_IMAGE, intel_overlay_put_image, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_SET_SPRITE_COLORKEY, intel_sprite_set_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GET_SPRITE_COLORKEY, intel_sprite_get_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GET_SPRITE_COLORKEY, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_WAIT, i915_gem_wait_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_CREATE, i915_gem_context_create_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
|
||||
|
@ -346,7 +346,6 @@ static const struct intel_device_info intel_broadwell_gt3m_info = {
|
||||
};
|
||||
|
||||
static const struct intel_device_info intel_cherryview_info = {
|
||||
.is_preliminary = 1,
|
||||
.gen = 8, .num_pipes = 3,
|
||||
.need_gfx_hws = 1, .has_hotplug = 1,
|
||||
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
|
||||
@ -369,6 +368,19 @@ static const struct intel_device_info intel_skylake_info = {
|
||||
IVB_CURSOR_OFFSETS,
|
||||
};
|
||||
|
||||
static const struct intel_device_info intel_skylake_gt3_info = {
|
||||
.is_preliminary = 1,
|
||||
.is_skylake = 1,
|
||||
.gen = 9, .num_pipes = 3,
|
||||
.need_gfx_hws = 1, .has_hotplug = 1,
|
||||
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
|
||||
.has_llc = 1,
|
||||
.has_ddi = 1,
|
||||
.has_fbc = 1,
|
||||
GEN_DEFAULT_PIPEOFFSETS,
|
||||
IVB_CURSOR_OFFSETS,
|
||||
};
|
||||
|
||||
/*
|
||||
* Make sure any device matches here are from most specific to most
|
||||
* general. For example, since the Quanta match is based on the subsystem
|
||||
@ -406,7 +418,9 @@ static const struct intel_device_info intel_skylake_info = {
|
||||
INTEL_BDW_GT3M_IDS(&intel_broadwell_gt3m_info), \
|
||||
INTEL_BDW_GT3D_IDS(&intel_broadwell_gt3d_info), \
|
||||
INTEL_CHV_IDS(&intel_cherryview_info), \
|
||||
INTEL_SKL_IDS(&intel_skylake_info)
|
||||
INTEL_SKL_GT1_IDS(&intel_skylake_info), \
|
||||
INTEL_SKL_GT2_IDS(&intel_skylake_info), \
|
||||
INTEL_SKL_GT3_IDS(&intel_skylake_gt3_info) \
|
||||
|
||||
static const struct pci_device_id pciidlist[] = { /* aka */
|
||||
INTEL_PCI_IDS,
|
||||
@ -553,6 +567,7 @@ static int i915_drm_suspend(struct drm_device *dev)
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_crtc *crtc;
|
||||
pci_power_t opregion_target_state;
|
||||
int error;
|
||||
|
||||
/* ignore lid events during suspend */
|
||||
mutex_lock(&dev_priv->modeset_restore_lock);
|
||||
@ -567,38 +582,33 @@ static int i915_drm_suspend(struct drm_device *dev)
|
||||
|
||||
pci_save_state(dev->pdev);
|
||||
|
||||
/* If KMS is active, we do the leavevt stuff here */
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
int error;
|
||||
|
||||
error = i915_gem_suspend(dev);
|
||||
if (error) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"GEM idle failed, resume might fail\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
intel_suspend_gt_powersave(dev);
|
||||
|
||||
/*
|
||||
* Disable CRTCs directly since we want to preserve sw state
|
||||
* for _thaw. Also, power gate the CRTC power wells.
|
||||
*/
|
||||
drm_modeset_lock_all(dev);
|
||||
for_each_crtc(dev, crtc)
|
||||
intel_crtc_control(crtc, false);
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
intel_dp_mst_suspend(dev);
|
||||
|
||||
intel_runtime_pm_disable_interrupts(dev_priv);
|
||||
intel_hpd_cancel_work(dev_priv);
|
||||
|
||||
intel_suspend_encoders(dev_priv);
|
||||
|
||||
intel_suspend_hw(dev);
|
||||
error = i915_gem_suspend(dev);
|
||||
if (error) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"GEM idle failed, resume might fail\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
intel_suspend_gt_powersave(dev);
|
||||
|
||||
/*
|
||||
* Disable CRTCs directly since we want to preserve sw state
|
||||
* for _thaw. Also, power gate the CRTC power wells.
|
||||
*/
|
||||
drm_modeset_lock_all(dev);
|
||||
for_each_crtc(dev, crtc)
|
||||
intel_crtc_control(crtc, false);
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
intel_dp_mst_suspend(dev);
|
||||
|
||||
intel_runtime_pm_disable_interrupts(dev_priv);
|
||||
intel_hpd_cancel_work(dev_priv);
|
||||
|
||||
intel_suspend_encoders(dev_priv);
|
||||
|
||||
intel_suspend_hw(dev);
|
||||
|
||||
i915_gem_suspend_gtt_mappings(dev);
|
||||
|
||||
i915_save_state(dev);
|
||||
@ -679,53 +689,48 @@ static int i915_drm_resume(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
i915_gem_restore_gtt_mappings(dev);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
}
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
i915_gem_restore_gtt_mappings(dev);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
i915_restore_state(dev);
|
||||
intel_opregion_setup(dev);
|
||||
|
||||
/* KMS EnterVT equivalent */
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
intel_init_pch_refclk(dev);
|
||||
drm_mode_config_reset(dev);
|
||||
intel_init_pch_refclk(dev);
|
||||
drm_mode_config_reset(dev);
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
if (i915_gem_init_hw(dev)) {
|
||||
DRM_ERROR("failed to re-initialize GPU, declaring wedged!\n");
|
||||
atomic_set_mask(I915_WEDGED, &dev_priv->gpu_error.reset_counter);
|
||||
}
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
/* We need working interrupts for modeset enabling ... */
|
||||
intel_runtime_pm_enable_interrupts(dev_priv);
|
||||
|
||||
intel_modeset_init_hw(dev);
|
||||
|
||||
spin_lock_irq(&dev_priv->irq_lock);
|
||||
if (dev_priv->display.hpd_irq_setup)
|
||||
dev_priv->display.hpd_irq_setup(dev);
|
||||
spin_unlock_irq(&dev_priv->irq_lock);
|
||||
|
||||
drm_modeset_lock_all(dev);
|
||||
intel_modeset_setup_hw_state(dev, true);
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
intel_dp_mst_resume(dev);
|
||||
|
||||
/*
|
||||
* ... but also need to make sure that hotplug processing
|
||||
* doesn't cause havoc. Like in the driver load code we don't
|
||||
* bother with the tiny race here where we might loose hotplug
|
||||
* notifications.
|
||||
* */
|
||||
intel_hpd_init(dev_priv);
|
||||
/* Config may have changed between suspend and resume */
|
||||
drm_helper_hpd_irq_event(dev);
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
if (i915_gem_init_hw(dev)) {
|
||||
DRM_ERROR("failed to re-initialize GPU, declaring wedged!\n");
|
||||
atomic_set_mask(I915_WEDGED, &dev_priv->gpu_error.reset_counter);
|
||||
}
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
/* We need working interrupts for modeset enabling ... */
|
||||
intel_runtime_pm_enable_interrupts(dev_priv);
|
||||
|
||||
intel_modeset_init_hw(dev);
|
||||
|
||||
spin_lock_irq(&dev_priv->irq_lock);
|
||||
if (dev_priv->display.hpd_irq_setup)
|
||||
dev_priv->display.hpd_irq_setup(dev);
|
||||
spin_unlock_irq(&dev_priv->irq_lock);
|
||||
|
||||
drm_modeset_lock_all(dev);
|
||||
intel_modeset_setup_hw_state(dev, true);
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
intel_dp_mst_resume(dev);
|
||||
|
||||
/*
|
||||
* ... but also need to make sure that hotplug processing
|
||||
* doesn't cause havoc. Like in the driver load code we don't
|
||||
* bother with the tiny race here where we might loose hotplug
|
||||
* notifications.
|
||||
* */
|
||||
intel_hpd_init(dev_priv);
|
||||
/* Config may have changed between suspend and resume */
|
||||
drm_helper_hpd_irq_event(dev);
|
||||
|
||||
intel_opregion_init(dev);
|
||||
|
||||
@ -861,38 +866,29 @@ int i915_reset(struct drm_device *dev)
|
||||
* was running at the time of the reset (i.e. we weren't VT
|
||||
* switched away).
|
||||
*/
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
/* Used to prevent gem_check_wedged returning -EAGAIN during gpu reset */
|
||||
dev_priv->gpu_error.reload_in_reset = true;
|
||||
|
||||
ret = i915_gem_init_hw(dev);
|
||||
/* Used to prevent gem_check_wedged returning -EAGAIN during gpu reset */
|
||||
dev_priv->gpu_error.reload_in_reset = true;
|
||||
|
||||
dev_priv->gpu_error.reload_in_reset = false;
|
||||
ret = i915_gem_init_hw(dev);
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed hw init on reset %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev_priv->gpu_error.reload_in_reset = false;
|
||||
|
||||
/*
|
||||
* FIXME: This races pretty badly against concurrent holders of
|
||||
* ring interrupts. This is possible since we've started to drop
|
||||
* dev->struct_mutex in select places when waiting for the gpu.
|
||||
*/
|
||||
|
||||
/*
|
||||
* rps/rc6 re-init is necessary to restore state lost after the
|
||||
* reset and the re-install of gt irqs. Skip for ironlake per
|
||||
* previous concerns that it doesn't respond well to some forms
|
||||
* of re-init after reset.
|
||||
*/
|
||||
if (INTEL_INFO(dev)->gen > 5)
|
||||
intel_enable_gt_powersave(dev);
|
||||
} else {
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed hw init on reset %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* rps/rc6 re-init is necessary to restore state lost after the
|
||||
* reset and the re-install of gt irqs. Skip for ironlake per
|
||||
* previous concerns that it doesn't respond well to some forms
|
||||
* of re-init after reset.
|
||||
*/
|
||||
if (INTEL_INFO(dev)->gen > 5)
|
||||
intel_enable_gt_powersave(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1640,11 +1636,9 @@ static int __init i915_init(void)
|
||||
|
||||
if (!(driver.driver_features & DRIVER_MODESET)) {
|
||||
driver.get_vblank_timestamp = NULL;
|
||||
#ifndef CONFIG_DRM_I915_UMS
|
||||
/* Silently fail loading to not upset userspace. */
|
||||
DRM_DEBUG_DRIVER("KMS and UMS disabled.\n");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1660,10 +1654,8 @@ static int __init i915_init(void)
|
||||
|
||||
static void __exit i915_exit(void)
|
||||
{
|
||||
#ifndef CONFIG_DRM_I915_UMS
|
||||
if (!(driver.driver_features & DRIVER_MODESET))
|
||||
return; /* Never loaded a driver. */
|
||||
#endif
|
||||
|
||||
drm_pci_exit(&driver, &i915_pci_driver);
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
#define _I915_DRV_H_
|
||||
|
||||
#include <uapi/drm/i915_drm.h>
|
||||
#include <uapi/drm/drm_fourcc.h>
|
||||
|
||||
#include "i915_reg.h"
|
||||
#include "intel_bios.h"
|
||||
@ -55,7 +56,7 @@
|
||||
|
||||
#define DRIVER_NAME "i915"
|
||||
#define DRIVER_DESC "Intel Graphics"
|
||||
#define DRIVER_DATE "20150130"
|
||||
#define DRIVER_DATE "20150327"
|
||||
|
||||
#undef WARN_ON
|
||||
/* Many gcc seem to no see through this and fall over :( */
|
||||
@ -69,6 +70,9 @@
|
||||
#define WARN_ON(x) WARN((x), "WARN_ON(" #x ")")
|
||||
#endif
|
||||
|
||||
#undef WARN_ON_ONCE
|
||||
#define WARN_ON_ONCE(x) WARN_ONCE((x), "WARN_ON_ONCE(" #x ")")
|
||||
|
||||
#define MISSING_CASE(x) WARN(1, "Missing switch case (%lu) in %s\n", \
|
||||
(long) (x), __func__);
|
||||
|
||||
@ -222,9 +226,14 @@ enum hpd_pin {
|
||||
|
||||
#define for_each_pipe(__dev_priv, __p) \
|
||||
for ((__p) = 0; (__p) < INTEL_INFO(__dev_priv)->num_pipes; (__p)++)
|
||||
#define for_each_plane(pipe, p) \
|
||||
for ((p) = 0; (p) < INTEL_INFO(dev)->num_sprites[(pipe)] + 1; (p)++)
|
||||
#define for_each_sprite(p, s) for ((s) = 0; (s) < INTEL_INFO(dev)->num_sprites[(p)]; (s)++)
|
||||
#define for_each_plane(__dev_priv, __pipe, __p) \
|
||||
for ((__p) = 0; \
|
||||
(__p) < INTEL_INFO(__dev_priv)->num_sprites[(__pipe)] + 1; \
|
||||
(__p)++)
|
||||
#define for_each_sprite(__dev_priv, __p, __s) \
|
||||
for ((__s) = 0; \
|
||||
(__s) < INTEL_INFO(__dev_priv)->num_sprites[(__p)]; \
|
||||
(__s)++)
|
||||
|
||||
#define for_each_crtc(dev, crtc) \
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
|
||||
@ -237,6 +246,12 @@ enum hpd_pin {
|
||||
&(dev)->mode_config.encoder_list, \
|
||||
base.head)
|
||||
|
||||
#define for_each_intel_connector(dev, intel_connector) \
|
||||
list_for_each_entry(intel_connector, \
|
||||
&dev->mode_config.connector_list, \
|
||||
base.head)
|
||||
|
||||
|
||||
#define for_each_encoder_on_crtc(dev, __crtc, intel_encoder) \
|
||||
list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \
|
||||
if ((intel_encoder)->base.crtc == (__crtc))
|
||||
@ -412,6 +427,8 @@ struct drm_i915_error_state {
|
||||
u32 forcewake;
|
||||
u32 error; /* gen6+ */
|
||||
u32 err_int; /* gen7 */
|
||||
u32 fault_data0; /* gen8, gen9 */
|
||||
u32 fault_data1; /* gen8, gen9 */
|
||||
u32 done_reg;
|
||||
u32 gac_eco;
|
||||
u32 gam_ecochk;
|
||||
@ -529,7 +546,7 @@ struct drm_i915_display_funcs {
|
||||
* Returns true on success, false on failure.
|
||||
*/
|
||||
bool (*find_dpll)(const struct intel_limit *limit,
|
||||
struct intel_crtc *crtc,
|
||||
struct intel_crtc_state *crtc_state,
|
||||
int target, int refclk,
|
||||
struct dpll *match_clock,
|
||||
struct dpll *best_clock);
|
||||
@ -538,7 +555,7 @@ struct drm_i915_display_funcs {
|
||||
struct drm_crtc *crtc,
|
||||
uint32_t sprite_width, uint32_t sprite_height,
|
||||
int pixel_size, bool enable, bool scaled);
|
||||
void (*modeset_global_resources)(struct drm_device *dev);
|
||||
void (*modeset_global_resources)(struct drm_atomic_state *state);
|
||||
/* Returns the active state of the crtc, and if the crtc is active,
|
||||
* fills out the pipe-config with the hw state. */
|
||||
bool (*get_pipe_config)(struct intel_crtc *,
|
||||
@ -692,7 +709,18 @@ struct intel_device_info {
|
||||
int trans_offsets[I915_MAX_TRANSCODERS];
|
||||
int palette_offsets[I915_MAX_PIPES];
|
||||
int cursor_offsets[I915_MAX_PIPES];
|
||||
unsigned int eu_total;
|
||||
|
||||
/* Slice/subslice/EU info */
|
||||
u8 slice_total;
|
||||
u8 subslice_total;
|
||||
u8 subslice_per_slice;
|
||||
u8 eu_total;
|
||||
u8 eu_per_subslice;
|
||||
/* For each slice, which subslice(s) has(have) 7 EUs (bitfield)? */
|
||||
u8 subslice_7eu[3];
|
||||
u8 has_slice_pg:1;
|
||||
u8 has_subslice_pg:1;
|
||||
u8 has_eu_pg:1;
|
||||
};
|
||||
|
||||
#undef DEFINE_FLAG
|
||||
@ -771,11 +799,20 @@ struct intel_context {
|
||||
struct list_head link;
|
||||
};
|
||||
|
||||
enum fb_op_origin {
|
||||
ORIGIN_GTT,
|
||||
ORIGIN_CPU,
|
||||
ORIGIN_CS,
|
||||
ORIGIN_FLIP,
|
||||
};
|
||||
|
||||
struct i915_fbc {
|
||||
unsigned long size;
|
||||
unsigned long uncompressed_size;
|
||||
unsigned threshold;
|
||||
unsigned int fb_id;
|
||||
enum plane plane;
|
||||
unsigned int possible_framebuffer_bits;
|
||||
unsigned int busy_bits;
|
||||
struct intel_crtc *crtc;
|
||||
int y;
|
||||
|
||||
struct drm_mm_node compressed_fb;
|
||||
@ -787,14 +824,6 @@ struct i915_fbc {
|
||||
* possible. */
|
||||
bool enabled;
|
||||
|
||||
/* On gen8 some rings cannont perform fbc clean operation so for now
|
||||
* we are doing this on SW with mmio.
|
||||
* This variable works in the opposite information direction
|
||||
* of ring->fbc_dirty telling software on frontbuffer tracking
|
||||
* to perform the cache clean on sw side.
|
||||
*/
|
||||
bool need_sw_cache_clean;
|
||||
|
||||
struct intel_fbc_work {
|
||||
struct delayed_work work;
|
||||
struct drm_crtc *crtc;
|
||||
@ -888,150 +917,21 @@ struct intel_gmbus {
|
||||
};
|
||||
|
||||
struct i915_suspend_saved_registers {
|
||||
u8 saveLBB;
|
||||
u32 saveDSPACNTR;
|
||||
u32 saveDSPBCNTR;
|
||||
u32 saveDSPARB;
|
||||
u32 savePIPEACONF;
|
||||
u32 savePIPEBCONF;
|
||||
u32 savePIPEASRC;
|
||||
u32 savePIPEBSRC;
|
||||
u32 saveFPA0;
|
||||
u32 saveFPA1;
|
||||
u32 saveDPLL_A;
|
||||
u32 saveDPLL_A_MD;
|
||||
u32 saveHTOTAL_A;
|
||||
u32 saveHBLANK_A;
|
||||
u32 saveHSYNC_A;
|
||||
u32 saveVTOTAL_A;
|
||||
u32 saveVBLANK_A;
|
||||
u32 saveVSYNC_A;
|
||||
u32 saveBCLRPAT_A;
|
||||
u32 saveTRANSACONF;
|
||||
u32 saveTRANS_HTOTAL_A;
|
||||
u32 saveTRANS_HBLANK_A;
|
||||
u32 saveTRANS_HSYNC_A;
|
||||
u32 saveTRANS_VTOTAL_A;
|
||||
u32 saveTRANS_VBLANK_A;
|
||||
u32 saveTRANS_VSYNC_A;
|
||||
u32 savePIPEASTAT;
|
||||
u32 saveDSPASTRIDE;
|
||||
u32 saveDSPASIZE;
|
||||
u32 saveDSPAPOS;
|
||||
u32 saveDSPAADDR;
|
||||
u32 saveDSPASURF;
|
||||
u32 saveDSPATILEOFF;
|
||||
u32 savePFIT_PGM_RATIOS;
|
||||
u32 saveBLC_HIST_CTL;
|
||||
u32 saveBLC_PWM_CTL;
|
||||
u32 saveBLC_PWM_CTL2;
|
||||
u32 saveBLC_CPU_PWM_CTL;
|
||||
u32 saveBLC_CPU_PWM_CTL2;
|
||||
u32 saveFPB0;
|
||||
u32 saveFPB1;
|
||||
u32 saveDPLL_B;
|
||||
u32 saveDPLL_B_MD;
|
||||
u32 saveHTOTAL_B;
|
||||
u32 saveHBLANK_B;
|
||||
u32 saveHSYNC_B;
|
||||
u32 saveVTOTAL_B;
|
||||
u32 saveVBLANK_B;
|
||||
u32 saveVSYNC_B;
|
||||
u32 saveBCLRPAT_B;
|
||||
u32 saveTRANSBCONF;
|
||||
u32 saveTRANS_HTOTAL_B;
|
||||
u32 saveTRANS_HBLANK_B;
|
||||
u32 saveTRANS_HSYNC_B;
|
||||
u32 saveTRANS_VTOTAL_B;
|
||||
u32 saveTRANS_VBLANK_B;
|
||||
u32 saveTRANS_VSYNC_B;
|
||||
u32 savePIPEBSTAT;
|
||||
u32 saveDSPBSTRIDE;
|
||||
u32 saveDSPBSIZE;
|
||||
u32 saveDSPBPOS;
|
||||
u32 saveDSPBADDR;
|
||||
u32 saveDSPBSURF;
|
||||
u32 saveDSPBTILEOFF;
|
||||
u32 saveVGA0;
|
||||
u32 saveVGA1;
|
||||
u32 saveVGA_PD;
|
||||
u32 saveVGACNTRL;
|
||||
u32 saveADPA;
|
||||
u32 saveLVDS;
|
||||
u32 savePP_ON_DELAYS;
|
||||
u32 savePP_OFF_DELAYS;
|
||||
u32 saveDVOA;
|
||||
u32 saveDVOB;
|
||||
u32 saveDVOC;
|
||||
u32 savePP_ON;
|
||||
u32 savePP_OFF;
|
||||
u32 savePP_CONTROL;
|
||||
u32 savePP_DIVISOR;
|
||||
u32 savePFIT_CONTROL;
|
||||
u32 save_palette_a[256];
|
||||
u32 save_palette_b[256];
|
||||
u32 saveFBC_CONTROL;
|
||||
u32 saveIER;
|
||||
u32 saveIIR;
|
||||
u32 saveIMR;
|
||||
u32 saveDEIER;
|
||||
u32 saveDEIMR;
|
||||
u32 saveGTIER;
|
||||
u32 saveGTIMR;
|
||||
u32 saveFDI_RXA_IMR;
|
||||
u32 saveFDI_RXB_IMR;
|
||||
u32 saveCACHE_MODE_0;
|
||||
u32 saveMI_ARB_STATE;
|
||||
u32 saveSWF0[16];
|
||||
u32 saveSWF1[16];
|
||||
u32 saveSWF2[3];
|
||||
u8 saveMSR;
|
||||
u8 saveSR[8];
|
||||
u8 saveGR[25];
|
||||
u8 saveAR_INDEX;
|
||||
u8 saveAR[21];
|
||||
u8 saveDACMASK;
|
||||
u8 saveCR[37];
|
||||
uint64_t saveFENCE[I915_MAX_NUM_FENCES];
|
||||
u32 saveCURACNTR;
|
||||
u32 saveCURAPOS;
|
||||
u32 saveCURABASE;
|
||||
u32 saveCURBCNTR;
|
||||
u32 saveCURBPOS;
|
||||
u32 saveCURBBASE;
|
||||
u32 saveCURSIZE;
|
||||
u32 saveDP_B;
|
||||
u32 saveDP_C;
|
||||
u32 saveDP_D;
|
||||
u32 savePIPEA_GMCH_DATA_M;
|
||||
u32 savePIPEB_GMCH_DATA_M;
|
||||
u32 savePIPEA_GMCH_DATA_N;
|
||||
u32 savePIPEB_GMCH_DATA_N;
|
||||
u32 savePIPEA_DP_LINK_M;
|
||||
u32 savePIPEB_DP_LINK_M;
|
||||
u32 savePIPEA_DP_LINK_N;
|
||||
u32 savePIPEB_DP_LINK_N;
|
||||
u32 saveFDI_RXA_CTL;
|
||||
u32 saveFDI_TXA_CTL;
|
||||
u32 saveFDI_RXB_CTL;
|
||||
u32 saveFDI_TXB_CTL;
|
||||
u32 savePFA_CTL_1;
|
||||
u32 savePFB_CTL_1;
|
||||
u32 savePFA_WIN_SZ;
|
||||
u32 savePFB_WIN_SZ;
|
||||
u32 savePFA_WIN_POS;
|
||||
u32 savePFB_WIN_POS;
|
||||
u32 savePCH_DREF_CONTROL;
|
||||
u32 saveDISP_ARB_CTL;
|
||||
u32 savePIPEA_DATA_M1;
|
||||
u32 savePIPEA_DATA_N1;
|
||||
u32 savePIPEA_LINK_M1;
|
||||
u32 savePIPEA_LINK_N1;
|
||||
u32 savePIPEB_DATA_M1;
|
||||
u32 savePIPEB_DATA_N1;
|
||||
u32 savePIPEB_LINK_M1;
|
||||
u32 savePIPEB_LINK_N1;
|
||||
u32 saveMCHBAR_RENDER_STANDBY;
|
||||
u32 savePCH_PORT_HOTPLUG;
|
||||
u16 saveGCDGMBUS;
|
||||
};
|
||||
@ -1128,13 +1028,12 @@ struct intel_gen6_power_mgmt {
|
||||
u8 max_freq_softlimit; /* Max frequency permitted by the driver */
|
||||
u8 max_freq; /* Maximum frequency, RP0 if not overclocking */
|
||||
u8 min_freq; /* AKA RPn. Minimum frequency */
|
||||
u8 idle_freq; /* Frequency to request when we are idle */
|
||||
u8 efficient_freq; /* AKA RPe. Pre-determined balanced frequency */
|
||||
u8 rp1_freq; /* "less than" RP0 power/freqency */
|
||||
u8 rp0_freq; /* Non-overclocked max frequency. */
|
||||
u32 cz_freq;
|
||||
|
||||
u32 ei_interrupt_count;
|
||||
|
||||
int last_adj;
|
||||
enum { LOW_POWER, BETWEEN, HIGH_POWER } power;
|
||||
|
||||
@ -1171,9 +1070,6 @@ struct intel_ilk_power_mgmt {
|
||||
|
||||
int c_m;
|
||||
int r_t;
|
||||
|
||||
struct drm_i915_gem_object *pwrctx;
|
||||
struct drm_i915_gem_object *renderctx;
|
||||
};
|
||||
|
||||
struct drm_i915_private;
|
||||
@ -1455,6 +1351,7 @@ struct intel_vbt_data {
|
||||
bool edp_initialized;
|
||||
bool edp_support;
|
||||
int edp_bpp;
|
||||
bool edp_low_vswing;
|
||||
struct edp_power_seq edp_pps;
|
||||
|
||||
struct {
|
||||
@ -1515,6 +1412,25 @@ struct ilk_wm_values {
|
||||
enum intel_ddb_partitioning partitioning;
|
||||
};
|
||||
|
||||
struct vlv_wm_values {
|
||||
struct {
|
||||
uint16_t primary;
|
||||
uint16_t sprite[2];
|
||||
uint8_t cursor;
|
||||
} pipe[3];
|
||||
|
||||
struct {
|
||||
uint16_t plane;
|
||||
uint8_t cursor;
|
||||
} sr;
|
||||
|
||||
struct {
|
||||
uint8_t cursor;
|
||||
uint8_t sprite[2];
|
||||
uint8_t primary;
|
||||
} ddl[3];
|
||||
};
|
||||
|
||||
struct skl_ddb_entry {
|
||||
uint16_t start, end; /* in number of blocks, 'end' is exclusive */
|
||||
};
|
||||
@ -1641,6 +1557,10 @@ struct i915_workarounds {
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct i915_virtual_gpu {
|
||||
bool active;
|
||||
};
|
||||
|
||||
struct drm_i915_private {
|
||||
struct drm_device *dev;
|
||||
struct kmem_cache *slab;
|
||||
@ -1653,6 +1573,8 @@ struct drm_i915_private {
|
||||
|
||||
struct intel_uncore uncore;
|
||||
|
||||
struct i915_virtual_gpu vgpu;
|
||||
|
||||
struct intel_gmbus gmbus[GMBUS_NUM_PORTS];
|
||||
|
||||
|
||||
@ -1871,6 +1793,7 @@ struct drm_i915_private {
|
||||
union {
|
||||
struct ilk_wm_values hw;
|
||||
struct skl_wm_values skl_hw;
|
||||
struct vlv_wm_values vlv;
|
||||
};
|
||||
} wm;
|
||||
|
||||
@ -2142,7 +2065,7 @@ struct drm_i915_gem_request {
|
||||
u32 tail;
|
||||
|
||||
/**
|
||||
* Context related to this request
|
||||
* Context and ring buffer related to this request
|
||||
* Contexts are refcounted, so when this request is associated with a
|
||||
* context, we must increment the context's refcount, to guarantee that
|
||||
* it persists while any request is linked to it. Requests themselves
|
||||
@ -2152,6 +2075,7 @@ struct drm_i915_gem_request {
|
||||
* context.
|
||||
*/
|
||||
struct intel_context *ctx;
|
||||
struct intel_ringbuffer *ringbuf;
|
||||
|
||||
/** Batch buffer related to this request if any */
|
||||
struct drm_i915_gem_object *batch_obj;
|
||||
@ -2166,6 +2090,9 @@ struct drm_i915_gem_request {
|
||||
/** file_priv list entry for this request */
|
||||
struct list_head client_list;
|
||||
|
||||
/** process identifier submitting this request */
|
||||
struct pid *pid;
|
||||
|
||||
uint32_t uniq;
|
||||
|
||||
/**
|
||||
@ -2352,6 +2279,7 @@ struct drm_i915_cmd_table {
|
||||
})
|
||||
#define INTEL_INFO(p) (&__I915__(p)->info)
|
||||
#define INTEL_DEVID(p) (INTEL_INFO(p)->device_id)
|
||||
#define INTEL_REVID(p) (__I915__(p)->dev->pdev->revision)
|
||||
|
||||
#define IS_I830(dev) (INTEL_DEVID(dev) == 0x3577)
|
||||
#define IS_845G(dev) (INTEL_DEVID(dev) == 0x2562)
|
||||
@ -2374,9 +2302,6 @@ struct drm_i915_cmd_table {
|
||||
#define IS_IVB_GT1(dev) (INTEL_DEVID(dev) == 0x0156 || \
|
||||
INTEL_DEVID(dev) == 0x0152 || \
|
||||
INTEL_DEVID(dev) == 0x015a)
|
||||
#define IS_SNB_GT1(dev) (INTEL_DEVID(dev) == 0x0102 || \
|
||||
INTEL_DEVID(dev) == 0x0106 || \
|
||||
INTEL_DEVID(dev) == 0x010A)
|
||||
#define IS_VALLEYVIEW(dev) (INTEL_INFO(dev)->is_valleyview)
|
||||
#define IS_CHERRYVIEW(dev) (INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev))
|
||||
#define IS_HASWELL(dev) (INTEL_INFO(dev)->is_haswell)
|
||||
@ -2400,6 +2325,12 @@ struct drm_i915_cmd_table {
|
||||
INTEL_DEVID(dev) == 0x0A1E)
|
||||
#define IS_PRELIMINARY_HW(intel_info) ((intel_info)->is_preliminary)
|
||||
|
||||
#define SKL_REVID_A0 (0x0)
|
||||
#define SKL_REVID_B0 (0x1)
|
||||
#define SKL_REVID_C0 (0x2)
|
||||
#define SKL_REVID_D0 (0x3)
|
||||
#define SKL_REVID_E0 (0x4)
|
||||
|
||||
/*
|
||||
* The genX designation typically refers to the render engine, so render
|
||||
* capability related checks should use IS_GEN, while display and other checks
|
||||
@ -2499,6 +2430,7 @@ struct drm_i915_cmd_table {
|
||||
#define NUM_L3_SLICES(dev) (IS_HSW_GT3(dev) ? 2 : HAS_L3_DPF(dev))
|
||||
|
||||
#define GT_FREQUENCY_MULTIPLIER 50
|
||||
#define GEN9_FREQ_SCALER 3
|
||||
|
||||
#include "i915_trace.h"
|
||||
|
||||
@ -2507,14 +2439,11 @@ extern int i915_max_ioctl;
|
||||
|
||||
extern int i915_suspend_legacy(struct drm_device *dev, pm_message_t state);
|
||||
extern int i915_resume_legacy(struct drm_device *dev);
|
||||
extern int i915_master_create(struct drm_device *dev, struct drm_master *master);
|
||||
extern void i915_master_destroy(struct drm_device *dev, struct drm_master *master);
|
||||
|
||||
/* i915_params.c */
|
||||
struct i915_params {
|
||||
int modeset;
|
||||
int panel_ignore_lid;
|
||||
unsigned int powersave;
|
||||
int semaphores;
|
||||
unsigned int lvds_downclock;
|
||||
int lvds_channel_mode;
|
||||
@ -2534,11 +2463,12 @@ struct i915_params {
|
||||
bool enable_hangcheck;
|
||||
bool fastboot;
|
||||
bool prefault_disable;
|
||||
bool load_detect_test;
|
||||
bool reset;
|
||||
bool disable_display;
|
||||
bool disable_vtd_wa;
|
||||
int use_mmio_flip;
|
||||
bool mmio_debug;
|
||||
int mmio_debug;
|
||||
bool verbose_state_checks;
|
||||
bool nuclear_pageflip;
|
||||
};
|
||||
@ -2591,6 +2521,10 @@ void intel_uncore_forcewake_get(struct drm_i915_private *dev_priv,
|
||||
void intel_uncore_forcewake_put(struct drm_i915_private *dev_priv,
|
||||
enum forcewake_domains domains);
|
||||
void assert_forcewakes_inactive(struct drm_i915_private *dev_priv);
|
||||
static inline bool intel_vgpu_active(struct drm_device *dev)
|
||||
{
|
||||
return to_i915(dev)->vgpu.active;
|
||||
}
|
||||
|
||||
void
|
||||
i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
|
||||
@ -2669,12 +2603,6 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
|
||||
int i915_gem_wait_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
void i915_gem_load(struct drm_device *dev);
|
||||
unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv,
|
||||
long target,
|
||||
unsigned flags);
|
||||
#define I915_SHRINK_PURGEABLE 0x1
|
||||
#define I915_SHRINK_UNBOUND 0x2
|
||||
#define I915_SHRINK_BOUND 0x4
|
||||
void *i915_gem_object_alloc(struct drm_device *dev);
|
||||
void i915_gem_object_free(struct drm_i915_gem_object *obj);
|
||||
void i915_gem_object_init(struct drm_i915_gem_object *obj,
|
||||
@ -2691,20 +2619,16 @@ void i915_gem_vma_destroy(struct i915_vma *vma);
|
||||
#define PIN_GLOBAL 0x4
|
||||
#define PIN_OFFSET_BIAS 0x8
|
||||
#define PIN_OFFSET_MASK (~4095)
|
||||
int __must_check i915_gem_object_pin_view(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm,
|
||||
uint32_t alignment,
|
||||
uint64_t flags,
|
||||
const struct i915_ggtt_view *view);
|
||||
static inline
|
||||
int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm,
|
||||
uint32_t alignment,
|
||||
uint64_t flags)
|
||||
{
|
||||
return i915_gem_object_pin_view(obj, vm, alignment, flags,
|
||||
&i915_ggtt_view_normal);
|
||||
}
|
||||
int __must_check
|
||||
i915_gem_object_pin(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm,
|
||||
uint32_t alignment,
|
||||
uint64_t flags);
|
||||
int __must_check
|
||||
i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
|
||||
const struct i915_ggtt_view *view,
|
||||
uint32_t alignment,
|
||||
uint64_t flags);
|
||||
|
||||
int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
|
||||
u32 flags);
|
||||
@ -2844,8 +2768,10 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write);
|
||||
int __must_check
|
||||
i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
|
||||
u32 alignment,
|
||||
struct intel_engine_cs *pipelined);
|
||||
void i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj);
|
||||
struct intel_engine_cs *pipelined,
|
||||
const struct i915_ggtt_view *view);
|
||||
void i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj,
|
||||
const struct i915_ggtt_view *view);
|
||||
int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj,
|
||||
int align);
|
||||
int i915_gem_open(struct drm_device *dev, struct drm_file *file);
|
||||
@ -2868,60 +2794,46 @@ struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
|
||||
|
||||
void i915_gem_restore_fences(struct drm_device *dev);
|
||||
|
||||
unsigned long i915_gem_obj_offset_view(struct drm_i915_gem_object *o,
|
||||
struct i915_address_space *vm,
|
||||
enum i915_ggtt_view_type view);
|
||||
static inline
|
||||
unsigned long i915_gem_obj_offset(struct drm_i915_gem_object *o,
|
||||
struct i915_address_space *vm)
|
||||
unsigned long
|
||||
i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o,
|
||||
const struct i915_ggtt_view *view);
|
||||
unsigned long
|
||||
i915_gem_obj_offset(struct drm_i915_gem_object *o,
|
||||
struct i915_address_space *vm);
|
||||
static inline unsigned long
|
||||
i915_gem_obj_ggtt_offset(struct drm_i915_gem_object *o)
|
||||
{
|
||||
return i915_gem_obj_offset_view(o, vm, I915_GGTT_VIEW_NORMAL);
|
||||
return i915_gem_obj_ggtt_offset_view(o, &i915_ggtt_view_normal);
|
||||
}
|
||||
|
||||
bool i915_gem_obj_bound_any(struct drm_i915_gem_object *o);
|
||||
bool i915_gem_obj_bound_view(struct drm_i915_gem_object *o,
|
||||
struct i915_address_space *vm,
|
||||
enum i915_ggtt_view_type view);
|
||||
static inline
|
||||
bool i915_gem_obj_ggtt_bound_view(struct drm_i915_gem_object *o,
|
||||
const struct i915_ggtt_view *view);
|
||||
bool i915_gem_obj_bound(struct drm_i915_gem_object *o,
|
||||
struct i915_address_space *vm)
|
||||
{
|
||||
return i915_gem_obj_bound_view(o, vm, I915_GGTT_VIEW_NORMAL);
|
||||
}
|
||||
struct i915_address_space *vm);
|
||||
|
||||
unsigned long i915_gem_obj_size(struct drm_i915_gem_object *o,
|
||||
struct i915_address_space *vm);
|
||||
struct i915_vma *i915_gem_obj_to_vma_view(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm,
|
||||
const struct i915_ggtt_view *view);
|
||||
static inline
|
||||
struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm)
|
||||
{
|
||||
return i915_gem_obj_to_vma_view(obj, vm, &i915_ggtt_view_normal);
|
||||
}
|
||||
|
||||
struct i915_vma *
|
||||
i915_gem_obj_lookup_or_create_vma_view(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm,
|
||||
const struct i915_ggtt_view *view);
|
||||
i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm);
|
||||
struct i915_vma *
|
||||
i915_gem_obj_to_ggtt_view(struct drm_i915_gem_object *obj,
|
||||
const struct i915_ggtt_view *view);
|
||||
|
||||
static inline
|
||||
struct i915_vma *
|
||||
i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm)
|
||||
{
|
||||
return i915_gem_obj_lookup_or_create_vma_view(obj, vm,
|
||||
&i915_ggtt_view_normal);
|
||||
}
|
||||
struct i915_address_space *vm);
|
||||
struct i915_vma *
|
||||
i915_gem_obj_lookup_or_create_ggtt_vma(struct drm_i915_gem_object *obj,
|
||||
const struct i915_ggtt_view *view);
|
||||
|
||||
struct i915_vma *i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj);
|
||||
static inline bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj) {
|
||||
struct i915_vma *vma;
|
||||
list_for_each_entry(vma, &obj->vma_list, vma_link)
|
||||
if (vma->pin_count > 0)
|
||||
return true;
|
||||
return false;
|
||||
static inline struct i915_vma *
|
||||
i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
return i915_gem_obj_to_ggtt_view(obj, &i915_ggtt_view_normal);
|
||||
}
|
||||
bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj);
|
||||
|
||||
/* Some GGTT VM helpers */
|
||||
#define i915_obj_to_ggtt(obj) \
|
||||
@ -2944,13 +2856,7 @@ i915_vm_to_ppgtt(struct i915_address_space *vm)
|
||||
|
||||
static inline bool i915_gem_obj_ggtt_bound(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
return i915_gem_obj_bound(obj, i915_obj_to_ggtt(obj));
|
||||
}
|
||||
|
||||
static inline unsigned long
|
||||
i915_gem_obj_ggtt_offset(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
return i915_gem_obj_offset(obj, i915_obj_to_ggtt(obj));
|
||||
return i915_gem_obj_ggtt_bound_view(obj, &i915_ggtt_view_normal);
|
||||
}
|
||||
|
||||
static inline unsigned long
|
||||
@ -2974,7 +2880,13 @@ i915_gem_object_ggtt_unbind(struct drm_i915_gem_object *obj)
|
||||
return i915_vma_unbind(i915_gem_obj_to_ggtt(obj));
|
||||
}
|
||||
|
||||
void i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj);
|
||||
void i915_gem_object_ggtt_unpin_view(struct drm_i915_gem_object *obj,
|
||||
const struct i915_ggtt_view *view);
|
||||
static inline void
|
||||
i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
i915_gem_object_ggtt_unpin_view(obj, &i915_ggtt_view_normal);
|
||||
}
|
||||
|
||||
/* i915_gem_context.c */
|
||||
int __must_check i915_gem_context_init(struct drm_device *dev);
|
||||
@ -3046,6 +2958,17 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
|
||||
u32 gtt_offset,
|
||||
u32 size);
|
||||
|
||||
/* i915_gem_shrinker.c */
|
||||
unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv,
|
||||
long target,
|
||||
unsigned flags);
|
||||
#define I915_SHRINK_PURGEABLE 0x1
|
||||
#define I915_SHRINK_UNBOUND 0x2
|
||||
#define I915_SHRINK_BOUND 0x4
|
||||
unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv);
|
||||
void i915_gem_shrinker_init(struct drm_i915_private *dev_priv);
|
||||
|
||||
|
||||
/* i915_gem_tiling.c */
|
||||
static inline bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
@ -3121,10 +3044,6 @@ int i915_parse_cmds(struct intel_engine_cs *ring,
|
||||
extern int i915_save_state(struct drm_device *dev);
|
||||
extern int i915_restore_state(struct drm_device *dev);
|
||||
|
||||
/* i915_ums.c */
|
||||
void i915_save_display_reg(struct drm_device *dev);
|
||||
void i915_restore_display_reg(struct drm_device *dev);
|
||||
|
||||
/* i915_sysfs.c */
|
||||
void i915_setup_sysfs(struct drm_device *dev_priv);
|
||||
void i915_teardown_sysfs(struct drm_device *dev_priv);
|
||||
@ -3196,8 +3115,7 @@ extern void i915_redisable_vga(struct drm_device *dev);
|
||||
extern void i915_redisable_vga_power_on(struct drm_device *dev);
|
||||
extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
|
||||
extern void intel_init_pch_refclk(struct drm_device *dev);
|
||||
extern void gen6_set_rps(struct drm_device *dev, u8 val);
|
||||
extern void valleyview_set_rps(struct drm_device *dev, u8 val);
|
||||
extern void intel_set_rps(struct drm_device *dev, u8 val);
|
||||
extern void intel_set_memory_cxsr(struct drm_i915_private *dev_priv,
|
||||
bool enable);
|
||||
extern void intel_detect_pch(struct drm_device *dev);
|
||||
@ -3210,8 +3128,6 @@ int i915_reg_read_ioctl(struct drm_device *dev, void *data,
|
||||
int i915_get_reset_stats_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
|
||||
void intel_notify_mmio_flip(struct intel_engine_cs *ring);
|
||||
|
||||
/* overlay */
|
||||
extern struct intel_overlay_error_state *intel_overlay_capture_error_state(struct drm_device *dev);
|
||||
extern void intel_overlay_print_error_state(struct drm_i915_error_state_buf *e,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2008 Intel Corporation
|
||||
* Copyright © 2008-2015 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
@ -29,9 +29,9 @@
|
||||
#include <drm/drm_vma_manager.h>
|
||||
#include <drm/i915_drm.h>
|
||||
#include "i915_drv.h"
|
||||
#include "i915_vgpu.h"
|
||||
#include "i915_trace.h"
|
||||
#include "intel_drv.h"
|
||||
#include <linux/oom.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/swap.h>
|
||||
@ -52,15 +52,6 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
|
||||
struct drm_i915_fence_reg *fence,
|
||||
bool enable);
|
||||
|
||||
static unsigned long i915_gem_shrinker_count(struct shrinker *shrinker,
|
||||
struct shrink_control *sc);
|
||||
static unsigned long i915_gem_shrinker_scan(struct shrinker *shrinker,
|
||||
struct shrink_control *sc);
|
||||
static int i915_gem_shrinker_oom(struct notifier_block *nb,
|
||||
unsigned long event,
|
||||
void *ptr);
|
||||
static unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv);
|
||||
|
||||
static bool cpu_cache_is_coherent(struct drm_device *dev,
|
||||
enum i915_cache_level level)
|
||||
{
|
||||
@ -350,7 +341,7 @@ i915_gem_phys_pwrite(struct drm_i915_gem_object *obj,
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
void *vaddr = obj->phys_handle->vaddr + args->offset;
|
||||
char __user *user_data = to_user_ptr(args->data_ptr);
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
/* We manually control the domain here and pretend that it
|
||||
* remains coherent i.e. in the GTT domain, like shmem_pwrite.
|
||||
@ -359,6 +350,7 @@ i915_gem_phys_pwrite(struct drm_i915_gem_object *obj,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
intel_fb_obj_invalidate(obj, NULL, ORIGIN_CPU);
|
||||
if (__copy_from_user_inatomic_nocache(vaddr, user_data, args->size)) {
|
||||
unsigned long unwritten;
|
||||
|
||||
@ -369,13 +361,18 @@ i915_gem_phys_pwrite(struct drm_i915_gem_object *obj,
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
unwritten = copy_from_user(vaddr, user_data, args->size);
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
if (unwritten)
|
||||
return -EFAULT;
|
||||
if (unwritten) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
drm_clflush_virt_range(vaddr, args->size);
|
||||
i915_gem_chipset_flush(dev);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
intel_fb_obj_flush(obj, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *i915_gem_object_alloc(struct drm_device *dev)
|
||||
@ -809,6 +806,8 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
|
||||
|
||||
offset = i915_gem_obj_ggtt_offset(obj) + args->offset;
|
||||
|
||||
intel_fb_obj_invalidate(obj, NULL, ORIGIN_GTT);
|
||||
|
||||
while (remain > 0) {
|
||||
/* Operation in this page
|
||||
*
|
||||
@ -829,7 +828,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
|
||||
if (fast_user_write(dev_priv->gtt.mappable, page_base,
|
||||
page_offset, user_data, page_length)) {
|
||||
ret = -EFAULT;
|
||||
goto out_unpin;
|
||||
goto out_flush;
|
||||
}
|
||||
|
||||
remain -= page_length;
|
||||
@ -837,6 +836,8 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
|
||||
offset += page_length;
|
||||
}
|
||||
|
||||
out_flush:
|
||||
intel_fb_obj_flush(obj, false);
|
||||
out_unpin:
|
||||
i915_gem_object_ggtt_unpin(obj);
|
||||
out:
|
||||
@ -951,6 +952,8 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
intel_fb_obj_invalidate(obj, NULL, ORIGIN_CPU);
|
||||
|
||||
i915_gem_object_pin_pages(obj);
|
||||
|
||||
offset = args->offset;
|
||||
@ -1029,6 +1032,7 @@ out:
|
||||
if (needs_clflush_after)
|
||||
i915_gem_chipset_flush(dev);
|
||||
|
||||
intel_fb_obj_flush(obj, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1922,12 +1926,6 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
|
||||
return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset);
|
||||
}
|
||||
|
||||
static inline int
|
||||
i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
return obj->madv == I915_MADV_DONTNEED;
|
||||
}
|
||||
|
||||
/* Immediately discard the backing storage */
|
||||
static void
|
||||
i915_gem_object_truncate(struct drm_i915_gem_object *obj)
|
||||
@ -2033,85 +2031,6 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
i915_gem_shrink(struct drm_i915_private *dev_priv,
|
||||
long target, unsigned flags)
|
||||
{
|
||||
const struct {
|
||||
struct list_head *list;
|
||||
unsigned int bit;
|
||||
} phases[] = {
|
||||
{ &dev_priv->mm.unbound_list, I915_SHRINK_UNBOUND },
|
||||
{ &dev_priv->mm.bound_list, I915_SHRINK_BOUND },
|
||||
{ NULL, 0 },
|
||||
}, *phase;
|
||||
unsigned long count = 0;
|
||||
|
||||
/*
|
||||
* As we may completely rewrite the (un)bound list whilst unbinding
|
||||
* (due to retiring requests) we have to strictly process only
|
||||
* one element of the list at the time, and recheck the list
|
||||
* on every iteration.
|
||||
*
|
||||
* In particular, we must hold a reference whilst removing the
|
||||
* object as we may end up waiting for and/or retiring the objects.
|
||||
* This might release the final reference (held by the active list)
|
||||
* and result in the object being freed from under us. This is
|
||||
* similar to the precautions the eviction code must take whilst
|
||||
* removing objects.
|
||||
*
|
||||
* Also note that although these lists do not hold a reference to
|
||||
* the object we can safely grab one here: The final object
|
||||
* unreferencing and the bound_list are both protected by the
|
||||
* dev->struct_mutex and so we won't ever be able to observe an
|
||||
* object on the bound_list with a reference count equals 0.
|
||||
*/
|
||||
for (phase = phases; phase->list; phase++) {
|
||||
struct list_head still_in_list;
|
||||
|
||||
if ((flags & phase->bit) == 0)
|
||||
continue;
|
||||
|
||||
INIT_LIST_HEAD(&still_in_list);
|
||||
while (count < target && !list_empty(phase->list)) {
|
||||
struct drm_i915_gem_object *obj;
|
||||
struct i915_vma *vma, *v;
|
||||
|
||||
obj = list_first_entry(phase->list,
|
||||
typeof(*obj), global_list);
|
||||
list_move_tail(&obj->global_list, &still_in_list);
|
||||
|
||||
if (flags & I915_SHRINK_PURGEABLE &&
|
||||
!i915_gem_object_is_purgeable(obj))
|
||||
continue;
|
||||
|
||||
drm_gem_object_reference(&obj->base);
|
||||
|
||||
/* For the unbound phase, this should be a no-op! */
|
||||
list_for_each_entry_safe(vma, v,
|
||||
&obj->vma_list, vma_link)
|
||||
if (i915_vma_unbind(vma))
|
||||
break;
|
||||
|
||||
if (i915_gem_object_put_pages(obj) == 0)
|
||||
count += obj->base.size >> PAGE_SHIFT;
|
||||
|
||||
drm_gem_object_unreference(&obj->base);
|
||||
}
|
||||
list_splice(&still_in_list, phase->list);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
i915_gem_shrink_all(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
i915_gem_evict_everything(dev_priv->dev);
|
||||
return i915_gem_shrink(dev_priv, LONG_MAX,
|
||||
I915_SHRINK_BOUND | I915_SHRINK_UNBOUND);
|
||||
}
|
||||
|
||||
static int
|
||||
i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
@ -2492,6 +2411,8 @@ int __i915_add_request(struct intel_engine_cs *ring,
|
||||
list_add_tail(&request->client_list,
|
||||
&file_priv->mm.request_list);
|
||||
spin_unlock(&file_priv->mm.lock);
|
||||
|
||||
request->pid = get_pid(task_pid(current));
|
||||
}
|
||||
|
||||
trace_i915_gem_request_add(request);
|
||||
@ -2572,6 +2493,8 @@ static void i915_gem_free_request(struct drm_i915_gem_request *request)
|
||||
list_del(&request->list);
|
||||
i915_gem_request_remove_from_client(request);
|
||||
|
||||
put_pid(request->pid);
|
||||
|
||||
i915_gem_request_unreference(request);
|
||||
}
|
||||
|
||||
@ -2744,7 +2667,6 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
|
||||
*/
|
||||
while (!list_empty(&ring->request_list)) {
|
||||
struct drm_i915_gem_request *request;
|
||||
struct intel_ringbuffer *ringbuf;
|
||||
|
||||
request = list_first_entry(&ring->request_list,
|
||||
struct drm_i915_gem_request,
|
||||
@ -2755,23 +2677,12 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
|
||||
|
||||
trace_i915_gem_request_retire(request);
|
||||
|
||||
/* This is one of the few common intersection points
|
||||
* between legacy ringbuffer submission and execlists:
|
||||
* we need to tell them apart in order to find the correct
|
||||
* ringbuffer to which the request belongs to.
|
||||
*/
|
||||
if (i915.enable_execlists) {
|
||||
struct intel_context *ctx = request->ctx;
|
||||
ringbuf = ctx->engine[ring->id].ringbuf;
|
||||
} else
|
||||
ringbuf = ring->buffer;
|
||||
|
||||
/* We know the GPU must have read the request to have
|
||||
* sent us the seqno + interrupt, so use the position
|
||||
* of tail of the request to update the last known position
|
||||
* of the GPU head.
|
||||
*/
|
||||
ringbuf->last_retired_head = request->postfix;
|
||||
request->ringbuf->last_retired_head = request->postfix;
|
||||
|
||||
i915_gem_free_request(request);
|
||||
}
|
||||
@ -3516,9 +3427,9 @@ static bool i915_gem_valid_gtt_space(struct i915_vma *vma,
|
||||
static struct i915_vma *
|
||||
i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm,
|
||||
const struct i915_ggtt_view *ggtt_view,
|
||||
unsigned alignment,
|
||||
uint64_t flags,
|
||||
const struct i915_ggtt_view *view)
|
||||
uint64_t flags)
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
@ -3530,6 +3441,9 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
|
||||
struct i915_vma *vma;
|
||||
int ret;
|
||||
|
||||
if(WARN_ON(i915_is_ggtt(vm) != !!ggtt_view))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
fence_size = i915_gem_get_gtt_size(dev,
|
||||
obj->base.size,
|
||||
obj->tiling_mode);
|
||||
@ -3568,7 +3482,9 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
|
||||
|
||||
i915_gem_object_pin_pages(obj);
|
||||
|
||||
vma = i915_gem_obj_lookup_or_create_vma_view(obj, vm, view);
|
||||
vma = ggtt_view ? i915_gem_obj_lookup_or_create_ggtt_vma(obj, ggtt_view) :
|
||||
i915_gem_obj_lookup_or_create_vma(obj, vm);
|
||||
|
||||
if (IS_ERR(vma))
|
||||
goto err_unpin;
|
||||
|
||||
@ -3598,6 +3514,17 @@ search_free:
|
||||
if (ret)
|
||||
goto err_remove_node;
|
||||
|
||||
/* allocate before insert / bind */
|
||||
if (vma->vm->allocate_va_range) {
|
||||
trace_i915_va_alloc(vma->vm, vma->node.start, vma->node.size,
|
||||
VM_TO_TRACE_NAME(vma->vm));
|
||||
ret = vma->vm->allocate_va_range(vma->vm,
|
||||
vma->node.start,
|
||||
vma->node.size);
|
||||
if (ret)
|
||||
goto err_remove_node;
|
||||
}
|
||||
|
||||
trace_i915_vma_bind(vma, flags);
|
||||
ret = i915_vma_bind(vma, obj->cache_level,
|
||||
flags & PIN_GLOBAL ? GLOBAL_BIND : 0);
|
||||
@ -3768,7 +3695,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
|
||||
}
|
||||
|
||||
if (write)
|
||||
intel_fb_obj_invalidate(obj, NULL);
|
||||
intel_fb_obj_invalidate(obj, NULL, ORIGIN_GTT);
|
||||
|
||||
trace_i915_gem_object_change_domain(obj,
|
||||
old_read_domains,
|
||||
@ -3950,7 +3877,8 @@ static bool is_pin_display(struct drm_i915_gem_object *obj)
|
||||
int
|
||||
i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
|
||||
u32 alignment,
|
||||
struct intel_engine_cs *pipelined)
|
||||
struct intel_engine_cs *pipelined,
|
||||
const struct i915_ggtt_view *view)
|
||||
{
|
||||
u32 old_read_domains, old_write_domain;
|
||||
bool was_pin_display;
|
||||
@ -3986,7 +3914,9 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
|
||||
* (e.g. libkms for the bootup splash), we have to ensure that we
|
||||
* always use map_and_fenceable for all scanout buffers.
|
||||
*/
|
||||
ret = i915_gem_obj_ggtt_pin(obj, alignment, PIN_MAPPABLE);
|
||||
ret = i915_gem_object_ggtt_pin(obj, view, alignment,
|
||||
view->type == I915_GGTT_VIEW_NORMAL ?
|
||||
PIN_MAPPABLE : 0);
|
||||
if (ret)
|
||||
goto err_unpin_display;
|
||||
|
||||
@ -4014,9 +3944,11 @@ err_unpin_display:
|
||||
}
|
||||
|
||||
void
|
||||
i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj)
|
||||
i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj,
|
||||
const struct i915_ggtt_view *view)
|
||||
{
|
||||
i915_gem_object_ggtt_unpin(obj);
|
||||
i915_gem_object_ggtt_unpin_view(obj, view);
|
||||
|
||||
obj->pin_display = is_pin_display(obj);
|
||||
}
|
||||
|
||||
@ -4083,7 +4015,7 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write)
|
||||
}
|
||||
|
||||
if (write)
|
||||
intel_fb_obj_invalidate(obj, NULL);
|
||||
intel_fb_obj_invalidate(obj, NULL, ORIGIN_CPU);
|
||||
|
||||
trace_i915_gem_object_change_domain(obj,
|
||||
old_read_domains,
|
||||
@ -4165,12 +4097,12 @@ i915_vma_misplaced(struct i915_vma *vma, uint32_t alignment, uint64_t flags)
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
i915_gem_object_pin_view(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm,
|
||||
uint32_t alignment,
|
||||
uint64_t flags,
|
||||
const struct i915_ggtt_view *view)
|
||||
static int
|
||||
i915_gem_object_do_pin(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm,
|
||||
const struct i915_ggtt_view *ggtt_view,
|
||||
uint32_t alignment,
|
||||
uint64_t flags)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
|
||||
struct i915_vma *vma;
|
||||
@ -4186,17 +4118,29 @@ i915_gem_object_pin_view(struct drm_i915_gem_object *obj,
|
||||
if (WARN_ON((flags & (PIN_MAPPABLE | PIN_GLOBAL)) == PIN_MAPPABLE))
|
||||
return -EINVAL;
|
||||
|
||||
vma = i915_gem_obj_to_vma_view(obj, vm, view);
|
||||
if (WARN_ON(i915_is_ggtt(vm) != !!ggtt_view))
|
||||
return -EINVAL;
|
||||
|
||||
vma = ggtt_view ? i915_gem_obj_to_ggtt_view(obj, ggtt_view) :
|
||||
i915_gem_obj_to_vma(obj, vm);
|
||||
|
||||
if (IS_ERR(vma))
|
||||
return PTR_ERR(vma);
|
||||
|
||||
if (vma) {
|
||||
if (WARN_ON(vma->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT))
|
||||
return -EBUSY;
|
||||
|
||||
if (i915_vma_misplaced(vma, alignment, flags)) {
|
||||
unsigned long offset;
|
||||
offset = ggtt_view ? i915_gem_obj_ggtt_offset_view(obj, ggtt_view) :
|
||||
i915_gem_obj_offset(obj, vm);
|
||||
WARN(vma->pin_count,
|
||||
"bo is already pinned with incorrect alignment:"
|
||||
"bo is already pinned in %s with incorrect alignment:"
|
||||
" offset=%lx, req.alignment=%x, req.map_and_fenceable=%d,"
|
||||
" obj->map_and_fenceable=%d\n",
|
||||
i915_gem_obj_offset_view(obj, vm, view->type),
|
||||
ggtt_view ? "ggtt" : "ppgtt",
|
||||
offset,
|
||||
alignment,
|
||||
!!(flags & PIN_MAPPABLE),
|
||||
obj->map_and_fenceable);
|
||||
@ -4210,8 +4154,12 @@ i915_gem_object_pin_view(struct drm_i915_gem_object *obj,
|
||||
|
||||
bound = vma ? vma->bound : 0;
|
||||
if (vma == NULL || !drm_mm_node_allocated(&vma->node)) {
|
||||
vma = i915_gem_object_bind_to_vm(obj, vm, alignment,
|
||||
flags, view);
|
||||
/* In true PPGTT, bind has possibly changed PDEs, which
|
||||
* means we must do a context switch before the GPU can
|
||||
* accurately read some of the VMAs.
|
||||
*/
|
||||
vma = i915_gem_object_bind_to_vm(obj, vm, ggtt_view, alignment,
|
||||
flags);
|
||||
if (IS_ERR(vma))
|
||||
return PTR_ERR(vma);
|
||||
}
|
||||
@ -4237,7 +4185,7 @@ i915_gem_object_pin_view(struct drm_i915_gem_object *obj,
|
||||
fenceable = (vma->node.size == fence_size &&
|
||||
(vma->node.start & (fence_alignment - 1)) == 0);
|
||||
|
||||
mappable = (vma->node.start + obj->base.size <=
|
||||
mappable = (vma->node.start + fence_size <=
|
||||
dev_priv->gtt.mappable_end);
|
||||
|
||||
obj->map_and_fenceable = mappable && fenceable;
|
||||
@ -4252,16 +4200,41 @@ i915_gem_object_pin_view(struct drm_i915_gem_object *obj,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj)
|
||||
int
|
||||
i915_gem_object_pin(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm,
|
||||
uint32_t alignment,
|
||||
uint64_t flags)
|
||||
{
|
||||
struct i915_vma *vma = i915_gem_obj_to_ggtt(obj);
|
||||
return i915_gem_object_do_pin(obj, vm,
|
||||
i915_is_ggtt(vm) ? &i915_ggtt_view_normal : NULL,
|
||||
alignment, flags);
|
||||
}
|
||||
|
||||
int
|
||||
i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
|
||||
const struct i915_ggtt_view *view,
|
||||
uint32_t alignment,
|
||||
uint64_t flags)
|
||||
{
|
||||
if (WARN_ONCE(!view, "no view specified"))
|
||||
return -EINVAL;
|
||||
|
||||
return i915_gem_object_do_pin(obj, i915_obj_to_ggtt(obj), view,
|
||||
alignment, flags | PIN_GLOBAL);
|
||||
}
|
||||
|
||||
void
|
||||
i915_gem_object_ggtt_unpin_view(struct drm_i915_gem_object *obj,
|
||||
const struct i915_ggtt_view *view)
|
||||
{
|
||||
struct i915_vma *vma = i915_gem_obj_to_ggtt_view(obj, view);
|
||||
|
||||
BUG_ON(!vma);
|
||||
BUG_ON(vma->pin_count == 0);
|
||||
BUG_ON(!i915_gem_obj_ggtt_bound(obj));
|
||||
WARN_ON(vma->pin_count == 0);
|
||||
WARN_ON(!i915_gem_obj_ggtt_bound_view(obj, view));
|
||||
|
||||
if (--vma->pin_count == 0)
|
||||
if (--vma->pin_count == 0 && view->type == I915_GGTT_VIEW_NORMAL)
|
||||
obj->pin_mappable = false;
|
||||
}
|
||||
|
||||
@ -4382,7 +4355,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
|
||||
obj->madv = args->madv;
|
||||
|
||||
/* if the object is no longer attached, discard its backing storage */
|
||||
if (i915_gem_object_is_purgeable(obj) && obj->pages == NULL)
|
||||
if (obj->madv == I915_MADV_DONTNEED && obj->pages == NULL)
|
||||
i915_gem_object_truncate(obj);
|
||||
|
||||
args->retained = obj->madv != __I915_MADV_PURGED;
|
||||
@ -4557,15 +4530,33 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
|
||||
intel_runtime_pm_put(dev_priv);
|
||||
}
|
||||
|
||||
struct i915_vma *i915_gem_obj_to_vma_view(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm,
|
||||
const struct i915_ggtt_view *view)
|
||||
struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
|
||||
struct i915_address_space *vm)
|
||||
{
|
||||
struct i915_vma *vma;
|
||||
list_for_each_entry(vma, &obj->vma_list, vma_link)
|
||||
if (vma->vm == vm && vma->ggtt_view.type == view->type)
|
||||
list_for_each_entry(vma, &obj->vma_list, vma_link) {
|
||||
if (i915_is_ggtt(vma->vm) &&
|
||||
vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL)
|
||||
continue;
|
||||
if (vma->vm == vm)
|
||||
return vma;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct i915_vma *i915_gem_obj_to_ggtt_view(struct drm_i915_gem_object *obj,
|
||||
const struct i915_ggtt_view *view)
|
||||
{
|
||||
struct i915_address_space *ggtt = i915_obj_to_ggtt(obj);
|
||||
struct i915_vma *vma;
|
||||
|
||||
if (WARN_ONCE(!view, "no view specified"))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
list_for_each_entry(vma, &obj->vma_list, vma_link)
|
||||
if (vma->vm == ggtt &&
|
||||
i915_ggtt_view_equal(&vma->ggtt_view, view))
|
||||
return vma;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -4612,10 +4603,6 @@ i915_gem_suspend(struct drm_device *dev)
|
||||
|
||||
i915_gem_retire_requests(dev);
|
||||
|
||||
/* Under UMS, be paranoid and evict. */
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
i915_gem_evict_everything(dev);
|
||||
|
||||
i915_gem_stop_ringbuffers(dev);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
@ -4986,18 +4973,8 @@ i915_gem_load(struct drm_device *dev)
|
||||
i915_gem_idle_work_handler);
|
||||
init_waitqueue_head(&dev_priv->gpu_error.reset_queue);
|
||||
|
||||
/* On GEN3 we really need to make sure the ARB C3 LP bit is set */
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET) && IS_GEN3(dev)) {
|
||||
I915_WRITE(MI_ARB_STATE,
|
||||
_MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE));
|
||||
}
|
||||
|
||||
dev_priv->relative_constants_mode = I915_EXEC_CONSTANTS_REL_GENERAL;
|
||||
|
||||
/* Old X drivers will take 0-2 for front, back, depth buffers */
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
dev_priv->fence_reg_start = 3;
|
||||
|
||||
if (INTEL_INFO(dev)->gen >= 7 && !IS_VALLEYVIEW(dev))
|
||||
dev_priv->num_fence_regs = 32;
|
||||
else if (INTEL_INFO(dev)->gen >= 4 || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
|
||||
@ -5005,6 +4982,10 @@ i915_gem_load(struct drm_device *dev)
|
||||
else
|
||||
dev_priv->num_fence_regs = 8;
|
||||
|
||||
if (intel_vgpu_active(dev))
|
||||
dev_priv->num_fence_regs =
|
||||
I915_READ(vgtif_reg(avail_rs.fence_num));
|
||||
|
||||
/* Initialize fence registers to zero */
|
||||
INIT_LIST_HEAD(&dev_priv->mm.fence_list);
|
||||
i915_gem_restore_fences(dev);
|
||||
@ -5014,13 +4995,7 @@ i915_gem_load(struct drm_device *dev)
|
||||
|
||||
dev_priv->mm.interruptible = true;
|
||||
|
||||
dev_priv->mm.shrinker.scan_objects = i915_gem_shrinker_scan;
|
||||
dev_priv->mm.shrinker.count_objects = i915_gem_shrinker_count;
|
||||
dev_priv->mm.shrinker.seeks = DEFAULT_SEEKS;
|
||||
register_shrinker(&dev_priv->mm.shrinker);
|
||||
|
||||
dev_priv->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom;
|
||||
register_oom_notifier(&dev_priv->mm.oom_notifier);
|
||||
i915_gem_shrinker_init(dev_priv);
|
||||
|
||||
i915_gem_batch_pool_init(dev, &dev_priv->mm.batch_pool);
|
||||
|
||||
@ -5112,81 +5087,10 @@ void i915_gem_track_fb(struct drm_i915_gem_object *old,
|
||||
}
|
||||
}
|
||||
|
||||
static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
|
||||
{
|
||||
if (!mutex_is_locked(mutex))
|
||||
return false;
|
||||
|
||||
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES)
|
||||
return mutex->owner == task;
|
||||
#else
|
||||
/* Since UP may be pre-empted, we cannot assume that we own the lock */
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
|
||||
{
|
||||
if (!mutex_trylock(&dev->struct_mutex)) {
|
||||
if (!mutex_is_locked_by(&dev->struct_mutex, current))
|
||||
return false;
|
||||
|
||||
if (to_i915(dev)->mm.shrinker_no_lock_stealing)
|
||||
return false;
|
||||
|
||||
*unlock = false;
|
||||
} else
|
||||
*unlock = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int num_vma_bound(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct i915_vma *vma;
|
||||
int count = 0;
|
||||
|
||||
list_for_each_entry(vma, &obj->vma_list, vma_link)
|
||||
if (drm_mm_node_allocated(&vma->node))
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
i915_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
|
||||
{
|
||||
struct drm_i915_private *dev_priv =
|
||||
container_of(shrinker, struct drm_i915_private, mm.shrinker);
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
struct drm_i915_gem_object *obj;
|
||||
unsigned long count;
|
||||
bool unlock;
|
||||
|
||||
if (!i915_gem_shrinker_lock(dev, &unlock))
|
||||
return 0;
|
||||
|
||||
count = 0;
|
||||
list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list)
|
||||
if (obj->pages_pin_count == 0)
|
||||
count += obj->base.size >> PAGE_SHIFT;
|
||||
|
||||
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
|
||||
if (!i915_gem_obj_is_pinned(obj) &&
|
||||
obj->pages_pin_count == num_vma_bound(obj))
|
||||
count += obj->base.size >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
if (unlock)
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* All the new VM stuff */
|
||||
unsigned long i915_gem_obj_offset_view(struct drm_i915_gem_object *o,
|
||||
struct i915_address_space *vm,
|
||||
enum i915_ggtt_view_type view)
|
||||
unsigned long
|
||||
i915_gem_obj_offset(struct drm_i915_gem_object *o,
|
||||
struct i915_address_space *vm)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = o->base.dev->dev_private;
|
||||
struct i915_vma *vma;
|
||||
@ -5194,24 +5098,59 @@ unsigned long i915_gem_obj_offset_view(struct drm_i915_gem_object *o,
|
||||
WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base);
|
||||
|
||||
list_for_each_entry(vma, &o->vma_list, vma_link) {
|
||||
if (vma->vm == vm && vma->ggtt_view.type == view)
|
||||
if (i915_is_ggtt(vma->vm) &&
|
||||
vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL)
|
||||
continue;
|
||||
if (vma->vm == vm)
|
||||
return vma->node.start;
|
||||
|
||||
}
|
||||
|
||||
WARN(1, "%s vma for this object not found.\n",
|
||||
i915_is_ggtt(vm) ? "global" : "ppgtt");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool i915_gem_obj_bound_view(struct drm_i915_gem_object *o,
|
||||
struct i915_address_space *vm,
|
||||
enum i915_ggtt_view_type view)
|
||||
unsigned long
|
||||
i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o,
|
||||
const struct i915_ggtt_view *view)
|
||||
{
|
||||
struct i915_address_space *ggtt = i915_obj_to_ggtt(o);
|
||||
struct i915_vma *vma;
|
||||
|
||||
list_for_each_entry(vma, &o->vma_list, vma_link)
|
||||
if (vma->vm == vm &&
|
||||
vma->ggtt_view.type == view &&
|
||||
if (vma->vm == ggtt &&
|
||||
i915_ggtt_view_equal(&vma->ggtt_view, view))
|
||||
return vma->node.start;
|
||||
|
||||
WARN(1, "global vma for this object not found.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool i915_gem_obj_bound(struct drm_i915_gem_object *o,
|
||||
struct i915_address_space *vm)
|
||||
{
|
||||
struct i915_vma *vma;
|
||||
|
||||
list_for_each_entry(vma, &o->vma_list, vma_link) {
|
||||
if (i915_is_ggtt(vma->vm) &&
|
||||
vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL)
|
||||
continue;
|
||||
if (vma->vm == vm && drm_mm_node_allocated(&vma->node))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool i915_gem_obj_ggtt_bound_view(struct drm_i915_gem_object *o,
|
||||
const struct i915_ggtt_view *view)
|
||||
{
|
||||
struct i915_address_space *ggtt = i915_obj_to_ggtt(o);
|
||||
struct i915_vma *vma;
|
||||
|
||||
list_for_each_entry(vma, &o->vma_list, vma_link)
|
||||
if (vma->vm == ggtt &&
|
||||
i915_ggtt_view_equal(&vma->ggtt_view, view) &&
|
||||
drm_mm_node_allocated(&vma->node))
|
||||
return true;
|
||||
|
||||
@ -5239,118 +5178,26 @@ unsigned long i915_gem_obj_size(struct drm_i915_gem_object *o,
|
||||
|
||||
BUG_ON(list_empty(&o->vma_list));
|
||||
|
||||
list_for_each_entry(vma, &o->vma_list, vma_link)
|
||||
list_for_each_entry(vma, &o->vma_list, vma_link) {
|
||||
if (i915_is_ggtt(vma->vm) &&
|
||||
vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL)
|
||||
continue;
|
||||
if (vma->vm == vm)
|
||||
return vma->node.size;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
|
||||
bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct drm_i915_private *dev_priv =
|
||||
container_of(shrinker, struct drm_i915_private, mm.shrinker);
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
unsigned long freed;
|
||||
bool unlock;
|
||||
|
||||
if (!i915_gem_shrinker_lock(dev, &unlock))
|
||||
return SHRINK_STOP;
|
||||
|
||||
freed = i915_gem_shrink(dev_priv,
|
||||
sc->nr_to_scan,
|
||||
I915_SHRINK_BOUND |
|
||||
I915_SHRINK_UNBOUND |
|
||||
I915_SHRINK_PURGEABLE);
|
||||
if (freed < sc->nr_to_scan)
|
||||
freed += i915_gem_shrink(dev_priv,
|
||||
sc->nr_to_scan - freed,
|
||||
I915_SHRINK_BOUND |
|
||||
I915_SHRINK_UNBOUND);
|
||||
if (unlock)
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return freed;
|
||||
}
|
||||
|
||||
static int
|
||||
i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
|
||||
{
|
||||
struct drm_i915_private *dev_priv =
|
||||
container_of(nb, struct drm_i915_private, mm.oom_notifier);
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
struct drm_i915_gem_object *obj;
|
||||
unsigned long timeout = msecs_to_jiffies(5000) + 1;
|
||||
unsigned long pinned, bound, unbound, freed_pages;
|
||||
bool was_interruptible;
|
||||
bool unlock;
|
||||
|
||||
while (!i915_gem_shrinker_lock(dev, &unlock) && --timeout) {
|
||||
schedule_timeout_killable(1);
|
||||
if (fatal_signal_pending(current))
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
if (timeout == 0) {
|
||||
pr_err("Unable to purge GPU memory due lock contention.\n");
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
was_interruptible = dev_priv->mm.interruptible;
|
||||
dev_priv->mm.interruptible = false;
|
||||
|
||||
freed_pages = i915_gem_shrink_all(dev_priv);
|
||||
|
||||
dev_priv->mm.interruptible = was_interruptible;
|
||||
|
||||
/* Because we may be allocating inside our own driver, we cannot
|
||||
* assert that there are no objects with pinned pages that are not
|
||||
* being pointed to by hardware.
|
||||
*/
|
||||
unbound = bound = pinned = 0;
|
||||
list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) {
|
||||
if (!obj->base.filp) /* not backed by a freeable object */
|
||||
continue;
|
||||
|
||||
if (obj->pages_pin_count)
|
||||
pinned += obj->base.size;
|
||||
else
|
||||
unbound += obj->base.size;
|
||||
}
|
||||
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
|
||||
if (!obj->base.filp)
|
||||
continue;
|
||||
|
||||
if (obj->pages_pin_count)
|
||||
pinned += obj->base.size;
|
||||
else
|
||||
bound += obj->base.size;
|
||||
}
|
||||
|
||||
if (unlock)
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (freed_pages || unbound || bound)
|
||||
pr_info("Purging GPU memory, %lu bytes freed, %lu bytes still pinned.\n",
|
||||
freed_pages << PAGE_SHIFT, pinned);
|
||||
if (unbound || bound)
|
||||
pr_err("%lu and %lu bytes still available in the "
|
||||
"bound and unbound GPU page lists.\n",
|
||||
bound, unbound);
|
||||
|
||||
*(unsigned long *)ptr += freed_pages;
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
struct i915_vma *i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct i915_address_space *ggtt = i915_obj_to_ggtt(obj);
|
||||
struct i915_vma *vma;
|
||||
|
||||
list_for_each_entry(vma, &obj->vma_list, vma_link)
|
||||
if (vma->vm == ggtt &&
|
||||
vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL)
|
||||
return vma;
|
||||
|
||||
return NULL;
|
||||
list_for_each_entry(vma, &obj->vma_list, vma_link) {
|
||||
if (i915_is_ggtt(vma->vm) &&
|
||||
vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL)
|
||||
continue;
|
||||
if (vma->pin_count > 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -296,11 +296,15 @@ void i915_gem_context_reset(struct drm_device *dev)
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int i;
|
||||
|
||||
/* In execlists mode we will unreference the context when the execlist
|
||||
* queue is cleared and the requests destroyed.
|
||||
*/
|
||||
if (i915.enable_execlists)
|
||||
if (i915.enable_execlists) {
|
||||
struct intel_context *ctx;
|
||||
|
||||
list_for_each_entry(ctx, &dev_priv->context_list, link) {
|
||||
intel_lr_context_reset(dev, ctx);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < I915_NUM_RINGS; i++) {
|
||||
struct intel_engine_cs *ring = &dev_priv->ring[i];
|
||||
@ -565,6 +569,66 @@ mi_set_context(struct intel_engine_cs *ring,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool should_skip_switch(struct intel_engine_cs *ring,
|
||||
struct intel_context *from,
|
||||
struct intel_context *to)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = ring->dev->dev_private;
|
||||
|
||||
if (to->remap_slice)
|
||||
return false;
|
||||
|
||||
if (to->ppgtt) {
|
||||
if (from == to && !test_bit(ring->id,
|
||||
&to->ppgtt->pd_dirty_rings))
|
||||
return true;
|
||||
} else if (dev_priv->mm.aliasing_ppgtt) {
|
||||
if (from == to && !test_bit(ring->id,
|
||||
&dev_priv->mm.aliasing_ppgtt->pd_dirty_rings))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
needs_pd_load_pre(struct intel_engine_cs *ring, struct intel_context *to)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = ring->dev->dev_private;
|
||||
|
||||
if (!to->ppgtt)
|
||||
return false;
|
||||
|
||||
if (INTEL_INFO(ring->dev)->gen < 8)
|
||||
return true;
|
||||
|
||||
if (ring != &dev_priv->ring[RCS])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
needs_pd_load_post(struct intel_engine_cs *ring, struct intel_context *to,
|
||||
u32 hw_flags)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = ring->dev->dev_private;
|
||||
|
||||
if (!to->ppgtt)
|
||||
return false;
|
||||
|
||||
if (!IS_GEN8(ring->dev))
|
||||
return false;
|
||||
|
||||
if (ring != &dev_priv->ring[RCS])
|
||||
return false;
|
||||
|
||||
if (hw_flags & MI_RESTORE_INHIBIT)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int do_switch(struct intel_engine_cs *ring,
|
||||
struct intel_context *to)
|
||||
{
|
||||
@ -580,7 +644,7 @@ static int do_switch(struct intel_engine_cs *ring,
|
||||
BUG_ON(!i915_gem_obj_is_pinned(from->legacy_hw_ctx.rcs_state));
|
||||
}
|
||||
|
||||
if (from == to && !to->remap_slice)
|
||||
if (should_skip_switch(ring, from, to))
|
||||
return 0;
|
||||
|
||||
/* Trying to pin first makes error handling easier. */
|
||||
@ -598,11 +662,18 @@ static int do_switch(struct intel_engine_cs *ring,
|
||||
*/
|
||||
from = ring->last_context;
|
||||
|
||||
if (to->ppgtt) {
|
||||
if (needs_pd_load_pre(ring, to)) {
|
||||
/* Older GENs and non render rings still want the load first,
|
||||
* "PP_DCLV followed by PP_DIR_BASE register through Load
|
||||
* Register Immediate commands in Ring Buffer before submitting
|
||||
* a context."*/
|
||||
trace_switch_mm(ring, to);
|
||||
ret = to->ppgtt->switch_mm(to->ppgtt, ring);
|
||||
if (ret)
|
||||
goto unpin_out;
|
||||
|
||||
/* Doing a PD load always reloads the page dirs */
|
||||
clear_bit(ring->id, &to->ppgtt->pd_dirty_rings);
|
||||
}
|
||||
|
||||
if (ring != &dev_priv->ring[RCS]) {
|
||||
@ -633,13 +704,41 @@ static int do_switch(struct intel_engine_cs *ring,
|
||||
goto unpin_out;
|
||||
}
|
||||
|
||||
if (!to->legacy_hw_ctx.initialized || i915_gem_context_is_default(to))
|
||||
if (!to->legacy_hw_ctx.initialized) {
|
||||
hw_flags |= MI_RESTORE_INHIBIT;
|
||||
/* NB: If we inhibit the restore, the context is not allowed to
|
||||
* die because future work may end up depending on valid address
|
||||
* space. This means we must enforce that a page table load
|
||||
* occur when this occurs. */
|
||||
} else if (to->ppgtt &&
|
||||
test_and_clear_bit(ring->id, &to->ppgtt->pd_dirty_rings))
|
||||
hw_flags |= MI_FORCE_RESTORE;
|
||||
|
||||
/* We should never emit switch_mm more than once */
|
||||
WARN_ON(needs_pd_load_pre(ring, to) &&
|
||||
needs_pd_load_post(ring, to, hw_flags));
|
||||
|
||||
ret = mi_set_context(ring, to, hw_flags);
|
||||
if (ret)
|
||||
goto unpin_out;
|
||||
|
||||
/* GEN8 does *not* require an explicit reload if the PDPs have been
|
||||
* setup, and we do not wish to move them.
|
||||
*/
|
||||
if (needs_pd_load_post(ring, to, hw_flags)) {
|
||||
trace_switch_mm(ring, to);
|
||||
ret = to->ppgtt->switch_mm(to->ppgtt, ring);
|
||||
/* The hardware context switch is emitted, but we haven't
|
||||
* actually changed the state - so it's probably safe to bail
|
||||
* here. Still, let the user know something dangerous has
|
||||
* happened.
|
||||
*/
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to change address space on context switch\n");
|
||||
goto unpin_out;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_L3_SLICES; i++) {
|
||||
if (!(to->remap_slice & (1<<i)))
|
||||
continue;
|
||||
@ -677,7 +776,7 @@ static int do_switch(struct intel_engine_cs *ring,
|
||||
i915_gem_context_unreference(from);
|
||||
}
|
||||
|
||||
uninitialized = !to->legacy_hw_ctx.initialized && from == NULL;
|
||||
uninitialized = !to->legacy_hw_ctx.initialized;
|
||||
to->legacy_hw_ctx.initialized = true;
|
||||
|
||||
done:
|
||||
|
@ -63,6 +63,10 @@ mark_free(struct i915_vma *vma, struct list_head *unwind)
|
||||
*
|
||||
* This function is used by the object/vma binding code.
|
||||
*
|
||||
* Since this function is only used to free up virtual address space it only
|
||||
* ignores pinned vmas, and not object where the backing storage itself is
|
||||
* pinned. Hence obj->pages_pin_count does not protect against eviction.
|
||||
*
|
||||
* To clarify: This is for freeing up virtual address space, not for freeing
|
||||
* memory in e.g. the shrinker.
|
||||
*/
|
||||
|
@ -251,7 +251,6 @@ static inline int use_cpu_reloc(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
return (HAS_LLC(obj->base.dev) ||
|
||||
obj->base.write_domain == I915_GEM_DOMAIN_CPU ||
|
||||
!obj->map_and_fenceable ||
|
||||
obj->cache_level != I915_CACHE_NONE);
|
||||
}
|
||||
|
||||
@ -337,6 +336,51 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
clflush_write32(void *addr, uint32_t value)
|
||||
{
|
||||
/* This is not a fast path, so KISS. */
|
||||
drm_clflush_virt_range(addr, sizeof(uint32_t));
|
||||
*(uint32_t *)addr = value;
|
||||
drm_clflush_virt_range(addr, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static int
|
||||
relocate_entry_clflush(struct drm_i915_gem_object *obj,
|
||||
struct drm_i915_gem_relocation_entry *reloc,
|
||||
uint64_t target_offset)
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
uint32_t page_offset = offset_in_page(reloc->offset);
|
||||
uint64_t delta = (int)reloc->delta + target_offset;
|
||||
char *vaddr;
|
||||
int ret;
|
||||
|
||||
ret = i915_gem_object_set_to_gtt_domain(obj, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vaddr = kmap_atomic(i915_gem_object_get_page(obj,
|
||||
reloc->offset >> PAGE_SHIFT));
|
||||
clflush_write32(vaddr + page_offset, lower_32_bits(delta));
|
||||
|
||||
if (INTEL_INFO(dev)->gen >= 8) {
|
||||
page_offset = offset_in_page(page_offset + sizeof(uint32_t));
|
||||
|
||||
if (page_offset == 0) {
|
||||
kunmap_atomic(vaddr);
|
||||
vaddr = kmap_atomic(i915_gem_object_get_page(obj,
|
||||
(reloc->offset + sizeof(uint32_t)) >> PAGE_SHIFT));
|
||||
}
|
||||
|
||||
clflush_write32(vaddr + page_offset, upper_32_bits(delta));
|
||||
}
|
||||
|
||||
kunmap_atomic(vaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
|
||||
struct eb_vmas *eb,
|
||||
@ -426,8 +470,14 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
|
||||
|
||||
if (use_cpu_reloc(obj))
|
||||
ret = relocate_entry_cpu(obj, reloc, target_offset);
|
||||
else
|
||||
else if (obj->map_and_fenceable)
|
||||
ret = relocate_entry_gtt(obj, reloc, target_offset);
|
||||
else if (cpu_has_clflush)
|
||||
ret = relocate_entry_clflush(obj, reloc, target_offset);
|
||||
else {
|
||||
WARN_ONCE(1, "Impossible case in relocation handling\n");
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -525,6 +575,12 @@ i915_gem_execbuffer_relocate(struct eb_vmas *eb)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool only_mappable_for_reloc(unsigned int flags)
|
||||
{
|
||||
return (flags & (EXEC_OBJECT_NEEDS_FENCE | __EXEC_OBJECT_NEEDS_MAP)) ==
|
||||
__EXEC_OBJECT_NEEDS_MAP;
|
||||
}
|
||||
|
||||
static int
|
||||
i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
|
||||
struct intel_engine_cs *ring,
|
||||
@ -536,14 +592,21 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
|
||||
int ret;
|
||||
|
||||
flags = 0;
|
||||
if (entry->flags & __EXEC_OBJECT_NEEDS_MAP)
|
||||
flags |= PIN_GLOBAL | PIN_MAPPABLE;
|
||||
if (entry->flags & EXEC_OBJECT_NEEDS_GTT)
|
||||
flags |= PIN_GLOBAL;
|
||||
if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS)
|
||||
flags |= BATCH_OFFSET_BIAS | PIN_OFFSET_BIAS;
|
||||
if (!drm_mm_node_allocated(&vma->node)) {
|
||||
if (entry->flags & __EXEC_OBJECT_NEEDS_MAP)
|
||||
flags |= PIN_GLOBAL | PIN_MAPPABLE;
|
||||
if (entry->flags & EXEC_OBJECT_NEEDS_GTT)
|
||||
flags |= PIN_GLOBAL;
|
||||
if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS)
|
||||
flags |= BATCH_OFFSET_BIAS | PIN_OFFSET_BIAS;
|
||||
}
|
||||
|
||||
ret = i915_gem_object_pin(obj, vma->vm, entry->alignment, flags);
|
||||
if ((ret == -ENOSPC || ret == -E2BIG) &&
|
||||
only_mappable_for_reloc(entry->flags))
|
||||
ret = i915_gem_object_pin(obj, vma->vm,
|
||||
entry->alignment,
|
||||
flags & ~(PIN_GLOBAL | PIN_MAPPABLE));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -605,13 +668,14 @@ eb_vma_misplaced(struct i915_vma *vma)
|
||||
vma->node.start & (entry->alignment - 1))
|
||||
return true;
|
||||
|
||||
if (entry->flags & __EXEC_OBJECT_NEEDS_MAP && !obj->map_and_fenceable)
|
||||
return true;
|
||||
|
||||
if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS &&
|
||||
vma->node.start < BATCH_OFFSET_BIAS)
|
||||
return true;
|
||||
|
||||
/* avoid costly ping-pong once a batch bo ended up non-mappable */
|
||||
if (entry->flags & __EXEC_OBJECT_NEEDS_MAP && !obj->map_and_fenceable)
|
||||
return !only_mappable_for_reloc(entry->flags);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -971,7 +1035,7 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas,
|
||||
obj->dirty = 1;
|
||||
i915_gem_request_assign(&obj->last_write_req, req);
|
||||
|
||||
intel_fb_obj_invalidate(obj, ring);
|
||||
intel_fb_obj_invalidate(obj, ring, ORIGIN_CS);
|
||||
|
||||
/* update for the implicit flush after a batch */
|
||||
obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
|
||||
@ -1076,16 +1140,15 @@ i915_gem_execbuffer_parse(struct intel_engine_cs *ring,
|
||||
struct drm_i915_gem_object *batch_obj,
|
||||
u32 batch_start_offset,
|
||||
u32 batch_len,
|
||||
bool is_master,
|
||||
u32 *flags)
|
||||
bool is_master)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(batch_obj->base.dev);
|
||||
struct drm_i915_gem_object *shadow_batch_obj;
|
||||
bool need_reloc = false;
|
||||
struct i915_vma *vma;
|
||||
int ret;
|
||||
|
||||
shadow_batch_obj = i915_gem_batch_pool_get(&dev_priv->mm.batch_pool,
|
||||
batch_obj->base.size);
|
||||
PAGE_ALIGN(batch_len));
|
||||
if (IS_ERR(shadow_batch_obj))
|
||||
return shadow_batch_obj;
|
||||
|
||||
@ -1095,40 +1158,30 @@ i915_gem_execbuffer_parse(struct intel_engine_cs *ring,
|
||||
batch_start_offset,
|
||||
batch_len,
|
||||
is_master);
|
||||
if (ret) {
|
||||
if (ret == -EACCES)
|
||||
return batch_obj;
|
||||
} else {
|
||||
struct i915_vma *vma;
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
memset(shadow_exec_entry, 0, sizeof(*shadow_exec_entry));
|
||||
ret = i915_gem_obj_ggtt_pin(shadow_batch_obj, 0, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
vma = i915_gem_obj_to_ggtt(shadow_batch_obj);
|
||||
vma->exec_entry = shadow_exec_entry;
|
||||
vma->exec_entry->flags = __EXEC_OBJECT_PURGEABLE;
|
||||
drm_gem_object_reference(&shadow_batch_obj->base);
|
||||
i915_gem_execbuffer_reserve_vma(vma, ring, &need_reloc);
|
||||
list_add_tail(&vma->exec_list, &eb->vmas);
|
||||
memset(shadow_exec_entry, 0, sizeof(*shadow_exec_entry));
|
||||
|
||||
shadow_batch_obj->base.pending_read_domains =
|
||||
batch_obj->base.pending_read_domains;
|
||||
vma = i915_gem_obj_to_ggtt(shadow_batch_obj);
|
||||
vma->exec_entry = shadow_exec_entry;
|
||||
vma->exec_entry->flags = __EXEC_OBJECT_PURGEABLE | __EXEC_OBJECT_HAS_PIN;
|
||||
drm_gem_object_reference(&shadow_batch_obj->base);
|
||||
list_add_tail(&vma->exec_list, &eb->vmas);
|
||||
|
||||
/*
|
||||
* Set the DISPATCH_SECURE bit to remove the NON_SECURE
|
||||
* bit from MI_BATCH_BUFFER_START commands issued in the
|
||||
* dispatch_execbuffer implementations. We specifically
|
||||
* don't want that set when the command parser is
|
||||
* enabled.
|
||||
*
|
||||
* FIXME: with aliasing ppgtt, buffers that should only
|
||||
* be in ggtt still end up in the aliasing ppgtt. remove
|
||||
* this check when that is fixed.
|
||||
*/
|
||||
if (USES_FULL_PPGTT(dev))
|
||||
*flags |= I915_DISPATCH_SECURE;
|
||||
}
|
||||
shadow_batch_obj->base.pending_read_domains = I915_GEM_DOMAIN_COMMAND;
|
||||
|
||||
return ret ? ERR_PTR(ret) : shadow_batch_obj;
|
||||
return shadow_batch_obj;
|
||||
|
||||
err:
|
||||
if (ret == -EACCES) /* unhandled chained batch */
|
||||
return batch_obj;
|
||||
else
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
int
|
||||
@ -1138,7 +1191,7 @@ i915_gem_ringbuffer_submission(struct drm_device *dev, struct drm_file *file,
|
||||
struct drm_i915_gem_execbuffer2 *args,
|
||||
struct list_head *vmas,
|
||||
struct drm_i915_gem_object *batch_obj,
|
||||
u64 exec_start, u32 flags)
|
||||
u64 exec_start, u32 dispatch_flags)
|
||||
{
|
||||
struct drm_clip_rect *cliprects = NULL;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
@ -1198,6 +1251,13 @@ i915_gem_ringbuffer_submission(struct drm_device *dev, struct drm_file *file,
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
if (ctx->ppgtt)
|
||||
WARN(ctx->ppgtt->pd_dirty_rings & (1<<ring->id),
|
||||
"%s didn't clear reload\n", ring->name);
|
||||
else if (dev_priv->mm.aliasing_ppgtt)
|
||||
WARN(dev_priv->mm.aliasing_ppgtt->pd_dirty_rings &
|
||||
(1<<ring->id), "%s didn't clear reload\n", ring->name);
|
||||
|
||||
instp_mode = args->flags & I915_EXEC_CONSTANTS_MASK;
|
||||
instp_mask = I915_EXEC_CONSTANTS_MASK;
|
||||
switch (instp_mode) {
|
||||
@ -1266,19 +1326,19 @@ i915_gem_ringbuffer_submission(struct drm_device *dev, struct drm_file *file,
|
||||
|
||||
ret = ring->dispatch_execbuffer(ring,
|
||||
exec_start, exec_len,
|
||||
flags);
|
||||
dispatch_flags);
|
||||
if (ret)
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
ret = ring->dispatch_execbuffer(ring,
|
||||
exec_start, exec_len,
|
||||
flags);
|
||||
dispatch_flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
trace_i915_gem_ring_dispatch(intel_ring_get_request(ring), flags);
|
||||
trace_i915_gem_ring_dispatch(intel_ring_get_request(ring), dispatch_flags);
|
||||
|
||||
i915_gem_execbuffer_move_to_active(vmas, ring);
|
||||
i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj);
|
||||
@ -1353,7 +1413,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
struct i915_address_space *vm;
|
||||
const u32 ctx_id = i915_execbuffer2_get_context_id(*args);
|
||||
u64 exec_start = args->batch_start_offset;
|
||||
u32 flags;
|
||||
u32 dispatch_flags;
|
||||
int ret;
|
||||
bool need_relocs;
|
||||
|
||||
@ -1364,15 +1424,15 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
flags = 0;
|
||||
dispatch_flags = 0;
|
||||
if (args->flags & I915_EXEC_SECURE) {
|
||||
if (!file->is_master || !capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
flags |= I915_DISPATCH_SECURE;
|
||||
dispatch_flags |= I915_DISPATCH_SECURE;
|
||||
}
|
||||
if (args->flags & I915_EXEC_IS_PINNED)
|
||||
flags |= I915_DISPATCH_PINNED;
|
||||
dispatch_flags |= I915_DISPATCH_PINNED;
|
||||
|
||||
if ((args->flags & I915_EXEC_RING_MASK) > LAST_USER_RING) {
|
||||
DRM_DEBUG("execbuf with unknown ring: %d\n",
|
||||
@ -1494,12 +1554,27 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
batch_obj,
|
||||
args->batch_start_offset,
|
||||
args->batch_len,
|
||||
file->is_master,
|
||||
&flags);
|
||||
file->is_master);
|
||||
if (IS_ERR(batch_obj)) {
|
||||
ret = PTR_ERR(batch_obj);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the DISPATCH_SECURE bit to remove the NON_SECURE
|
||||
* bit from MI_BATCH_BUFFER_START commands issued in the
|
||||
* dispatch_execbuffer implementations. We specifically
|
||||
* don't want that set when the command parser is
|
||||
* enabled.
|
||||
*
|
||||
* FIXME: with aliasing ppgtt, buffers that should only
|
||||
* be in ggtt still end up in the aliasing ppgtt. remove
|
||||
* this check when that is fixed.
|
||||
*/
|
||||
if (USES_FULL_PPGTT(dev))
|
||||
dispatch_flags |= I915_DISPATCH_SECURE;
|
||||
|
||||
exec_start = 0;
|
||||
}
|
||||
|
||||
batch_obj->base.pending_read_domains |= I915_GEM_DOMAIN_COMMAND;
|
||||
@ -1507,14 +1582,14 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
/* snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
|
||||
* batch" bit. Hence we need to pin secure batches into the global gtt.
|
||||
* hsw should have this fixed, but bdw mucks it up again. */
|
||||
if (flags & I915_DISPATCH_SECURE) {
|
||||
if (dispatch_flags & I915_DISPATCH_SECURE) {
|
||||
/*
|
||||
* So on first glance it looks freaky that we pin the batch here
|
||||
* outside of the reservation loop. But:
|
||||
* - The batch is already pinned into the relevant ppgtt, so we
|
||||
* already have the backing storage fully allocated.
|
||||
* - No other BO uses the global gtt (well contexts, but meh),
|
||||
* so we don't really have issues with mutliple objects not
|
||||
* so we don't really have issues with multiple objects not
|
||||
* fitting due to fragmentation.
|
||||
* So this is actually safe.
|
||||
*/
|
||||
@ -1527,7 +1602,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
exec_start += i915_gem_obj_offset(batch_obj, vm);
|
||||
|
||||
ret = dev_priv->gt.do_execbuf(dev, file, ring, ctx, args,
|
||||
&eb->vmas, batch_obj, exec_start, flags);
|
||||
&eb->vmas, batch_obj, exec_start,
|
||||
dispatch_flags);
|
||||
|
||||
/*
|
||||
* FIXME: We crucially rely upon the active tracking for the (ppgtt)
|
||||
@ -1535,7 +1611,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
* needs to be adjusted to also track the ggtt batch vma properly as
|
||||
* active.
|
||||
*/
|
||||
if (flags & I915_DISPATCH_SECURE)
|
||||
if (dispatch_flags & I915_DISPATCH_SECURE)
|
||||
i915_gem_object_ggtt_unpin(batch_obj);
|
||||
err:
|
||||
/* the request owns the ref now */
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user