Re: [PATCH] drm: remove drm_bridge->dev

2019-12-10 Thread Thomas Zimmermann
Hi

Am 10.12.19 um 16:11 schrieb Mihail Atanassov:
> As suggested in [1], the 'dev' field is a bit repetitive, since it 1:1
> follows the setting and NULLing of the 'encoder' field. Therefore, use
> drm_bridge->encoder->dev in place of drm_bridge->dev.
> 
> [1] https://patchwork.freedesktop.org/patch/343824/
> 
> Cc: Daniel Vetter 
> Suggested-by: Thomas Zimmermann 
> Signed-off-by: Mihail Atanassov 

Do you need help with merging the patch?

Best regards
Thomas

> ---
>  drivers/gpu/drm/bridge/adv7511/adv7511_drv.c   |  2 +-
>  drivers/gpu/drm/bridge/analogix/analogix-anx6345.c |  2 +-
>  drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c |  2 +-
>  drivers/gpu/drm/bridge/cdns-dsi.c  |  2 +-
>  drivers/gpu/drm/bridge/dumb-vga-dac.c  |  2 +-
>  .../gpu/drm/bridge/megachips-stdp-ge-b850v3-fw.c   |  2 +-
>  drivers/gpu/drm/bridge/nxp-ptn3460.c   |  2 +-
>  drivers/gpu/drm/bridge/panel.c |  2 +-
>  drivers/gpu/drm/bridge/parade-ps8622.c |  2 +-
>  drivers/gpu/drm/bridge/sii902x.c   |  6 +++---
>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c  |  6 +++---
>  drivers/gpu/drm/bridge/tc358764.c  |  4 ++--
>  drivers/gpu/drm/bridge/tc358767.c  |  6 +++---
>  drivers/gpu/drm/bridge/ti-sn65dsi86.c  |  2 +-
>  drivers/gpu/drm/bridge/ti-tfp410.c |  6 +++---
>  drivers/gpu/drm/drm_bridge.c   | 10 --
>  drivers/gpu/drm/i2c/tda998x_drv.c  |  2 +-
>  drivers/gpu/drm/mcde/mcde_dsi.c|  2 +-
>  drivers/gpu/drm/msm/edp/edp_bridge.c   |  2 +-
>  drivers/gpu/drm/msm/hdmi/hdmi_bridge.c |  4 ++--
>  drivers/gpu/drm/rcar-du/rcar_lvds.c|  3 ++-
>  include/drm/drm_bridge.h   |  2 --
>  22 files changed, 35 insertions(+), 38 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c 
> b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
> index 9e13e466e72c..009cf1fef8d4 100644
> --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
> +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
> @@ -863,7 +863,7 @@ static int adv7511_bridge_attach(struct drm_bridge 
> *bridge)
>   adv->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
>   DRM_CONNECTOR_POLL_DISCONNECT;
>  
> - ret = drm_connector_init(bridge->dev, >connector,
> + ret = drm_connector_init(bridge->encoder->dev, >connector,
>_connector_funcs,
>DRM_MODE_CONNECTOR_HDMIA);
>   if (ret) {
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c 
> b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
> index 9917ce0d86a0..5b806d23fcb3 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
> @@ -541,7 +541,7 @@ static int anx6345_bridge_attach(struct drm_bridge 
> *bridge)
>   return err;
>   }
>  
> - err = drm_connector_init(bridge->dev, >connector,
> + err = drm_connector_init(bridge->encoder->dev, >connector,
>_connector_funcs,
>DRM_MODE_CONNECTOR_eDP);
>   if (err) {
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c 
> b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c
> index 41867be03751..7463537950cb 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c
> @@ -908,7 +908,7 @@ static int anx78xx_bridge_attach(struct drm_bridge 
> *bridge)
>   return err;
>   }
>  
> - err = drm_connector_init(bridge->dev, >connector,
> + err = drm_connector_init(bridge->encoder->dev, >connector,
>_connector_funcs,
>DRM_MODE_CONNECTOR_DisplayPort);
>   if (err) {
> diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c 
> b/drivers/gpu/drm/bridge/cdns-dsi.c
> index 3a5bd4e7fd1e..32863e3ad537 100644
> --- a/drivers/gpu/drm/bridge/cdns-dsi.c
> +++ b/drivers/gpu/drm/bridge/cdns-dsi.c
> @@ -651,7 +651,7 @@ static int cdns_dsi_bridge_attach(struct drm_bridge 
> *bridge)
>   struct cdns_dsi *dsi = input_to_dsi(input);
>   struct cdns_dsi_output *output = >output;
>  
> - if (!drm_core_check_feature(bridge->dev, DRIVER_ATOMIC)) {
> + if (!drm_core_check_feature(bridge->encoder->dev, DRIVER_ATOMIC)) {
>   dev_err(dsi->base.dev,
>   "cdns-dsi driver is only compatible with DRM devices 
> supporting atomic updates");
>   return -ENOTSUPP;
> diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c 
> b/drivers/gpu/drm/bridge/dumb-vga-dac.c
> index cc33dc411b9e..67ad6cecf68d 100644
> --- a/drivers/gpu/drm/bridge/dumb-vga-dac.c
> 

Re: [Intel-gfx] [PATCH] drm/i915/display: cleanup intel_bw_state on i915 module removal

2019-12-10 Thread Bharadiya,Pankaj
On Tue, Dec 10, 2019 at 09:57:39PM -0800, Lucas De Marchi wrote:
> On Mon, Dec 09, 2019 at 08:09:02PM +0530, Pankaj Bharadiya wrote:
> >intel_bw_state allocated memory is not getting freed even after
> >module removal.
> >
> >kmemleak reported backtrace:
> >
> >   [<79019739>] kmemdup+0x17/0x40
> >   [] intel_bw_duplicate_state+0x1b/0x40 [i915]
> >   [<7423ed0c>] drm_atomic_get_private_obj_state+0xca/0x140
> >   [<100e3533>] intel_bw_atomic_check+0x133/0x350 [i915]
> >   [<126d0e0c>] intel_atomic_check+0x1ab7/0x20d0 [i915]
> >   [] drm_atomic_check_only+0x563/0x810
> >   [] drm_atomic_commit+0xe/0x50
> >   [] drm_atomic_helper_disable_all+0x133/0x160
> >   [<3c44760c>] drm_atomic_helper_shutdown+0x65/0xc0
> >   [<414e3e5c>] i915_driver_remove+0xcb/0x130 [i915]
> >   [] i915_pci_remove+0x19/0x40 [i915]
> >   [<2dcbd148>] pci_device_remove+0x36/0xb0
> >   [<3c8c6b0a>] device_release_driver_internal+0xe0/0x1c0
> >   [<580e9566>] unbind_store+0xc3/0x120
> >   [<869d0df5>] kernfs_fop_write+0x104/0x190
> >   [<4dc1a355>] vfs_write+0xb9/0x1d0
> 
> what I find strange in this is that the last state was allocated by the
> "driver remove" code path.
> 
> >
> >Call the drm_atomic_private_obj_fini(), which inturn calls the
> >intel_bw_destroy_state() to make sure the intel_bw_state memory is
> >freed properly.
> >
> >Signed-off-by: Pankaj Bharadiya 
> >---
> >drivers/gpu/drm/i915/display/intel_bw.c  | 5 +
> >drivers/gpu/drm/i915/display/intel_bw.h  | 1 +
> >drivers/gpu/drm/i915/display/intel_display.c | 2 ++
> >3 files changed, 8 insertions(+)
> >
> >diff --git a/drivers/gpu/drm/i915/display/intel_bw.c 
> >b/drivers/gpu/drm/i915/display/intel_bw.c
> >index dcb66a33be9b..b228671d5a5d 100644
> >--- a/drivers/gpu/drm/i915/display/intel_bw.c
> >+++ b/drivers/gpu/drm/i915/display/intel_bw.c
> >@@ -486,3 +486,8 @@ int intel_bw_init(struct drm_i915_private *dev_priv)
> >
> > return 0;
> >}
> >+
> >+void intel_bw_cleanup(struct drm_i915_private *dev_priv)
> >+{
> >+drm_atomic_private_obj_fini(_priv->bw_obj);
> >+}
> >diff --git a/drivers/gpu/drm/i915/display/intel_bw.h 
> >b/drivers/gpu/drm/i915/display/intel_bw.h
> >index 9db10af012f4..20b9ad241802 100644
> >--- a/drivers/gpu/drm/i915/display/intel_bw.h
> >+++ b/drivers/gpu/drm/i915/display/intel_bw.h
> >@@ -25,6 +25,7 @@ struct intel_bw_state {
> >
> >void intel_bw_init_hw(struct drm_i915_private *dev_priv);
> >int intel_bw_init(struct drm_i915_private *dev_priv);
> >+void intel_bw_cleanup(struct drm_i915_private *dev_priv);
> >int intel_bw_atomic_check(struct intel_atomic_state *state);
> >void intel_bw_crtc_update(struct intel_bw_state *bw_state,
> >   const struct intel_crtc_state *crtc_state);
> >diff --git a/drivers/gpu/drm/i915/display/intel_display.c 
> >b/drivers/gpu/drm/i915/display/intel_display.c
> >index 3190aa27ffdc..756eb90b1bb1 100644
> >--- a/drivers/gpu/drm/i915/display/intel_display.c
> >+++ b/drivers/gpu/drm/i915/display/intel_display.c
> >@@ -17912,6 +17912,8 @@ void intel_modeset_driver_remove(struct 
> >drm_i915_private *i915)
> >
> > intel_gmbus_teardown(i915);
> >
> >+intel_bw_cleanup(i915);
> 
> This doesn't seem to match the (reverse) order of
> intel_modeset_init()... but it's actually the gmbus_teardown() that is
> out of place. Did you check if it's not a wrong shutdown ordering?
>

In intel_modeset_init(), intel_gmbus_setup() happens after
intel_bw_init().
I think the patch follows the reverse ordering properly.
Am I missing anything?

Thanks,
Pankaj

> thanks
> Lucas De Marchi
> 
> >+
> > destroy_workqueue(i915->flip_wq);
> > destroy_workqueue(i915->modeset_wq);
> >
> >-- 
> >2.23.0
> >
> >___
> >Intel-gfx mailing list
> >intel-...@lists.freedesktop.org
> >https://lists.freedesktop.org/mailman/listinfo/intel-gfx
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [Intel-gfx] [PATCH] drm/i915/display: cleanup intel_bw_state on i915 module removal

2019-12-10 Thread Lucas De Marchi

On Mon, Dec 09, 2019 at 08:09:02PM +0530, Pankaj Bharadiya wrote:

intel_bw_state allocated memory is not getting freed even after
module removal.

kmemleak reported backtrace:

   [<79019739>] kmemdup+0x17/0x40
   [] intel_bw_duplicate_state+0x1b/0x40 [i915]
   [<7423ed0c>] drm_atomic_get_private_obj_state+0xca/0x140
   [<100e3533>] intel_bw_atomic_check+0x133/0x350 [i915]
   [<126d0e0c>] intel_atomic_check+0x1ab7/0x20d0 [i915]
   [] drm_atomic_check_only+0x563/0x810
   [] drm_atomic_commit+0xe/0x50
   [] drm_atomic_helper_disable_all+0x133/0x160
   [<3c44760c>] drm_atomic_helper_shutdown+0x65/0xc0
   [<414e3e5c>] i915_driver_remove+0xcb/0x130 [i915]
   [] i915_pci_remove+0x19/0x40 [i915]
   [<2dcbd148>] pci_device_remove+0x36/0xb0
   [<3c8c6b0a>] device_release_driver_internal+0xe0/0x1c0
   [<580e9566>] unbind_store+0xc3/0x120
   [<869d0df5>] kernfs_fop_write+0x104/0x190
   [<4dc1a355>] vfs_write+0xb9/0x1d0


what I find strange in this is that the last state was allocated by the
"driver remove" code path.



Call the drm_atomic_private_obj_fini(), which inturn calls the
intel_bw_destroy_state() to make sure the intel_bw_state memory is
freed properly.

Signed-off-by: Pankaj Bharadiya 
---
drivers/gpu/drm/i915/display/intel_bw.c  | 5 +
drivers/gpu/drm/i915/display/intel_bw.h  | 1 +
drivers/gpu/drm/i915/display/intel_display.c | 2 ++
3 files changed, 8 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/intel_bw.c 
b/drivers/gpu/drm/i915/display/intel_bw.c
index dcb66a33be9b..b228671d5a5d 100644
--- a/drivers/gpu/drm/i915/display/intel_bw.c
+++ b/drivers/gpu/drm/i915/display/intel_bw.c
@@ -486,3 +486,8 @@ int intel_bw_init(struct drm_i915_private *dev_priv)

return 0;
}
+
+void intel_bw_cleanup(struct drm_i915_private *dev_priv)
+{
+   drm_atomic_private_obj_fini(_priv->bw_obj);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_bw.h 
b/drivers/gpu/drm/i915/display/intel_bw.h
index 9db10af012f4..20b9ad241802 100644
--- a/drivers/gpu/drm/i915/display/intel_bw.h
+++ b/drivers/gpu/drm/i915/display/intel_bw.h
@@ -25,6 +25,7 @@ struct intel_bw_state {

void intel_bw_init_hw(struct drm_i915_private *dev_priv);
int intel_bw_init(struct drm_i915_private *dev_priv);
+void intel_bw_cleanup(struct drm_i915_private *dev_priv);
int intel_bw_atomic_check(struct intel_atomic_state *state);
void intel_bw_crtc_update(struct intel_bw_state *bw_state,
  const struct intel_crtc_state *crtc_state);
diff --git a/drivers/gpu/drm/i915/display/intel_display.c 
b/drivers/gpu/drm/i915/display/intel_display.c
index 3190aa27ffdc..756eb90b1bb1 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -17912,6 +17912,8 @@ void intel_modeset_driver_remove(struct 
drm_i915_private *i915)

intel_gmbus_teardown(i915);

+   intel_bw_cleanup(i915);


This doesn't seem to match the (reverse) order of
intel_modeset_init()... but it's actually the gmbus_teardown() that is
out of place. Did you check if it's not a wrong shutdown ordering?

thanks
Lucas De Marchi


+
destroy_workqueue(i915->flip_wq);
destroy_workqueue(i915->modeset_wq);

--
2.23.0

___
Intel-gfx mailing list
intel-...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 05/25] goldish_pipe: rename local pin_user_pages() routine

2019-12-10 Thread John Hubbard
1. Avoid naming conflicts: rename local static function from
"pin_user_pages()" to "goldfish_pin_pages()".

An upcoming patch will introduce a global pin_user_pages()
function.

Reviewed-by: Jan Kara 
Reviewed-by: Jérôme Glisse 
Reviewed-by: Ira Weiny 
Signed-off-by: John Hubbard 
---
 drivers/platform/goldfish/goldfish_pipe.c | 18 +-
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/platform/goldfish/goldfish_pipe.c 
b/drivers/platform/goldfish/goldfish_pipe.c
index cef0133aa47a..ef50c264db71 100644
--- a/drivers/platform/goldfish/goldfish_pipe.c
+++ b/drivers/platform/goldfish/goldfish_pipe.c
@@ -257,12 +257,12 @@ static int goldfish_pipe_error_convert(int status)
}
 }
 
-static int pin_user_pages(unsigned long first_page,
- unsigned long last_page,
- unsigned int last_page_size,
- int is_write,
- struct page *pages[MAX_BUFFERS_PER_COMMAND],
- unsigned int *iter_last_page_size)
+static int goldfish_pin_pages(unsigned long first_page,
+ unsigned long last_page,
+ unsigned int last_page_size,
+ int is_write,
+ struct page *pages[MAX_BUFFERS_PER_COMMAND],
+ unsigned int *iter_last_page_size)
 {
int ret;
int requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1;
@@ -354,9 +354,9 @@ static int transfer_max_buffers(struct goldfish_pipe *pipe,
if (mutex_lock_interruptible(>lock))
return -ERESTARTSYS;
 
-   pages_count = pin_user_pages(first_page, last_page,
-last_page_size, is_write,
-pipe->pages, _last_page_size);
+   pages_count = goldfish_pin_pages(first_page, last_page,
+last_page_size, is_write,
+pipe->pages, _last_page_size);
if (pages_count < 0) {
mutex_unlock(>lock);
return pages_count;
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 23/25] mm/gup: track FOLL_PIN pages

2019-12-10 Thread John Hubbard
Add tracking of pages that were pinned via FOLL_PIN.

As mentioned in the FOLL_PIN documentation, callers who effectively set
FOLL_PIN are required to ultimately free such pages via unpin_user_page().
The effect is similar to FOLL_GET, and may be thought of as "FOLL_GET
for DIO and/or RDMA use".

Pages that have been pinned via FOLL_PIN are identifiable via a
new function call:

   bool page_dma_pinned(struct page *page);

What to do in response to encountering such a page, is left to later
patchsets. There is discussion about this in [1], [2], and [3].

This also changes a BUG_ON(), to a WARN_ON(), in follow_page_mask().

[1] Some slow progress on get_user_pages() (Apr 2, 2019):
https://lwn.net/Articles/784574/
[2] DMA and get_user_pages() (LPC: Dec 12, 2018):
https://lwn.net/Articles/774411/
[3] The trouble with get_user_pages() (Apr 30, 2018):
https://lwn.net/Articles/753027/

Suggested-by: Jan Kara 
Suggested-by: Jérôme Glisse 
Reviewed-by: Jan Kara 
Reviewed-by: Jérôme Glisse 
Reviewed-by: Ira Weiny 
Cc: Kirill A. Shutemov 
Signed-off-by: John Hubbard 
---
 Documentation/core-api/pin_user_pages.rst |   2 +-
 include/linux/mm.h|  77 -
 include/linux/mmzone.h|   2 +
 include/linux/page_ref.h  |  10 +
 mm/gup.c  | 369 --
 mm/huge_memory.c  |  26 +-
 mm/hugetlb.c  |  25 +-
 mm/vmstat.c   |   2 +
 8 files changed, 397 insertions(+), 116 deletions(-)

diff --git a/Documentation/core-api/pin_user_pages.rst 
b/Documentation/core-api/pin_user_pages.rst
index fd2a19c96189..2640d122de29 100644
--- a/Documentation/core-api/pin_user_pages.rst
+++ b/Documentation/core-api/pin_user_pages.rst
@@ -53,7 +53,7 @@ Which flags are set by each wrapper
 For these pin_user_pages*() functions, FOLL_PIN is OR'd in with whatever gup
 flags the caller provides. The caller is required to pass in a non-null struct
 pages* array, and the function then pin pages by incrementing each by a special
-value. For now, that value is +1, just like get_user_pages*().::
+value: GUP_PIN_COUNTING_BIAS.::
 
  Function
  
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 6a1a357e7d86..1765332f27e8 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1005,6 +1005,10 @@ static inline bool is_pci_p2pdma_page(const struct page 
*page)
 #define page_ref_zero_or_close_to_overflow(page) \
((unsigned int) page_ref_count(page) + 127u <= 127u)
 
+#define page_ref_zero_or_close_to_bias_overflow(page) \
+   ((unsigned int) page_ref_count(page) + \
+   GUP_PIN_COUNTING_BIAS <= GUP_PIN_COUNTING_BIAS)
+
 static inline void get_page(struct page *page)
 {
page = compound_head(page);
@@ -1016,6 +1020,8 @@ static inline void get_page(struct page *page)
page_ref_inc(page);
 }
 
+bool __must_check try_grab_page(struct page *page, unsigned int flags);
+
 static inline __must_check bool try_get_page(struct page *page)
 {
page = compound_head(page);
@@ -1044,29 +1050,70 @@ static inline void put_page(struct page *page)
__put_page(page);
 }
 
-/**
- * unpin_user_page() - release a gup-pinned page
- * @page:pointer to page to be released
+/*
+ * GUP_PIN_COUNTING_BIAS, and the associated functions that use it, overload
+ * the page's refcount so that two separate items are tracked: the original 
page
+ * reference count, and also a new count of how many pin_user_pages() calls 
were
+ * made against the page. ("gup-pinned" is another term for the latter).
+ *
+ * With this scheme, pin_user_pages() becomes special: such pages are marked as
+ * distinct from normal pages. As such, the unpin_user_page() call (and its
+ * variants) must be used in order to release gup-pinned pages.
  *
- * Pages that were pinned via pin_user_pages*() must be released via either
- * unpin_user_page(), or one of the unpin_user_pages*() routines. This is so
- * that eventually such pages can be separately tracked and uniquely handled. 
In
- * particular, interactions with RDMA and filesystems need special handling.
+ * Choice of value:
  *
- * unpin_user_page() and put_page() are not interchangeable, despite this early
- * implementation that makes them look the same. unpin_user_page() calls must
- * be perfectly matched up with pin*() calls.
+ * By making GUP_PIN_COUNTING_BIAS a power of two, debugging of page reference
+ * counts with respect to pin_user_pages() and unpin_user_page() becomes
+ * simpler, due to the fact that adding an even power of two to the page
+ * refcount has the effect of using only the upper N bits, for the code that
+ * counts up using the bias value. This means that the lower bits are left for
+ * the exclusive use of the original code that increments and decrements by one
+ * (or at least, by much smaller values than the bias value).
+ *
+ * Of course, once the 

[PATCH v9 20/25] powerpc: book3s64: convert to pin_user_pages() and put_user_page()

2019-12-10 Thread John Hubbard
1. Convert from get_user_pages() to pin_user_pages().

2. As required by pin_user_pages(), release these pages via
put_user_page().

Cc: Jan Kara 
Signed-off-by: John Hubbard 
---
 arch/powerpc/mm/book3s64/iommu_api.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/mm/book3s64/iommu_api.c 
b/arch/powerpc/mm/book3s64/iommu_api.c
index 56cc84520577..a86547822034 100644
--- a/arch/powerpc/mm/book3s64/iommu_api.c
+++ b/arch/powerpc/mm/book3s64/iommu_api.c
@@ -103,7 +103,7 @@ static long mm_iommu_do_alloc(struct mm_struct *mm, 
unsigned long ua,
for (entry = 0; entry < entries; entry += chunk) {
unsigned long n = min(entries - entry, chunk);
 
-   ret = get_user_pages(ua + (entry << PAGE_SHIFT), n,
+   ret = pin_user_pages(ua + (entry << PAGE_SHIFT), n,
FOLL_WRITE | FOLL_LONGTERM,
mem->hpages + entry, NULL);
if (ret == n) {
@@ -167,9 +167,8 @@ static long mm_iommu_do_alloc(struct mm_struct *mm, 
unsigned long ua,
return 0;
 
 free_exit:
-   /* free the reference taken */
-   for (i = 0; i < pinned; i++)
-   put_page(mem->hpages[i]);
+   /* free the references taken */
+   put_user_pages(mem->hpages, pinned);
 
vfree(mem->hpas);
kfree(mem);
@@ -215,7 +214,8 @@ static void mm_iommu_unpin(struct 
mm_iommu_table_group_mem_t *mem)
if (mem->hpas[i] & MM_IOMMU_TABLE_GROUP_PAGE_DIRTY)
SetPageDirty(page);
 
-   put_page(page);
+   put_user_page(page);
+
mem->hpas[i] = 0;
}
 }
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 14/25] drm/via: set FOLL_PIN via pin_user_pages_fast()

2019-12-10 Thread John Hubbard
Convert drm/via to use the new pin_user_pages_fast() call, which sets
FOLL_PIN. Setting FOLL_PIN is now required for code that requires
tracking of pinned pages, and therefore for any code that calls
put_user_page().

In partial anticipation of this work, the drm/via driver was already
calling put_user_page() instead of put_page(). Therefore, in order to
convert from the get_user_pages()/put_page() model, to the
pin_user_pages()/put_user_page() model, the only change required
is to change get_user_pages() to pin_user_pages().

Acked-by: Daniel Vetter 
Reviewed-by: Jérôme Glisse 
Reviewed-by: Ira Weiny 
Signed-off-by: John Hubbard 
---
 drivers/gpu/drm/via/via_dmablit.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/via/via_dmablit.c 
b/drivers/gpu/drm/via/via_dmablit.c
index 3db000aacd26..37c5e572993a 100644
--- a/drivers/gpu/drm/via/via_dmablit.c
+++ b/drivers/gpu/drm/via/via_dmablit.c
@@ -239,7 +239,7 @@ via_lock_all_dma_pages(drm_via_sg_info_t *vsg,  
drm_via_dmablit_t *xfer)
vsg->pages = vzalloc(array_size(sizeof(struct page *), vsg->num_pages));
if (NULL == vsg->pages)
return -ENOMEM;
-   ret = get_user_pages_fast((unsigned long)xfer->mem_addr,
+   ret = pin_user_pages_fast((unsigned long)xfer->mem_addr,
vsg->num_pages,
vsg->direction == DMA_FROM_DEVICE ? FOLL_WRITE : 0,
vsg->pages);
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 12/25] IB/{core, hw, umem}: set FOLL_PIN via pin_user_pages*(), fix up ODP

2019-12-10 Thread John Hubbard
Convert infiniband to use the new pin_user_pages*() calls.

Also, revert earlier changes to Infiniband ODP that had it using
put_user_page(). ODP is "Case 3" in
Documentation/core-api/pin_user_pages.rst, which is to say, normal
get_user_pages() and put_page() is the API to use there.

The new pin_user_pages*() calls replace corresponding get_user_pages*()
calls, and set the FOLL_PIN flag. The FOLL_PIN flag requires that the
caller must return the pages via put_user_page*() calls, but infiniband
was already doing that as part of an earlier commit.

Reviewed-by: Jason Gunthorpe 
Signed-off-by: John Hubbard 
---
 drivers/infiniband/core/umem.c  |  2 +-
 drivers/infiniband/core/umem_odp.c  | 13 ++---
 drivers/infiniband/hw/hfi1/user_pages.c |  2 +-
 drivers/infiniband/hw/mthca/mthca_memfree.c |  2 +-
 drivers/infiniband/hw/qib/qib_user_pages.c  |  2 +-
 drivers/infiniband/hw/qib/qib_user_sdma.c   |  2 +-
 drivers/infiniband/hw/usnic/usnic_uiom.c|  2 +-
 drivers/infiniband/sw/siw/siw_mem.c |  2 +-
 8 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index 214e87aa609d..55daefaa9b88 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -266,7 +266,7 @@ struct ib_umem *ib_umem_get(struct ib_udata *udata, 
unsigned long addr,
sg = umem->sg_head.sgl;
 
while (npages) {
-   ret = get_user_pages_fast(cur_base,
+   ret = pin_user_pages_fast(cur_base,
  min_t(unsigned long, npages,
PAGE_SIZE /
sizeof(struct page *)),
diff --git a/drivers/infiniband/core/umem_odp.c 
b/drivers/infiniband/core/umem_odp.c
index e42d44e501fd..abc3bb6578cc 100644
--- a/drivers/infiniband/core/umem_odp.c
+++ b/drivers/infiniband/core/umem_odp.c
@@ -308,9 +308,8 @@ EXPORT_SYMBOL(ib_umem_odp_release);
  * The function returns -EFAULT if the DMA mapping operation fails. It returns
  * -EAGAIN if a concurrent invalidation prevents us from updating the page.
  *
- * The page is released via put_user_page even if the operation failed. For
- * on-demand pinning, the page is released whenever it isn't stored in the
- * umem.
+ * The page is released via put_page even if the operation failed. For 
on-demand
+ * pinning, the page is released whenever it isn't stored in the umem.
  */
 static int ib_umem_odp_map_dma_single_page(
struct ib_umem_odp *umem_odp,
@@ -363,7 +362,7 @@ static int ib_umem_odp_map_dma_single_page(
}
 
 out:
-   put_user_page(page);
+   put_page(page);
return ret;
 }
 
@@ -473,7 +472,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem_odp *umem_odp, 
u64 user_virt,
ret = -EFAULT;
break;
}
-   put_user_page(local_page_list[j]);
+   put_page(local_page_list[j]);
continue;
}
 
@@ -500,8 +499,8 @@ int ib_umem_odp_map_dma_pages(struct ib_umem_odp *umem_odp, 
u64 user_virt,
 * ib_umem_odp_map_dma_single_page().
 */
if (npages - (j + 1) > 0)
-   put_user_pages(_page_list[j+1],
-  npages - (j + 1));
+   release_pages(_page_list[j+1],
+ npages - (j + 1));
break;
}
}
diff --git a/drivers/infiniband/hw/hfi1/user_pages.c 
b/drivers/infiniband/hw/hfi1/user_pages.c
index 469acb961fbd..9a94761765c0 100644
--- a/drivers/infiniband/hw/hfi1/user_pages.c
+++ b/drivers/infiniband/hw/hfi1/user_pages.c
@@ -106,7 +106,7 @@ int hfi1_acquire_user_pages(struct mm_struct *mm, unsigned 
long vaddr, size_t np
int ret;
unsigned int gup_flags = FOLL_LONGTERM | (writable ? FOLL_WRITE : 0);
 
-   ret = get_user_pages_fast(vaddr, npages, gup_flags, pages);
+   ret = pin_user_pages_fast(vaddr, npages, gup_flags, pages);
if (ret < 0)
return ret;
 
diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.c 
b/drivers/infiniband/hw/mthca/mthca_memfree.c
index edccfd6e178f..8269ab040c21 100644
--- a/drivers/infiniband/hw/mthca/mthca_memfree.c
+++ b/drivers/infiniband/hw/mthca/mthca_memfree.c
@@ -472,7 +472,7 @@ int mthca_map_user_db(struct mthca_dev *dev, struct 
mthca_uar *uar,
goto out;
}
 
-   ret = get_user_pages_fast(uaddr & PAGE_MASK, 1,
+   ret = pin_user_pages_fast(uaddr & PAGE_MASK, 1,
  FOLL_WRITE | FOLL_LONGTERM, pages);
if (ret < 0)
goto out;
diff --git 

[PATCH v9 16/25] net/xdp: set FOLL_PIN via pin_user_pages()

2019-12-10 Thread John Hubbard
Convert net/xdp to use the new pin_longterm_pages() call, which sets
FOLL_PIN. Setting FOLL_PIN is now required for code that requires
tracking of pinned pages.

In partial anticipation of this work, the net/xdp code was already
calling put_user_page() instead of put_page(). Therefore, in order to
convert from the get_user_pages()/put_page() model, to the
pin_user_pages()/put_user_page() model, the only change required
here is to change get_user_pages() to pin_user_pages().

Acked-by: Björn Töpel 
Signed-off-by: John Hubbard 
---
 net/xdp/xdp_umem.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
index 3049af269fbf..d071003b5e76 100644
--- a/net/xdp/xdp_umem.c
+++ b/net/xdp/xdp_umem.c
@@ -291,7 +291,7 @@ static int xdp_umem_pin_pages(struct xdp_umem *umem)
return -ENOMEM;
 
down_read(>mm->mmap_sem);
-   npgs = get_user_pages(umem->address, umem->npgs,
+   npgs = pin_user_pages(umem->address, umem->npgs,
  gup_flags | FOLL_LONGTERM, >pgs[0], NULL);
up_read(>mm->mmap_sem);
 
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 06/25] mm: fix get_user_pages_remote()'s handling of FOLL_LONGTERM

2019-12-10 Thread John Hubbard
As it says in the updated comment in gup.c: current FOLL_LONGTERM
behavior is incompatible with FAULT_FLAG_ALLOW_RETRY because of the
FS DAX check requirement on vmas.

However, the corresponding restriction in get_user_pages_remote() was
slightly stricter than is actually required: it forbade all
FOLL_LONGTERM callers, but we can actually allow FOLL_LONGTERM callers
that do not set the "locked" arg.

Update the code and comments to loosen the restriction, allowing
FOLL_LONGTERM in some cases.

Also, copy the DAX check ("if a VMA is DAX, don't allow long term
pinning") from the VFIO call site, all the way into the internals
of get_user_pages_remote() and __gup_longterm_locked(). That is:
get_user_pages_remote() calls __gup_longterm_locked(), which in turn
calls check_dax_vmas(). This check will then be removed from the VFIO
call site in a subsequent patch.

Thanks to Jason Gunthorpe for pointing out a clean way to fix this,
and to Dan Williams for helping clarify the DAX refactoring.

Tested-by: Alex Williamson 
Acked-by: Alex Williamson 
Reviewed-by: Jason Gunthorpe 
Reviewed-by: Ira Weiny 
Suggested-by: Jason Gunthorpe 
Cc: Dan Williams 
Cc: Jerome Glisse 
Signed-off-by: John Hubbard 
---
 mm/gup.c | 27 ++-
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/mm/gup.c b/mm/gup.c
index 3ecce297a47f..c0c56888e7cc 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -29,6 +29,13 @@ struct follow_page_context {
unsigned int page_mask;
 };
 
+static __always_inline long __gup_longterm_locked(struct task_struct *tsk,
+ struct mm_struct *mm,
+ unsigned long start,
+ unsigned long nr_pages,
+ struct page **pages,
+ struct vm_area_struct **vmas,
+ unsigned int flags);
 /*
  * Return the compound head page with ref appropriately incremented,
  * or NULL if that failed.
@@ -1179,13 +1186,23 @@ long get_user_pages_remote(struct task_struct *tsk, 
struct mm_struct *mm,
struct vm_area_struct **vmas, int *locked)
 {
/*
-* FIXME: Current FOLL_LONGTERM behavior is incompatible with
+* Parts of FOLL_LONGTERM behavior are incompatible with
 * FAULT_FLAG_ALLOW_RETRY because of the FS DAX check requirement on
-* vmas.  As there are no users of this flag in this call we simply
-* disallow this option for now.
+* vmas. However, this only comes up if locked is set, and there are
+* callers that do request FOLL_LONGTERM, but do not set locked. So,
+* allow what we can.
 */
-   if (WARN_ON_ONCE(gup_flags & FOLL_LONGTERM))
-   return -EINVAL;
+   if (gup_flags & FOLL_LONGTERM) {
+   if (WARN_ON_ONCE(locked))
+   return -EINVAL;
+   /*
+* This will check the vmas (even if our vmas arg is NULL)
+* and return -ENOTSUPP if DAX isn't allowed in this case:
+*/
+   return __gup_longterm_locked(tsk, mm, start, nr_pages, pages,
+vmas, gup_flags | FOLL_TOUCH |
+FOLL_REMOTE);
+   }
 
return __get_user_pages_locked(tsk, mm, start, nr_pages, pages, vmas,
   locked,
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 15/25] fs/io_uring: set FOLL_PIN via pin_user_pages()

2019-12-10 Thread John Hubbard
Convert fs/io_uring to use the new pin_user_pages() call, which sets
FOLL_PIN. Setting FOLL_PIN is now required for code that requires
tracking of pinned pages, and therefore for any code that calls
put_user_page().

In partial anticipation of this work, the io_uring code was already
calling put_user_page() instead of put_page(). Therefore, in order to
convert from the get_user_pages()/put_page() model, to the
pin_user_pages()/put_user_page() model, the only change required
here is to change get_user_pages() to pin_user_pages().

Reviewed-by: Jens Axboe 
Reviewed-by: Jan Kara 
Signed-off-by: John Hubbard 
---
 fs/io_uring.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/io_uring.c b/fs/io_uring.c
index 405be10da73d..9639ebc21e8a 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -4521,7 +4521,7 @@ static int io_sqe_buffer_register(struct io_ring_ctx 
*ctx, void __user *arg,
 
ret = 0;
down_read(>mm->mmap_sem);
-   pret = get_user_pages(ubuf, nr_pages,
+   pret = pin_user_pages(ubuf, nr_pages,
  FOLL_WRITE | FOLL_LONGTERM,
  pages, vmas);
if (pret == nr_pages) {
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 08/25] mm/gup: allow FOLL_FORCE for get_user_pages_fast()

2019-12-10 Thread John Hubbard
Commit 817be129e6f2 ("mm: validate get_user_pages_fast flags") allowed
only FOLL_WRITE and FOLL_LONGTERM to be passed to get_user_pages_fast().
This, combined with the fact that get_user_pages_fast() falls back to
"slow gup", which *does* accept FOLL_FORCE, leads to an odd situation:
if you need FOLL_FORCE, you cannot call get_user_pages_fast().

There does not appear to be any reason for filtering out FOLL_FORCE.
There is nothing in the _fast() implementation that requires that we
avoid writing to the pages. So it appears to have been an oversight.

Fix by allowing FOLL_FORCE to be set for get_user_pages_fast().

Fixes: 817be129e6f2 ("mm: validate get_user_pages_fast flags")
Cc: Christoph Hellwig 
Reviewed-by: Leon Romanovsky 
Reviewed-by: Jan Kara 
Signed-off-by: John Hubbard 
---
 mm/gup.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/mm/gup.c b/mm/gup.c
index c0c56888e7cc..958ab0757389 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -2414,7 +2414,8 @@ int get_user_pages_fast(unsigned long start, int nr_pages,
unsigned long addr, len, end;
int nr = 0, ret = 0;
 
-   if (WARN_ON_ONCE(gup_flags & ~(FOLL_WRITE | FOLL_LONGTERM)))
+   if (WARN_ON_ONCE(gup_flags & ~(FOLL_WRITE | FOLL_LONGTERM |
+  FOLL_FORCE)))
return -EINVAL;
 
start = untagged_addr(start) & PAGE_MASK;
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 21/25] mm/gup_benchmark: use proper FOLL_WRITE flags instead of hard-coding "1"

2019-12-10 Thread John Hubbard
Fix the gup benchmark flags to use the symbolic FOLL_WRITE,
instead of a hard-coded "1" value.

Also, clean up the filtering of gup flags a little, by just doing
it once before issuing any of the get_user_pages*() calls. This
makes it harder to overlook, instead of having little "gup_flags & 1"
phrases in the function calls.

Reviewed-by: Ira Weiny 
Signed-off-by: John Hubbard 
---
 mm/gup_benchmark.c | 9 ++---
 tools/testing/selftests/vm/gup_benchmark.c | 6 +-
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/mm/gup_benchmark.c b/mm/gup_benchmark.c
index 7dd602d7f8db..7fc44d25eca7 100644
--- a/mm/gup_benchmark.c
+++ b/mm/gup_benchmark.c
@@ -48,18 +48,21 @@ static int __gup_benchmark_ioctl(unsigned int cmd,
nr = (next - addr) / PAGE_SIZE;
}
 
+   /* Filter out most gup flags: only allow a tiny subset here: */
+   gup->flags &= FOLL_WRITE;
+
switch (cmd) {
case GUP_FAST_BENCHMARK:
-   nr = get_user_pages_fast(addr, nr, gup->flags & 1,
+   nr = get_user_pages_fast(addr, nr, gup->flags,
 pages + i);
break;
case GUP_LONGTERM_BENCHMARK:
nr = get_user_pages(addr, nr,
-   (gup->flags & 1) | FOLL_LONGTERM,
+   gup->flags | FOLL_LONGTERM,
pages + i, NULL);
break;
case GUP_BENCHMARK:
-   nr = get_user_pages(addr, nr, gup->flags & 1, pages + i,
+   nr = get_user_pages(addr, nr, gup->flags, pages + i,
NULL);
break;
default:
diff --git a/tools/testing/selftests/vm/gup_benchmark.c 
b/tools/testing/selftests/vm/gup_benchmark.c
index 485cf06ef013..389327e9b30a 100644
--- a/tools/testing/selftests/vm/gup_benchmark.c
+++ b/tools/testing/selftests/vm/gup_benchmark.c
@@ -18,6 +18,9 @@
 #define GUP_LONGTERM_BENCHMARK _IOWR('g', 2, struct gup_benchmark)
 #define GUP_BENCHMARK  _IOWR('g', 3, struct gup_benchmark)
 
+/* Just the flags we need, copied from mm.h: */
+#define FOLL_WRITE 0x01/* check pte is writable */
+
 struct gup_benchmark {
__u64 get_delta_usec;
__u64 put_delta_usec;
@@ -85,7 +88,8 @@ int main(int argc, char **argv)
}
 
gup.nr_pages_per_call = nr_pages;
-   gup.flags = write;
+   if (write)
+   gup.flags |= FOLL_WRITE;
 
fd = open("/sys/kernel/debug/gup_benchmark", O_RDWR);
if (fd == -1)
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 24/25] mm/gup_benchmark: support pin_user_pages() and related calls

2019-12-10 Thread John Hubbard
Up until now, gup_benchmark supported testing of the
following kernel functions:

* get_user_pages(): via the '-U' command line option
* get_user_pages_longterm(): via the '-L' command line option
* get_user_pages_fast(): as the default (no options required)

Add test coverage for the new corresponding pin_*() functions:

* pin_user_pages_fast(): via the '-a' command line option
* pin_user_pages():  via the '-b' command line option

Also, add an option for clarity: '-u' for what is now (still) the
default choice: get_user_pages_fast().

Also, for the commands that set FOLL_PIN, verify that the pages
really are dma-pinned, via the new is_dma_pinned() routine.
Those commands are:

PIN_FAST_BENCHMARK : calls pin_user_pages_fast()
PIN_BENCHMARK  : calls pin_user_pages()

In between the calls to pin_*() and unpin_user_pages(),
check each page: if page_dma_pinned() returns false, then
WARN and return.

Do this outside of the benchmark timestamps, so that it doesn't
affect reported times.

Reviewed-by: Ira Weiny 
Signed-off-by: John Hubbard 
---
 mm/gup_benchmark.c | 65 --
 tools/testing/selftests/vm/gup_benchmark.c | 15 -
 2 files changed, 74 insertions(+), 6 deletions(-)

diff --git a/mm/gup_benchmark.c b/mm/gup_benchmark.c
index 7fc44d25eca7..76d32db48af8 100644
--- a/mm/gup_benchmark.c
+++ b/mm/gup_benchmark.c
@@ -8,6 +8,8 @@
 #define GUP_FAST_BENCHMARK _IOWR('g', 1, struct gup_benchmark)
 #define GUP_LONGTERM_BENCHMARK _IOWR('g', 2, struct gup_benchmark)
 #define GUP_BENCHMARK  _IOWR('g', 3, struct gup_benchmark)
+#define PIN_FAST_BENCHMARK _IOWR('g', 4, struct gup_benchmark)
+#define PIN_BENCHMARK  _IOWR('g', 5, struct gup_benchmark)
 
 struct gup_benchmark {
__u64 get_delta_usec;
@@ -19,6 +21,42 @@ struct gup_benchmark {
__u64 expansion[10];/* For future use */
 };
 
+static void put_back_pages(int cmd, struct page **pages, unsigned long 
nr_pages)
+{
+   int i;
+
+   switch (cmd) {
+   case GUP_FAST_BENCHMARK:
+   case GUP_LONGTERM_BENCHMARK:
+   case GUP_BENCHMARK:
+   for (i = 0; i < nr_pages; i++)
+   put_page(pages[i]);
+   break;
+
+   case PIN_FAST_BENCHMARK:
+   case PIN_BENCHMARK:
+   unpin_user_pages(pages, nr_pages);
+   break;
+   }
+}
+
+static void verify_dma_pinned(int cmd, struct page **pages,
+ unsigned long nr_pages)
+{
+   int i;
+
+   switch (cmd) {
+   case PIN_FAST_BENCHMARK:
+   case PIN_BENCHMARK:
+   for (i = 0; i < nr_pages; i++) {
+   if (WARN(!page_dma_pinned(pages[i]),
+"pages[%d] is NOT dma-pinned\n", i))
+   break;
+   }
+   break;
+   }
+}
+
 static int __gup_benchmark_ioctl(unsigned int cmd,
struct gup_benchmark *gup)
 {
@@ -65,6 +103,14 @@ static int __gup_benchmark_ioctl(unsigned int cmd,
nr = get_user_pages(addr, nr, gup->flags, pages + i,
NULL);
break;
+   case PIN_FAST_BENCHMARK:
+   nr = pin_user_pages_fast(addr, nr, gup->flags,
+pages + i);
+   break;
+   case PIN_BENCHMARK:
+   nr = pin_user_pages(addr, nr, gup->flags, pages + i,
+   NULL);
+   break;
default:
return -1;
}
@@ -75,15 +121,22 @@ static int __gup_benchmark_ioctl(unsigned int cmd,
}
end_time = ktime_get();
 
+   /* Shifting the meaning of nr_pages: now it is actual number pinned: */
+   nr_pages = i;
+
gup->get_delta_usec = ktime_us_delta(end_time, start_time);
gup->size = addr - gup->addr;
 
+   /*
+* Take an un-benchmark-timed moment to verify DMA pinned
+* state: print a warning if any non-dma-pinned pages are found:
+*/
+   verify_dma_pinned(cmd, pages, nr_pages);
+
start_time = ktime_get();
-   for (i = 0; i < nr_pages; i++) {
-   if (!pages[i])
-   break;
-   put_page(pages[i]);
-   }
+
+   put_back_pages(cmd, pages, nr_pages);
+
end_time = ktime_get();
gup->put_delta_usec = ktime_us_delta(end_time, start_time);
 
@@ -101,6 +154,8 @@ static long gup_benchmark_ioctl(struct file *filep, 
unsigned int cmd,
case GUP_FAST_BENCHMARK:
case GUP_LONGTERM_BENCHMARK:
case GUP_BENCHMARK:
+   case PIN_FAST_BENCHMARK:
+   case PIN_BENCHMARK:
break;
default:
return -EINVAL;
diff --git a/tools/testing/selftests/vm/gup_benchmark.c 

[PATCH v9 17/25] media/v4l2-core: set pages dirty upon releasing DMA buffers

2019-12-10 Thread John Hubbard
After DMA is complete, and the device and CPU caches are synchronized,
it's still required to mark the CPU pages as dirty, if the data was
coming from the device. However, this driver was just issuing a
bare put_page() call, without any set_page_dirty*() call.

Fix the problem, by calling set_page_dirty_lock() if the CPU pages
were potentially receiving data from the device.

Reviewed-by: Christoph Hellwig 
Acked-by: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Cc: 
Signed-off-by: John Hubbard 
---
 drivers/media/v4l2-core/videobuf-dma-sg.c | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c 
b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 66a6c6c236a7..28262190c3ab 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -349,8 +349,11 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma)
BUG_ON(dma->sglen);
 
if (dma->pages) {
-   for (i = 0; i < dma->nr_pages; i++)
+   for (i = 0; i < dma->nr_pages; i++) {
+   if (dma->direction == DMA_FROM_DEVICE)
+   set_page_dirty_lock(dma->pages[i]);
put_page(dma->pages[i]);
+   }
kfree(dma->pages);
dma->pages = NULL;
}
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 02/25] mm/gup: move try_get_compound_head() to top, fix minor issues

2019-12-10 Thread John Hubbard
An upcoming patch uses try_get_compound_head() more widely,
so move it to the top of gup.c.

Also fix a tiny spelling error and a checkpatch.pl warning.

Reviewed-by: Christoph Hellwig 
Reviewed-by: Jan Kara 
Reviewed-by: Ira Weiny 
Signed-off-by: John Hubbard 
---
 mm/gup.c | 29 +++--
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/mm/gup.c b/mm/gup.c
index f764432914c4..3ecce297a47f 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -29,6 +29,21 @@ struct follow_page_context {
unsigned int page_mask;
 };
 
+/*
+ * Return the compound head page with ref appropriately incremented,
+ * or NULL if that failed.
+ */
+static inline struct page *try_get_compound_head(struct page *page, int refs)
+{
+   struct page *head = compound_head(page);
+
+   if (WARN_ON_ONCE(page_ref_count(head) < 0))
+   return NULL;
+   if (unlikely(!page_cache_add_speculative(head, refs)))
+   return NULL;
+   return head;
+}
+
 /**
  * put_user_pages_dirty_lock() - release and optionally dirty gup-pinned pages
  * @pages:  array of pages to be maybe marked dirty, and definitely released.
@@ -1807,20 +1822,6 @@ static void __maybe_unused undo_dev_pagemap(int *nr, int 
nr_start,
}
 }
 
-/*
- * Return the compund head page with ref appropriately incremented,
- * or NULL if that failed.
- */
-static inline struct page *try_get_compound_head(struct page *page, int refs)
-{
-   struct page *head = compound_head(page);
-   if (WARN_ON_ONCE(page_ref_count(head) < 0))
-   return NULL;
-   if (unlikely(!page_cache_add_speculative(head, refs)))
-   return NULL;
-   return head;
-}
-
 #ifdef CONFIG_ARCH_HAS_PTE_SPECIAL
 static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
 unsigned int flags, struct page **pages, int *nr)
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 04/25] mm: devmap: refactor 1-based refcounting for ZONE_DEVICE pages

2019-12-10 Thread John Hubbard
An upcoming patch changes and complicates the refcounting and
especially the "put page" aspects of it. In order to keep
everything clean, refactor the devmap page release routines:

* Rename put_devmap_managed_page() to page_is_devmap_managed(),
  and limit the functionality to "read only": return a bool,
  with no side effects.

* Add a new routine, put_devmap_managed_page(), to handle checking
  what kind of page it is, and what kind of refcount handling it
  requires.

* Rename __put_devmap_managed_page() to free_devmap_managed_page(),
  and limit the functionality to unconditionally freeing a devmap
  page.

This is originally based on a separate patch by Ira Weiny, which
applied to an early version of the put_user_page() experiments.
Since then, Jérôme Glisse suggested the refactoring described above.

Cc: Christoph Hellwig 
Suggested-by: Jérôme Glisse 
Reviewed-by: Dan Williams 
Reviewed-by: Jan Kara 
Signed-off-by: Ira Weiny 
Signed-off-by: John Hubbard 
---
 include/linux/mm.h | 17 +
 mm/memremap.c  | 16 ++--
 mm/swap.c  | 24 
 3 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index c97ea3b694e6..77a4df06c8a7 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -952,9 +952,10 @@ static inline bool is_zone_device_page(const struct page 
*page)
 #endif
 
 #ifdef CONFIG_DEV_PAGEMAP_OPS
-void __put_devmap_managed_page(struct page *page);
+void free_devmap_managed_page(struct page *page);
 DECLARE_STATIC_KEY_FALSE(devmap_managed_key);
-static inline bool put_devmap_managed_page(struct page *page)
+
+static inline bool page_is_devmap_managed(struct page *page)
 {
if (!static_branch_unlikely(_managed_key))
return false;
@@ -963,7 +964,6 @@ static inline bool put_devmap_managed_page(struct page 
*page)
switch (page->pgmap->type) {
case MEMORY_DEVICE_PRIVATE:
case MEMORY_DEVICE_FS_DAX:
-   __put_devmap_managed_page(page);
return true;
default:
break;
@@ -971,7 +971,14 @@ static inline bool put_devmap_managed_page(struct page 
*page)
return false;
 }
 
+bool put_devmap_managed_page(struct page *page);
+
 #else /* CONFIG_DEV_PAGEMAP_OPS */
+static inline bool page_is_devmap_managed(struct page *page)
+{
+   return false;
+}
+
 static inline bool put_devmap_managed_page(struct page *page)
 {
return false;
@@ -1028,8 +1035,10 @@ static inline void put_page(struct page *page)
 * need to inform the device driver through callback. See
 * include/linux/memremap.h and HMM for details.
 */
-   if (put_devmap_managed_page(page))
+   if (page_is_devmap_managed(page)) {
+   put_devmap_managed_page(page);
return;
+   }
 
if (put_page_testzero(page))
__put_page(page);
diff --git a/mm/memremap.c b/mm/memremap.c
index e899fa876a62..2ba773859031 100644
--- a/mm/memremap.c
+++ b/mm/memremap.c
@@ -411,20 +411,8 @@ struct dev_pagemap *get_dev_pagemap(unsigned long pfn,
 EXPORT_SYMBOL_GPL(get_dev_pagemap);
 
 #ifdef CONFIG_DEV_PAGEMAP_OPS
-void __put_devmap_managed_page(struct page *page)
+void free_devmap_managed_page(struct page *page)
 {
-   int count = page_ref_dec_return(page);
-
-   /* still busy */
-   if (count > 1)
-   return;
-
-   /* only triggered by the dev_pagemap shutdown path */
-   if (count == 0) {
-   __put_page(page);
-   return;
-   }
-
/* notify page idle for dax */
if (!is_device_private_page(page)) {
wake_up_var(>_refcount);
@@ -461,5 +449,5 @@ void __put_devmap_managed_page(struct page *page)
page->mapping = NULL;
page->pgmap->ops->page_free(page);
 }
-EXPORT_SYMBOL(__put_devmap_managed_page);
+EXPORT_SYMBOL(free_devmap_managed_page);
 #endif /* CONFIG_DEV_PAGEMAP_OPS */
diff --git a/mm/swap.c b/mm/swap.c
index 5341ae93861f..49f7c2eea0ba 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -1102,3 +1102,27 @@ void __init swap_setup(void)
 * _really_ don't want to cluster much more
 */
 }
+
+#ifdef CONFIG_DEV_PAGEMAP_OPS
+bool put_devmap_managed_page(struct page *page)
+{
+   bool is_devmap = page_is_devmap_managed(page);
+
+   if (is_devmap) {
+   int count = page_ref_dec_return(page);
+
+   /*
+* devmap page refcounts are 1-based, rather than 0-based: if
+* refcount is 1, then the page is free and the refcount is
+* stable because nobody holds a reference on the page.
+*/
+   if (count == 1)
+   free_devmap_managed_page(page);
+   else if (!count)
+   __put_page(page);
+   }
+
+   return is_devmap;
+}
+EXPORT_SYMBOL(put_devmap_managed_page);
+#endif
-- 
2.24.0


[PATCH v9 13/25] mm/process_vm_access: set FOLL_PIN via pin_user_pages_remote()

2019-12-10 Thread John Hubbard
Convert process_vm_access to use the new pin_user_pages_remote()
call, which sets FOLL_PIN. Setting FOLL_PIN is now required for
code that requires tracking of pinned pages.

Also, release the pages via put_user_page*().

Also, rename "pages" to "pinned_pages", as this makes for
easier reading of process_vm_rw_single_vec().

Reviewed-by: Jan Kara 
Reviewed-by: Jérôme Glisse 
Reviewed-by: Ira Weiny 
Signed-off-by: John Hubbard 
---
 mm/process_vm_access.c | 28 +++-
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c
index 357aa7bef6c0..fd20ab675b85 100644
--- a/mm/process_vm_access.c
+++ b/mm/process_vm_access.c
@@ -42,12 +42,11 @@ static int process_vm_rw_pages(struct page **pages,
if (copy > len)
copy = len;
 
-   if (vm_write) {
+   if (vm_write)
copied = copy_page_from_iter(page, offset, copy, iter);
-   set_page_dirty_lock(page);
-   } else {
+   else
copied = copy_page_to_iter(page, offset, copy, iter);
-   }
+
len -= copied;
if (copied < copy && iov_iter_count(iter))
return -EFAULT;
@@ -96,7 +95,7 @@ static int process_vm_rw_single_vec(unsigned long addr,
flags |= FOLL_WRITE;
 
while (!rc && nr_pages && iov_iter_count(iter)) {
-   int pages = min(nr_pages, max_pages_per_loop);
+   int pinned_pages = min(nr_pages, max_pages_per_loop);
int locked = 1;
size_t bytes;
 
@@ -106,14 +105,15 @@ static int process_vm_rw_single_vec(unsigned long addr,
 * current/current->mm
 */
down_read(>mmap_sem);
-   pages = get_user_pages_remote(task, mm, pa, pages, flags,
- process_pages, NULL, );
+   pinned_pages = pin_user_pages_remote(task, mm, pa, pinned_pages,
+flags, process_pages,
+NULL, );
if (locked)
up_read(>mmap_sem);
-   if (pages <= 0)
+   if (pinned_pages <= 0)
return -EFAULT;
 
-   bytes = pages * PAGE_SIZE - start_offset;
+   bytes = pinned_pages * PAGE_SIZE - start_offset;
if (bytes > len)
bytes = len;
 
@@ -122,10 +122,12 @@ static int process_vm_rw_single_vec(unsigned long addr,
 vm_write);
len -= bytes;
start_offset = 0;
-   nr_pages -= pages;
-   pa += pages * PAGE_SIZE;
-   while (pages)
-   put_page(process_pages[--pages]);
+   nr_pages -= pinned_pages;
+   pa += pinned_pages * PAGE_SIZE;
+
+   /* If vm_write is set, the pages need to be made dirty: */
+   put_user_pages_dirty_lock(process_pages, pinned_pages,
+ vm_write);
}
 
return rc;
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 19/25] vfio, mm: pin_user_pages (FOLL_PIN) and put_user_page() conversion

2019-12-10 Thread John Hubbard
1. Change vfio from get_user_pages_remote(), to
pin_user_pages_remote().

2. Because all FOLL_PIN-acquired pages must be released via
put_user_page(), also convert the put_page() call over to
put_user_pages_dirty_lock().

Note that this effectively changes the code's behavior in
vfio_iommu_type1.c: put_pfn(): it now ultimately calls
set_page_dirty_lock(), instead of set_page_dirty(). This is
probably more accurate.

As Christoph Hellwig put it, "set_page_dirty() is only safe if we are
dealing with a file backed page where we have reference on the inode it
hangs off." [1]

[1] https://lore.kernel.org/r/20190723153640.gb...@lst.de

Tested-by: Alex Williamson 
Acked-by: Alex Williamson 
Signed-off-by: John Hubbard 
---
 drivers/vfio/vfio_iommu_type1.c | 7 +++
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index b800fc9a0251..18bfc2fc8e6d 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -309,9 +309,8 @@ static int put_pfn(unsigned long pfn, int prot)
 {
if (!is_invalid_reserved_pfn(pfn)) {
struct page *page = pfn_to_page(pfn);
-   if (prot & IOMMU_WRITE)
-   SetPageDirty(page);
-   put_page(page);
+
+   put_user_pages_dirty_lock(, 1, prot & IOMMU_WRITE);
return 1;
}
return 0;
@@ -329,7 +328,7 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned 
long vaddr,
flags |= FOLL_WRITE;
 
down_read(>mmap_sem);
-   ret = get_user_pages_remote(NULL, mm, vaddr, 1, flags | FOLL_LONGTERM,
+   ret = pin_user_pages_remote(NULL, mm, vaddr, 1, flags | FOLL_LONGTERM,
page, NULL, NULL);
if (ret == 1) {
*pfn = page_to_pfn(page[0]);
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 10/25] mm/gup: introduce pin_user_pages*() and FOLL_PIN

2019-12-10 Thread John Hubbard
Introduce pin_user_pages*() variations of get_user_pages*() calls,
and also pin_longterm_pages*() variations.

For now, these are placeholder calls, until the various call sites
are converted to use the correct get_user_pages*() or
pin_user_pages*() API.

These variants will eventually all set FOLL_PIN, which is also
introduced, and thoroughly documented.

pin_user_pages()
pin_user_pages_remote()
pin_user_pages_fast()

All pages that are pinned via the above calls, must be unpinned via
put_user_page().

The underlying rules are:

* FOLL_PIN is a gup-internal flag, so the call sites should not directly
set it. That behavior is enforced with assertions.

* Call sites that want to indicate that they are going to do DirectIO
  ("DIO") or something with similar characteristics, should call a
  get_user_pages()-like wrapper call that sets FOLL_PIN. These wrappers
  will:
* Start with "pin_user_pages" instead of "get_user_pages". That
  makes it easy to find and audit the call sites.
* Set FOLL_PIN

* For pages that are received via FOLL_PIN, those pages must be returned
  via put_user_page().

Thanks to Jan Kara and Vlastimil Babka for explaining the 4 cases
in this documentation. (I've reworded it and expanded upon it.)

Reviewed-by: Jan Kara 
Reviewed-by: Mike Rapoport   # Documentation
Reviewed-by: Jérôme Glisse 
Cc: Jonathan Corbet 
Cc: Ira Weiny 
Signed-off-by: John Hubbard 
---
 Documentation/core-api/index.rst  |   1 +
 Documentation/core-api/pin_user_pages.rst | 232 ++
 include/linux/mm.h|  63 --
 mm/gup.c  | 161 +--
 4 files changed, 423 insertions(+), 34 deletions(-)
 create mode 100644 Documentation/core-api/pin_user_pages.rst

diff --git a/Documentation/core-api/index.rst b/Documentation/core-api/index.rst
index ab0eae1c153a..413f7d7c8642 100644
--- a/Documentation/core-api/index.rst
+++ b/Documentation/core-api/index.rst
@@ -31,6 +31,7 @@ Core utilities
generic-radix-tree
memory-allocation
mm-api
+   pin_user_pages
gfp_mask-from-fs-io
timekeeping
boot-time-mm
diff --git a/Documentation/core-api/pin_user_pages.rst 
b/Documentation/core-api/pin_user_pages.rst
new file mode 100644
index ..a7a261d869f1
--- /dev/null
+++ b/Documentation/core-api/pin_user_pages.rst
@@ -0,0 +1,232 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+
+pin_user_pages() and related calls
+
+
+.. contents:: :local:
+
+Overview
+
+
+This document describes the following functions: ::
+
+ pin_user_pages
+ pin_user_pages_fast
+ pin_user_pages_remote
+
+Basic description of FOLL_PIN
+=
+
+FOLL_PIN and FOLL_LONGTERM are flags that can be passed to the 
get_user_pages*()
+("gup") family of functions. FOLL_PIN has significant interactions and
+interdependencies with FOLL_LONGTERM, so both are covered here.
+
+FOLL_PIN is internal to gup, meaning that it should not appear at the gup call
+sites. This allows the associated wrapper functions  (pin_user_pages*() and
+others) to set the correct combination of these flags, and to check for 
problems
+as well.
+
+FOLL_LONGTERM, on the other hand, *is* allowed to be set at the gup call sites.
+This is in order to avoid creating a large number of wrapper functions to cover
+all combinations of get*(), pin*(), FOLL_LONGTERM, and more. Also, the
+pin_user_pages*() APIs are clearly distinct from the get_user_pages*() APIs, so
+that's a natural dividing line, and a good point to make separate wrapper 
calls.
+In other words, use pin_user_pages*() for DMA-pinned pages, and
+get_user_pages*() for other cases. There are four cases described later on in
+this document, to further clarify that concept.
+
+FOLL_PIN and FOLL_GET are mutually exclusive for a given gup call. However,
+multiple threads and call sites are free to pin the same struct pages, via both
+FOLL_PIN and FOLL_GET. It's just the call site that needs to choose one or the
+other, not the struct page(s).
+
+The FOLL_PIN implementation is nearly the same as FOLL_GET, except that 
FOLL_PIN
+uses a different reference counting technique.
+
+FOLL_PIN is a prerequisite to FOLL_LONGTGERM. Another way of saying that is,
+FOLL_LONGTERM is a specific case, more restrictive case of FOLL_PIN.
+
+Which flags are set by each wrapper
+===
+
+For these pin_user_pages*() functions, FOLL_PIN is OR'd in with whatever gup
+flags the caller provides. The caller is required to pass in a non-null struct
+pages* array, and the function then pin pages by incrementing each by a special
+value. For now, that value is +1, just like get_user_pages*().::
+
+ Function
+ 
+ pin_user_pages  FOLL_PIN is always set internally by this function.
+ pin_user_pages_fast FOLL_PIN is always set internally by this function.
+ 

[PATCH v9 22/25] mm, tree-wide: rename put_user_page*() to unpin_user_page*()

2019-12-10 Thread John Hubbard
In order to provide a clearer, more symmetric API for pinning
and unpinning DMA pages. This way, pin_user_pages*() calls
match up with unpin_user_pages*() calls, and the API is a lot
closer to being self-explanatory.

Reviewed-by: Jan Kara 
Signed-off-by: John Hubbard 
---
 Documentation/core-api/pin_user_pages.rst   |  2 +-
 arch/powerpc/mm/book3s64/iommu_api.c|  4 +--
 drivers/gpu/drm/via/via_dmablit.c   |  4 +--
 drivers/infiniband/core/umem.c  |  2 +-
 drivers/infiniband/hw/hfi1/user_pages.c |  2 +-
 drivers/infiniband/hw/mthca/mthca_memfree.c |  6 ++--
 drivers/infiniband/hw/qib/qib_user_pages.c  |  2 +-
 drivers/infiniband/hw/qib/qib_user_sdma.c   |  6 ++--
 drivers/infiniband/hw/usnic/usnic_uiom.c|  2 +-
 drivers/infiniband/sw/siw/siw_mem.c |  2 +-
 drivers/media/v4l2-core/videobuf-dma-sg.c   |  4 +--
 drivers/platform/goldfish/goldfish_pipe.c   |  4 +--
 drivers/vfio/vfio_iommu_type1.c |  2 +-
 fs/io_uring.c   |  4 +--
 include/linux/mm.h  | 26 -
 mm/gup.c| 32 ++---
 mm/process_vm_access.c  |  4 +--
 net/xdp/xdp_umem.c  |  2 +-
 18 files changed, 55 insertions(+), 55 deletions(-)

diff --git a/Documentation/core-api/pin_user_pages.rst 
b/Documentation/core-api/pin_user_pages.rst
index a7a261d869f1..fd2a19c96189 100644
--- a/Documentation/core-api/pin_user_pages.rst
+++ b/Documentation/core-api/pin_user_pages.rst
@@ -219,7 +219,7 @@ since the system was booted, via two new /proc/vmstat 
entries: ::
 /proc/vmstat/nr_foll_pin_requested
 
 Those are both going to show zero, unless CONFIG_DEBUG_VM is set. This is
-because there is a noticeable performance drop in put_user_page(), when they
+because there is a noticeable performance drop in unpin_user_page(), when they
 are activated.
 
 References
diff --git a/arch/powerpc/mm/book3s64/iommu_api.c 
b/arch/powerpc/mm/book3s64/iommu_api.c
index a86547822034..eba73ebd8ae5 100644
--- a/arch/powerpc/mm/book3s64/iommu_api.c
+++ b/arch/powerpc/mm/book3s64/iommu_api.c
@@ -168,7 +168,7 @@ static long mm_iommu_do_alloc(struct mm_struct *mm, 
unsigned long ua,
 
 free_exit:
/* free the references taken */
-   put_user_pages(mem->hpages, pinned);
+   unpin_user_pages(mem->hpages, pinned);
 
vfree(mem->hpas);
kfree(mem);
@@ -214,7 +214,7 @@ static void mm_iommu_unpin(struct 
mm_iommu_table_group_mem_t *mem)
if (mem->hpas[i] & MM_IOMMU_TABLE_GROUP_PAGE_DIRTY)
SetPageDirty(page);
 
-   put_user_page(page);
+   unpin_user_page(page);
 
mem->hpas[i] = 0;
}
diff --git a/drivers/gpu/drm/via/via_dmablit.c 
b/drivers/gpu/drm/via/via_dmablit.c
index 37c5e572993a..719d036c9384 100644
--- a/drivers/gpu/drm/via/via_dmablit.c
+++ b/drivers/gpu/drm/via/via_dmablit.c
@@ -188,8 +188,8 @@ via_free_sg_info(struct pci_dev *pdev, drm_via_sg_info_t 
*vsg)
kfree(vsg->desc_pages);
/* fall through */
case dr_via_pages_locked:
-   put_user_pages_dirty_lock(vsg->pages, vsg->num_pages,
- (vsg->direction == DMA_FROM_DEVICE));
+   unpin_user_pages_dirty_lock(vsg->pages, vsg->num_pages,
+  (vsg->direction == DMA_FROM_DEVICE));
/* fall through */
case dr_via_pages_alloc:
vfree(vsg->pages);
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index 55daefaa9b88..a6094766b6f5 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -54,7 +54,7 @@ static void __ib_umem_release(struct ib_device *dev, struct 
ib_umem *umem, int d
 
for_each_sg_page(umem->sg_head.sgl, _iter, umem->sg_nents, 0) {
page = sg_page_iter_page(_iter);
-   put_user_pages_dirty_lock(, 1, umem->writable && dirty);
+   unpin_user_pages_dirty_lock(, 1, umem->writable && dirty);
}
 
sg_free_table(>sg_head);
diff --git a/drivers/infiniband/hw/hfi1/user_pages.c 
b/drivers/infiniband/hw/hfi1/user_pages.c
index 9a94761765c0..3b505006c0a6 100644
--- a/drivers/infiniband/hw/hfi1/user_pages.c
+++ b/drivers/infiniband/hw/hfi1/user_pages.c
@@ -118,7 +118,7 @@ int hfi1_acquire_user_pages(struct mm_struct *mm, unsigned 
long vaddr, size_t np
 void hfi1_release_user_pages(struct mm_struct *mm, struct page **p,
 size_t npages, bool dirty)
 {
-   put_user_pages_dirty_lock(p, npages, dirty);
+   unpin_user_pages_dirty_lock(p, npages, dirty);
 
if (mm) { /* during close after signal, mm can be NULL */
atomic64_sub(npages, >pinned_vm);
diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.c 
b/drivers/infiniband/hw/mthca/mthca_memfree.c
index 

[PATCH v9 25/25] selftests/vm: run_vmtests: invoke gup_benchmark with basic FOLL_PIN coverage

2019-12-10 Thread John Hubbard
It's good to have basic unit test coverage of the new FOLL_PIN
behavior. Fortunately, the gup_benchmark unit test is extremely
fast (a few milliseconds), so adding it the the run_vmtests suite
is going to cause no noticeable change in running time.

So, add two new invocations to run_vmtests:

1) Run gup_benchmark with normal get_user_pages().

2) Run gup_benchmark with pin_user_pages(). This is much like
the first call, except that it sets FOLL_PIN.

Running these two in quick succession also provide a visual
comparison of the running times, which is convenient.

The new invocations are fairly early in the run_vmtests script,
because with test suites, it's usually preferable to put the
shorter, faster tests first, all other things being equal.

Reviewed-by: Ira Weiny 
Signed-off-by: John Hubbard 
---
 tools/testing/selftests/vm/run_vmtests | 22 ++
 1 file changed, 22 insertions(+)

diff --git a/tools/testing/selftests/vm/run_vmtests 
b/tools/testing/selftests/vm/run_vmtests
index a692ea828317..df6a6bf3f238 100755
--- a/tools/testing/selftests/vm/run_vmtests
+++ b/tools/testing/selftests/vm/run_vmtests
@@ -112,6 +112,28 @@ echo "NOTE: The above hugetlb tests provide minimal 
coverage.  Use"
 echo "  https://github.com/libhugetlbfs/libhugetlbfs.git for"
 echo "  hugetlb regression testing."
 
+echo ""
+echo "running 'gup_benchmark -U' (normal/slow gup)"
+echo ""
+./gup_benchmark -U
+if [ $? -ne 0 ]; then
+   echo "[FAIL]"
+   exitcode=1
+else
+   echo "[PASS]"
+fi
+
+echo "--"
+echo "running gup_benchmark -b (pin_user_pages)"
+echo "--"
+./gup_benchmark -b
+if [ $? -ne 0 ]; then
+   echo "[FAIL]"
+   exitcode=1
+else
+   echo "[PASS]"
+fi
+
 echo "---"
 echo "running userfaultfd"
 echo "---"
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 09/25] IB/umem: use get_user_pages_fast() to pin DMA pages

2019-12-10 Thread John Hubbard
And get rid of the mmap_sem calls, as part of that. Note
that get_user_pages_fast() will, if necessary, fall back to
__gup_longterm_unlocked(), which takes the mmap_sem as needed.

Reviewed-by: Leon Romanovsky 
Reviewed-by: Christoph Hellwig 
Reviewed-by: Jan Kara 
Reviewed-by: Jason Gunthorpe 
Reviewed-by: Ira Weiny 
Signed-off-by: John Hubbard 
---
 drivers/infiniband/core/umem.c | 17 ++---
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index 7a3b99597ead..214e87aa609d 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -266,16 +266,13 @@ struct ib_umem *ib_umem_get(struct ib_udata *udata, 
unsigned long addr,
sg = umem->sg_head.sgl;
 
while (npages) {
-   down_read(>mmap_sem);
-   ret = get_user_pages(cur_base,
-min_t(unsigned long, npages,
-  PAGE_SIZE / sizeof (struct page *)),
-gup_flags | FOLL_LONGTERM,
-page_list, NULL);
-   if (ret < 0) {
-   up_read(>mmap_sem);
+   ret = get_user_pages_fast(cur_base,
+ min_t(unsigned long, npages,
+   PAGE_SIZE /
+   sizeof(struct page *)),
+ gup_flags | FOLL_LONGTERM, page_list);
+   if (ret < 0)
goto umem_release;
-   }
 
cur_base += ret * PAGE_SIZE;
npages   -= ret;
@@ -283,8 +280,6 @@ struct ib_umem *ib_umem_get(struct ib_udata *udata, 
unsigned long addr,
sg = ib_umem_add_sg_table(sg, page_list, ret,
dma_get_max_seg_size(context->device->dma_device),
>sg_nents);
-
-   up_read(>mmap_sem);
}
 
sg_mark_end(sg);
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 18/25] media/v4l2-core: pin_user_pages (FOLL_PIN) and put_user_page() conversion

2019-12-10 Thread John Hubbard
1. Change v4l2 from get_user_pages() to pin_user_pages().

2. Because all FOLL_PIN-acquired pages must be released via
put_user_page(), also convert the put_page() call over to
put_user_pages_dirty_lock().

Acked-by: Hans Verkuil 
Cc: Ira Weiny 
Signed-off-by: John Hubbard 
---
 drivers/media/v4l2-core/videobuf-dma-sg.c | 11 ---
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c 
b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 28262190c3ab..162a2633b1e3 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -183,12 +183,12 @@ static int videobuf_dma_init_user_locked(struct 
videobuf_dmabuf *dma,
dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n",
data, size, dma->nr_pages);
 
-   err = get_user_pages(data & PAGE_MASK, dma->nr_pages,
+   err = pin_user_pages(data & PAGE_MASK, dma->nr_pages,
 flags | FOLL_LONGTERM, dma->pages, NULL);
 
if (err != dma->nr_pages) {
dma->nr_pages = (err >= 0) ? err : 0;
-   dprintk(1, "get_user_pages: err=%d [%d]\n", err,
+   dprintk(1, "pin_user_pages: err=%d [%d]\n", err,
dma->nr_pages);
return err < 0 ? err : -EINVAL;
}
@@ -349,11 +349,8 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma)
BUG_ON(dma->sglen);
 
if (dma->pages) {
-   for (i = 0; i < dma->nr_pages; i++) {
-   if (dma->direction == DMA_FROM_DEVICE)
-   set_page_dirty_lock(dma->pages[i]);
-   put_page(dma->pages[i]);
-   }
+   put_user_pages_dirty_lock(dma->pages, dma->nr_pages,
+ dma->direction == DMA_FROM_DEVICE);
kfree(dma->pages);
dma->pages = NULL;
}
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 11/25] goldish_pipe: convert to pin_user_pages() and put_user_page()

2019-12-10 Thread John Hubbard
1. Call the new global pin_user_pages_fast(), from pin_goldfish_pages().

2. As required by pin_user_pages(), release these pages via
put_user_page(). In this case, do so via put_user_pages_dirty_lock().

That has the side effect of calling set_page_dirty_lock(), instead
of set_page_dirty(). This is probably more accurate.

As Christoph Hellwig put it, "set_page_dirty() is only safe if we are
dealing with a file backed page where we have reference on the inode it
hangs off." [1]

Another side effect is that the release code is simplified because
the page[] loop is now in gup.c instead of here, so just delete the
local release_user_pages() entirely, and call
put_user_pages_dirty_lock() directly, instead.

[1] https://lore.kernel.org/r/20190723153640.gb...@lst.de

Reviewed-by: Jan Kara 
Reviewed-by: Ira Weiny 
Signed-off-by: John Hubbard 
---
 drivers/platform/goldfish/goldfish_pipe.c | 17 +++--
 1 file changed, 3 insertions(+), 14 deletions(-)

diff --git a/drivers/platform/goldfish/goldfish_pipe.c 
b/drivers/platform/goldfish/goldfish_pipe.c
index ef50c264db71..2a5901efecde 100644
--- a/drivers/platform/goldfish/goldfish_pipe.c
+++ b/drivers/platform/goldfish/goldfish_pipe.c
@@ -274,7 +274,7 @@ static int goldfish_pin_pages(unsigned long first_page,
*iter_last_page_size = last_page_size;
}
 
-   ret = get_user_pages_fast(first_page, requested_pages,
+   ret = pin_user_pages_fast(first_page, requested_pages,
  !is_write ? FOLL_WRITE : 0,
  pages);
if (ret <= 0)
@@ -285,18 +285,6 @@ static int goldfish_pin_pages(unsigned long first_page,
return ret;
 }
 
-static void release_user_pages(struct page **pages, int pages_count,
-  int is_write, s32 consumed_size)
-{
-   int i;
-
-   for (i = 0; i < pages_count; i++) {
-   if (!is_write && consumed_size > 0)
-   set_page_dirty(pages[i]);
-   put_page(pages[i]);
-   }
-}
-
 /* Populate the call parameters, merging adjacent pages together */
 static void populate_rw_params(struct page **pages,
   int pages_count,
@@ -372,7 +360,8 @@ static int transfer_max_buffers(struct goldfish_pipe *pipe,
 
*consumed_size = pipe->command_buffer->rw_params.consumed_size;
 
-   release_user_pages(pipe->pages, pages_count, is_write, *consumed_size);
+   put_user_pages_dirty_lock(pipe->pages, pages_count,
+ !is_write && *consumed_size > 0);
 
mutex_unlock(>lock);
return 0;
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 07/25] vfio: fix FOLL_LONGTERM use, simplify get_user_pages_remote() call

2019-12-10 Thread John Hubbard
Update VFIO to take advantage of the recently loosened restriction on
FOLL_LONGTERM with get_user_pages_remote(). Also, now it is possible to
fix a bug: the VFIO caller is logically a FOLL_LONGTERM user, but it
wasn't setting FOLL_LONGTERM.

Also, remove an unnessary pair of calls that were releasing and
reacquiring the mmap_sem. There is no need to avoid holding mmap_sem
just in order to call page_to_pfn().

Also, now that the the DAX check ("if a VMA is DAX, don't allow long
term pinning") is in the internals of get_user_pages_remote() and
__gup_longterm_locked(), there's no need for it at the VFIO call site.
So remove it.

Tested-by: Alex Williamson 
Acked-by: Alex Williamson 
Reviewed-by: Jason Gunthorpe 
Reviewed-by: Ira Weiny 
Suggested-by: Jason Gunthorpe 
Cc: Dan Williams 
Cc: Jerome Glisse 
Signed-off-by: John Hubbard 
---
 drivers/vfio/vfio_iommu_type1.c | 30 +-
 1 file changed, 5 insertions(+), 25 deletions(-)

diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 2ada8e6cdb88..b800fc9a0251 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -322,7 +322,6 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned 
long vaddr,
 {
struct page *page[1];
struct vm_area_struct *vma;
-   struct vm_area_struct *vmas[1];
unsigned int flags = 0;
int ret;
 
@@ -330,33 +329,14 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned 
long vaddr,
flags |= FOLL_WRITE;
 
down_read(>mmap_sem);
-   if (mm == current->mm) {
-   ret = get_user_pages(vaddr, 1, flags | FOLL_LONGTERM, page,
-vmas);
-   } else {
-   ret = get_user_pages_remote(NULL, mm, vaddr, 1, flags, page,
-   vmas, NULL);
-   /*
-* The lifetime of a vaddr_get_pfn() page pin is
-* userspace-controlled. In the fs-dax case this could
-* lead to indefinite stalls in filesystem operations.
-* Disallow attempts to pin fs-dax pages via this
-* interface.
-*/
-   if (ret > 0 && vma_is_fsdax(vmas[0])) {
-   ret = -EOPNOTSUPP;
-   put_page(page[0]);
-   }
-   }
-   up_read(>mmap_sem);
-
+   ret = get_user_pages_remote(NULL, mm, vaddr, 1, flags | FOLL_LONGTERM,
+   page, NULL, NULL);
if (ret == 1) {
*pfn = page_to_pfn(page[0]);
-   return 0;
+   ret = 0;
+   goto done;
}
 
-   down_read(>mmap_sem);
-
vaddr = untagged_addr(vaddr);
 
vma = find_vma_intersection(mm, vaddr, vaddr + 1);
@@ -366,7 +346,7 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned 
long vaddr,
if (is_invalid_reserved_pfn(*pfn))
ret = 0;
}
-
+done:
up_read(>mmap_sem);
return ret;
 }
-- 
2.24.0

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v9 03/25] mm: Cleanup __put_devmap_managed_page() vs ->page_free()

2019-12-10 Thread John Hubbard
From: Dan Williams 

After the removal of the device-public infrastructure there are only 2
->page_free() call backs in the kernel. One of those is a device-private
callback in the nouveau driver, the other is a generic wakeup needed in
the DAX case. In the hopes that all ->page_free() callbacks can be
migrated to common core kernel functionality, move the device-private
specific actions in __put_devmap_managed_page() under the
is_device_private_page() conditional, including the ->page_free()
callback. For the other page types just open-code the generic wakeup.

Yes, the wakeup is only needed in the MEMORY_DEVICE_FSDAX case, but it
does no harm in the MEMORY_DEVICE_DEVDAX and MEMORY_DEVICE_PCI_P2PDMA
case.

Reviewed-by: Christoph Hellwig 
Reviewed-by: Jérôme Glisse 
Cc: Jan Kara 
Cc: Ira Weiny 
Signed-off-by: Dan Williams 
Signed-off-by: John Hubbard 
---
 drivers/nvdimm/pmem.c |  6 
 mm/memremap.c | 80 ---
 2 files changed, 44 insertions(+), 42 deletions(-)

diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index ad8e4df1282b..4eae441f86c9 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -337,13 +337,7 @@ static void pmem_release_disk(void *__pmem)
put_disk(pmem->disk);
 }
 
-static void pmem_pagemap_page_free(struct page *page)
-{
-   wake_up_var(>_refcount);
-}
-
 static const struct dev_pagemap_ops fsdax_pagemap_ops = {
-   .page_free  = pmem_pagemap_page_free,
.kill   = pmem_pagemap_kill,
.cleanup= pmem_pagemap_cleanup,
 };
diff --git a/mm/memremap.c b/mm/memremap.c
index 03ccbdfeb697..e899fa876a62 100644
--- a/mm/memremap.c
+++ b/mm/memremap.c
@@ -27,7 +27,8 @@ static void devmap_managed_enable_put(void)
 
 static int devmap_managed_enable_get(struct dev_pagemap *pgmap)
 {
-   if (!pgmap->ops || !pgmap->ops->page_free) {
+   if (pgmap->type == MEMORY_DEVICE_PRIVATE &&
+   (!pgmap->ops || !pgmap->ops->page_free)) {
WARN(1, "Missing page_free method\n");
return -EINVAL;
}
@@ -414,44 +415,51 @@ void __put_devmap_managed_page(struct page *page)
 {
int count = page_ref_dec_return(page);
 
-   /*
-* If refcount is 1 then page is freed and refcount is stable as nobody
-* holds a reference on the page.
-*/
-   if (count == 1) {
-   /* Clear Active bit in case of parallel mark_page_accessed */
-   __ClearPageActive(page);
-   __ClearPageWaiters(page);
+   /* still busy */
+   if (count > 1)
+   return;
 
-   mem_cgroup_uncharge(page);
+   /* only triggered by the dev_pagemap shutdown path */
+   if (count == 0) {
+   __put_page(page);
+   return;
+   }
 
-   /*
-* When a device_private page is freed, the page->mapping field
-* may still contain a (stale) mapping value. For example, the
-* lower bits of page->mapping may still identify the page as
-* an anonymous page. Ultimately, this entire field is just
-* stale and wrong, and it will cause errors if not cleared.
-* One example is:
-*
-*  migrate_vma_pages()
-*migrate_vma_insert_page()
-*  page_add_new_anon_rmap()
-*__page_set_anon_rmap()
-*  ...checks page->mapping, via PageAnon(page) call,
-*and incorrectly concludes that the page is an
-*anonymous page. Therefore, it incorrectly,
-*silently fails to set up the new anon rmap.
-*
-* For other types of ZONE_DEVICE pages, migration is either
-* handled differently or not done at all, so there is no need
-* to clear page->mapping.
-*/
-   if (is_device_private_page(page))
-   page->mapping = NULL;
+   /* notify page idle for dax */
+   if (!is_device_private_page(page)) {
+   wake_up_var(>_refcount);
+   return;
+   }
 
-   page->pgmap->ops->page_free(page);
-   } else if (!count)
-   __put_page(page);
+   /* Clear Active bit in case of parallel mark_page_accessed */
+   __ClearPageActive(page);
+   __ClearPageWaiters(page);
+
+   mem_cgroup_uncharge(page);
+
+   /*
+* When a device_private page is freed, the page->mapping field
+* may still contain a (stale) mapping value. For example, the
+* lower bits of page->mapping may still identify the page as an
+* anonymous page. Ultimately, this entire field is just stale
+* and wrong, and it will cause errors if not cleared.  One
+* example is:
+*
+*  

[PATCH v9 00/25] mm/gup: track dma-pinned pages: FOLL_PIN

2019-12-10 Thread John Hubbard
Hi,

This implements an API naming change (put_user_page*() -->
unpin_user_page*()), and also implements tracking of FOLL_PIN pages. It
extends that tracking to a few select subsystems. More subsystems will
be added in follow up work.

Christoph Hellwig, a point of interest:

a) I've moved the bulk of the code out of the inline functions, as
   requested, for the devmap changes (patch 4: "mm: devmap: refactor
   1-based refcounting for ZONE_DEVICE pages").

Changes since v8:

* Merged the "mm/gup: pass flags arg to __gup_device_* functions" patch
  into the "mm/gup: track FOLL_PIN pages" patch, as requested by
  Christoph and Jan.

* Changed void grab_page() to bool try_grab_page(), and handled errors
  at the call sites. (From Jan's review comments.) try_grab_page()
  attempts to avoid page refcount overflows, even when counting up with
  GUP_PIN_COUNTING_BIAS increments.

* Fixed a bug that I'd introduced, when changing a BUG() to a WARN().

* Added Jan's reviewed-by tag to the " mm/gup: allow FOLL_FORCE for
  get_user_pages_fast()" patch.

* Documentation: pin_user_pages.rst: fixed an incorrect gup_benchmark
  invocation, left over from the pin_longterm days, spotted while preparing
  this version.

* Rebased onto today's linux.git (-rc1), and re-tested.

Changes since v7:

* Rebased onto Linux 5.5-rc1

* Reworked the grab_page() and try_grab_compound_head(), for API
  consistency and less diffs (thanks to Jan Kara's reviews).

* Added Leon Romanovsky's reviewed-by tags for two of the IB-related
  patches.

* patch 4 refactoring changes, as mentioned above.

There is a git repo and branch, for convenience:

g...@github.com:johnhubbard/linux.git pin_user_pages_tracking_v8

For the remaining list of "changes since version N", those are all in
v7, which is here:

  https://lore.kernel.org/r/20191121071354.456618-1-jhubb...@nvidia.com


Overview:

This is a prerequisite to solving the problem of proper interactions
between file-backed pages, and [R]DMA activities, as discussed in [1],
[2], [3], and in a remarkable number of email threads since about
2017. :)

A new internal gup flag, FOLL_PIN is introduced, and thoroughly
documented in the last patch's Documentation/vm/pin_user_pages.rst.

I believe that this will provide a good starting point for doing the
layout lease work that Ira Weiny has been working on. That's because
these new wrapper functions provide a clean, constrained, systematically
named set of functionality that, again, is required in order to even
know if a page is "dma-pinned".

In contrast to earlier approaches, the page tracking can be
incrementally applied to the kernel call sites that, until now, have
been simply calling get_user_pages() ("gup"). In other words, opt-in by
changing from this:

get_user_pages() (sets FOLL_GET)
put_page()

to this:
pin_user_pages() (sets FOLL_PIN)
unpin_user_page()


Testing:

* I've done some overall kernel testing (LTP, and a few other goodies),
  and some directed testing to exercise some of the changes. And as you
  can see, gup_benchmark is enhanced to exercise this. Basically, I've
  been able to runtime test the core get_user_pages() and
  pin_user_pages() and related routines, but not so much on several of
  the call sites--but those are generally just a couple of lines
  changed, each.

  Not much of the kernel is actually using this, which on one hand
  reduces risk quite a lot. But on the other hand, testing coverage
  is low. So I'd love it if, in particular, the Infiniband and PowerPC
  folks could do a smoke test of this series for me.

  Runtime testing for the call sites so far is pretty light:

* io_uring: Some directed tests from liburing exercise this, and
they pass.
* process_vm_access.c: A small directed test passes.
* gup_benchmark: the enhanced version hits the new gup.c code, and
 passes.
* infiniband: ran "ib_write_bw", which exercises the umem.c changes,
  but not the other changes.
* VFIO: compiles (I'm vowing to set up a run time test soon, but it's
  not ready just yet)
* powerpc: it compiles...
* drm/via: compiles...
* goldfish: compiles...
* net/xdp: compiles...
* media/v4l2: compiles...

[1] Some slow progress on get_user_pages() (Apr 2, 2019): 
https://lwn.net/Articles/784574/
[2] DMA and get_user_pages() (LPC: Dec 12, 2018): 
https://lwn.net/Articles/774411/
[3] The trouble with get_user_pages() (Apr 30, 2018): 
https://lwn.net/Articles/753027/

Dan Williams (1):
  mm: Cleanup __put_devmap_managed_page() vs ->page_free()

John Hubbard (24):
  mm/gup: factor out duplicate code from four routines
  mm/gup: move try_get_compound_head() to top, fix minor issues
  mm: devmap: refactor 1-based refcounting for ZONE_DEVICE pages
  goldish_pipe: rename local pin_user_pages() routine

[PATCH v9 01/25] mm/gup: factor out duplicate code from four routines

2019-12-10 Thread John Hubbard
There are four locations in gup.c that have a fair amount of code
duplication. This means that changing one requires making the same
changes in four places, not to mention reading the same code four
times, and wondering if there are subtle differences.

Factor out the common code into static functions, thus reducing the
overall line count and the code's complexity.

Also, take the opportunity to slightly improve the efficiency of the
error cases, by doing a mass subtraction of the refcount, surrounded
by get_page()/put_page().

Also, further simplify (slightly), by waiting until the the successful
end of each routine, to increment *nr.

Reviewed-by: Christoph Hellwig 
Reviewed-by: Jérôme Glisse 
Reviewed-by: Jan Kara 
Cc: Ira Weiny 
Cc: Christoph Hellwig 
Cc: Aneesh Kumar K.V 
Signed-off-by: John Hubbard 
---
 mm/gup.c | 91 ++--
 1 file changed, 36 insertions(+), 55 deletions(-)

diff --git a/mm/gup.c b/mm/gup.c
index 7646bf993b25..f764432914c4 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -1978,6 +1978,25 @@ static int __gup_device_huge_pud(pud_t pud, pud_t *pudp, 
unsigned long addr,
 }
 #endif
 
+static int record_subpages(struct page *page, unsigned long addr,
+  unsigned long end, struct page **pages)
+{
+   int nr;
+
+   for (nr = 0; addr != end; addr += PAGE_SIZE)
+   pages[nr++] = page++;
+
+   return nr;
+}
+
+static void put_compound_head(struct page *page, int refs)
+{
+   /* Do a get_page() first, in case refs == page->_refcount */
+   get_page(page);
+   page_ref_sub(page, refs);
+   put_page(page);
+}
+
 #ifdef CONFIG_ARCH_HAS_HUGEPD
 static unsigned long hugepte_addr_end(unsigned long addr, unsigned long end,
  unsigned long sz)
@@ -2007,32 +2026,20 @@ static int gup_hugepte(pte_t *ptep, unsigned long sz, 
unsigned long addr,
/* hugepages are never "special" */
VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
 
-   refs = 0;
head = pte_page(pte);
-
page = head + ((addr & (sz-1)) >> PAGE_SHIFT);
-   do {
-   VM_BUG_ON(compound_head(page) != head);
-   pages[*nr] = page;
-   (*nr)++;
-   page++;
-   refs++;
-   } while (addr += PAGE_SIZE, addr != end);
+   refs = record_subpages(page, addr, end, pages + *nr);
 
head = try_get_compound_head(head, refs);
-   if (!head) {
-   *nr -= refs;
+   if (!head)
return 0;
-   }
 
if (unlikely(pte_val(pte) != pte_val(*ptep))) {
-   /* Could be optimized better */
-   *nr -= refs;
-   while (refs--)
-   put_page(head);
+   put_compound_head(head, refs);
return 0;
}
 
+   *nr += refs;
SetPageReferenced(head);
return 1;
 }
@@ -2079,28 +2086,19 @@ static int gup_huge_pmd(pmd_t orig, pmd_t *pmdp, 
unsigned long addr,
return __gup_device_huge_pmd(orig, pmdp, addr, end, pages, nr);
}
 
-   refs = 0;
page = pmd_page(orig) + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
-   do {
-   pages[*nr] = page;
-   (*nr)++;
-   page++;
-   refs++;
-   } while (addr += PAGE_SIZE, addr != end);
+   refs = record_subpages(page, addr, end, pages + *nr);
 
head = try_get_compound_head(pmd_page(orig), refs);
-   if (!head) {
-   *nr -= refs;
+   if (!head)
return 0;
-   }
 
if (unlikely(pmd_val(orig) != pmd_val(*pmdp))) {
-   *nr -= refs;
-   while (refs--)
-   put_page(head);
+   put_compound_head(head, refs);
return 0;
}
 
+   *nr += refs;
SetPageReferenced(head);
return 1;
 }
@@ -2120,28 +2118,19 @@ static int gup_huge_pud(pud_t orig, pud_t *pudp, 
unsigned long addr,
return __gup_device_huge_pud(orig, pudp, addr, end, pages, nr);
}
 
-   refs = 0;
page = pud_page(orig) + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
-   do {
-   pages[*nr] = page;
-   (*nr)++;
-   page++;
-   refs++;
-   } while (addr += PAGE_SIZE, addr != end);
+   refs = record_subpages(page, addr, end, pages + *nr);
 
head = try_get_compound_head(pud_page(orig), refs);
-   if (!head) {
-   *nr -= refs;
+   if (!head)
return 0;
-   }
 
if (unlikely(pud_val(orig) != pud_val(*pudp))) {
-   *nr -= refs;
-   while (refs--)
-   put_page(head);
+   put_compound_head(head, refs);
return 0;
}
 
+   *nr += refs;
SetPageReferenced(head);
return 1;
 }
@@ -2157,28 +2146,20 @@ static int gup_huge_pgd(pgd_t orig, pgd_t *pgdp, 
unsigned long 

Re: [PATCH] drm/virtio: fix mmap page attributes

2019-12-10 Thread Gurchetan Singh
On Tue, Dec 10, 2019 at 12:58 AM Gerd Hoffmann  wrote:
>
> virtio-gpu uses cached mappings.  shmem helpers use writecombine though.
> So roll our own mmap function, wrapping drm_gem_shmem_mmap(), to tweak
> vm_page_prot accordingly.
>
> Reported-by: Gurchetan Singh 
> Signed-off-by: Gerd Hoffmann 
> ---
>  drivers/gpu/drm/virtio/virtgpu_object.c | 18 +-
>  1 file changed, 17 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c 
> b/drivers/gpu/drm/virtio/virtgpu_object.c
> index 017a9e0fc3bb..158610902054 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_object.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_object.c
> @@ -75,6 +75,22 @@ static void virtio_gpu_free_object(struct drm_gem_object 
> *obj)
> drm_gem_shmem_free_object(obj);
>  }
>
> +static int virtio_gpu_gem_mmap(struct drm_gem_object *obj, struct 
> vm_area_struct *vma)
> +{
> +   pgprot_t prot;
> +   int ret;
> +
> +   ret = drm_gem_shmem_mmap(obj, vma);
> +   if (ret < 0)
> +   return ret;
> +
> +   /* virtio-gpu needs normal caching, so clear writecombine */
> +   prot = vm_get_page_prot(vma->vm_flags);
> +   prot = pgprot_decrypted(prot);
> +   vma->vm_page_prot = prot;
> +   return 0;
> +}
> +
>  static const struct drm_gem_object_funcs virtio_gpu_gem_funcs = {
> .free = virtio_gpu_free_object,
> .open = virtio_gpu_gem_object_open,
> @@ -86,7 +102,7 @@ static const struct drm_gem_object_funcs 
> virtio_gpu_gem_funcs = {
> .get_sg_table = drm_gem_shmem_get_sg_table,
> .vmap = drm_gem_shmem_vmap,

Do we need vmap/vmunap?  It seems optionable and also uses non-cacheable memory?

> .vunmap = drm_gem_shmem_vunmap,
> -   .mmap = _gem_shmem_mmap,
> +   .mmap = _gpu_gem_mmap,

Why the _gpu_gem_mmap?  Shouldn't just virtio_gpu_gem_mmap work?



>  };
>
>  struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
> --
> 2.18.1
>
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH v8 20/26] powerpc: book3s64: convert to pin_user_pages() and put_user_page()

2019-12-10 Thread Andrew Morton
On Mon, 9 Dec 2019 21:53:00 -0800 John Hubbard  wrote:

> > Correction: this is somehow missing the fixes that resulted from Jan Kara's 
> > review (he
> > noted that we can't take a page lock in this context). I must have picked 
> > up the
> > wrong version of it, when I rebased for -rc1.
> > 
> 
> Andrew, given that the series is now in -mm, what's the preferred way for me 
> to fix this?
> Send a v9 version of the whole series? Or something else?

I think a full resend is warranted at this time - it's only been in
there a day and there seem to be quite a number of changes to be made.

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[Bug 204241] amdgpu fails to resume from suspend

2019-12-10 Thread bugzilla-daemon
https://bugzilla.kernel.org/show_bug.cgi?id=204241

crab2...@gmail.com changed:

   What|Removed |Added

 CC||crab2...@gmail.com

--- Comment #43 from crab2...@gmail.com ---
Same problem with my Thinkpad x395 (model 20NL000YCD). The system refused to
suspend consistently and showed a blurred screen. Also, the LED on power button
do not turn off. 

The issue still exist when I disable fingerprint reader and SD card reader in
bios.

-- 
You are receiving this mail because:
You are watching the assignee of the bug.
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[Bug 204241] amdgpu fails to resume from suspend

2019-12-10 Thread bugzilla-daemon
https://bugzilla.kernel.org/show_bug.cgi?id=204241

--- Comment #44 from crab2...@gmail.com ---
Created attachment 286253
  --> https://bugzilla.kernel.org/attachment.cgi?id=286253=edit
log of x395 when suspend

-- 
You are receiving this mail because:
You are watching the assignee of the bug.
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH v8 23/26] mm/gup: pass flags arg to __gup_device_* functions

2019-12-10 Thread John Hubbard
On 12/10/19 4:49 AM, Jan Kara wrote:
> On Mon 09-12-19 14:53:41, John Hubbard wrote:
>> A subsequent patch requires access to gup flags, so pass the flags
>> argument through to the __gup_device_* functions.
>>
>> Also placate checkpatch.pl by shortening a nearby line.
>>
>> TODO: Christoph Hellwig requested folding this into the patch the uses
>> the gup flags arguments.
> 
> You should probably implement this TODO? :)
> 
>   Honza

Yes. Done for v9.

thanks,
-- 
John Hubbard
NVIDIA
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH v8 24/26] mm/gup: track FOLL_PIN pages

2019-12-10 Thread John Hubbard
On 12/10/19 5:39 AM, Jan Kara wrote:
...
>> +void grab_page(struct page *page, unsigned int flags)
>> +{
>> +if (flags & FOLL_GET)
>> +get_page(page);
>> +else if (flags & FOLL_PIN) {
>> +get_page(page);
>> +WARN_ON_ONCE(flags & FOLL_GET);
>> +/*
>> + * Use get_page(), above, to do the refcount error
>> + * checking. Then just add in the remaining references:
>> + */
>> +page_ref_add(page, GUP_PIN_COUNTING_BIAS - 1);
> 
> This is wrong for two reasons:
> 
> 1) You miss compound_head() indirection from get_page() for this
> page_ref_add().

whoops, yes that is missing.

> 
> 2) page_ref_add() could overflow the counter without noticing.
> 
> Especially with GUP_PIN_COUNTING_BIAS being non-trivial, it is realistic
> that an attacker might try to overflow the page refcount and we have to
> protect the kernel against that. So I think that all the places that would
> use grab_page() actually need to use try_grab_page() and then gracefully
> deal with the failure.
> 

OK, I've replaced grab_page() everywhere with try_grab_page(), with the
above issues fixed. The v7 patchset had error handling for grab_page() failures,
that had been reviewed, so relevants parts of that have reappeared.

I had initially hesitated to do this, but now I've gone ahead and added:

#define page_ref_zero_or_close_to_bias_overflow(page) \
((unsigned int) page_ref_count(page) + \
GUP_PIN_COUNTING_BIAS <= GUP_PIN_COUNTING_BIAS)

...which is used in the new try_grab_page() for protection.


>> @@ -278,11 +425,23 @@ static struct page *follow_page_pte(struct 
>> vm_area_struct *vma,
>>  goto retry;
>>  }
>>  
>> -if (flags & FOLL_GET) {
>> +if (flags & (FOLL_PIN | FOLL_GET)) {
>> +/*
>> + * Allow try_get_page() to take care of error handling, for
>> + * both cases: FOLL_GET or FOLL_PIN:
>> + */
>>  if (unlikely(!try_get_page(page))) {
>>  page = ERR_PTR(-ENOMEM);
>>  goto out;
>>  }
>> +
>> +if (flags & FOLL_PIN) {
>> +WARN_ON_ONCE(flags & FOLL_GET);
>> +
>> +/* We got a +1 refcount from try_get_page(), above. */
>> +page_ref_add(page, GUP_PIN_COUNTING_BIAS - 1);
>> +__update_proc_vmstat(page, NR_FOLL_PIN_REQUESTED, 1);
>> +}
>>  }
> 
> The same problem here as above, plus this place should use the same
> try_grab..() helper, shouldn't it?


Yes, now that the new try_grab_page() has behavior that matches what
this call site needs. Done.


> 
>> @@ -544,8 +703,8 @@ static struct page *follow_page_mask(struct 
>> vm_area_struct *vma,
>>  /* make this handle hugepd */
>>  page = follow_huge_addr(mm, address, flags & FOLL_WRITE);
>>  if (!IS_ERR(page)) {
>> -BUG_ON(flags & FOLL_GET);
>> -return page;
>> +WARN_ON_ONCE(flags & (FOLL_GET | FOLL_PIN));
>> +return NULL;
> 
> I agree with the change to WARN_ON_ONCE but why is correct the change of
> the return value? Note that this is actually a "success branch".
> 

Good catch, thanks! I worked through the logic...correctly at first, but then I 
must 
have become temporarily dazed by the raw destructive power of the pre-existing 
BUG_ON() statement, and screwed it up after all. :)


thanks,
-- 
John Hubbard
NVIDIA

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH 2/2] drm/i915/vlv_dsi: Control panel and backlight enable GPIOs on BYT

2019-12-10 Thread Linus Walleij
On Mon, Dec 2, 2019 at 4:49 PM Hans de Goede  wrote:

> There is only one problem, currently is is not possible to
> unregister a mapping added with pinctrl_register_mappings
> and the i915 driver is typically a module which can be unloaded
> and I believe actually is unloaded as part of the i915 CI.
>
> pinctrl_register_mappings copies the passed in mapping, but
> it is a shallow copy, so it contains pointers to the modules
> const segment and we do not want to re-add another copy of
> the mapping when the module loads a second time.
>
> Fixing this is easy though, there already is a pinctrl_unregister_map()
> function, we just need to export it so that the i915 driver can
> remove the mapping when it is unbound.
>
> Linus would exporting this function be ok with you?

Yep!

> Linus, question what is the purpose of the "dupping" / shallow
> copying of the argument passed to pinctrl_register_map ?

The initial commit contained this comment later removed:

+   /*
+* Make a copy of the map array - string pointers will end up in the
+* kernel const section anyway so these do not need to be deep copied.
+*/

The use was to free up memory for platforms using boardfiles
with a gazillion variants and huge pin control tables, so these
could be marked  __initdata and discarded after boot.
As the strings would anyway stay around we didn't need to
deep copy.

See for example in arch/arm/mach-u300/core.c
static struct pinctrl_map __initdata u300_pinmux_map[]

> Since
> it is shallow the mem for any pointers contained within there need
> to be kept around by the caller, so why not let the caller keep
> the pinctrl_map struct itself around too?

So the strings will be kept around because the kernel can't get
rid of strings. (Yeah it is silly, should haven been fixed ages
ago, but not by me, haha :)

> If we are going to export pinctrl_unregister_map() we need to make it
> do the right thing for dupped maps too, we can just store the dup flag
> in struct pinctrl_maps. So this is easy, but I wonder if we cannot
> get rid of the dupping all together ?

Maybe ... I don't know. What do you think? I suppose you could
make u300 crash if you do that.

Yours,
Linus Walleij
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH] drm/drm_panel: fix EXPORT of drm_panel_of_backlight

2019-12-10 Thread Sam Ravnborg
Hi Linus.

On Tue, Dec 10, 2019 at 10:29:59PM +0100, Linus Walleij wrote:
> On Tue, Dec 10, 2019 at 8:48 PM Sam Ravnborg  wrote:
> 
> > Fix link failure for module builds of panels.
> > The conditional compilation around drm_panel_of_backlight()
> > was wrong for a module build.
> > Fix it using IS_ENABLED().
> >
> > Fixes: 152dbdeab1b2 ("drm/panel: add backlight support")
> > Cc: Sam Ravnborg 
> > Cc: Laurent Pinchart 
> > Cc: Thierry Reding 
> > Cc: Maarten Lankhorst 
> > Cc: Maxime Ripard 
> > Cc: Sean Paul 
> > Cc: David Airlie 
> > Cc: Daniel Vetter 
> > Cc: Maxime Ripard 
> > Cc: dri-devel@lists.freedesktop.org
> > Signed-off-by: Sam Ravnborg 
> 
> Looks like the right fix to me:
> Reviewed-by: Linus Walleij 

Thanks, patch pushed to drm-misc-next so we have this fixed.

Sam
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH] backlight: corgi: Convert to use GPIO descriptors

2019-12-10 Thread Linus Walleij
On Sun, Dec 8, 2019 at 9:06 PM Robert Jarzmik  wrote:

> Linus Walleij  writes:
> > @@ -525,13 +525,33 @@ static void spitz_bl_kick_battery(void)
> >   }
> >  }
> >
> > +static struct gpiod_lookup_table spitz_lcdcon_gpio_table = {
> > + .dev_id = "spi0.1",
> How do you know the correct device name is "spi0.1" ?

With SPI devices it is always hard to know without access to the
actual hardware, so every patch is a request for testing...

I looked at arch/arm/mach-pxa/spitz.c and
it registers just one spi bus (AFAICT) with 3 chip
selects so that will be "spi0", and then
spi_register_board_info() is called with an array of 3
devices (spitz_spi_devices[]). Those are in order of
chip select so chip select 0, 1, 2. This is the second
device so chip select 1.

The code in drivers/spi/spi.c names the devices
using spi_dev_set_name() like this:
dev_set_name(>dev, "%s.%u", dev_name(>controller->dev),
 spi->chip_select);

So it will theoretically "spi0.1"

Beware about bugs in the above interpreter because it is
just my brain.

Yours,
Linus Walleij
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH RESEND] drm/msm/adreno: Do not print error on "qcom, gpu-pwrlevels" absence

2019-12-10 Thread Fabio Estevam
Booting the adreno driver on a imx53 board leads to the following
error message:

adreno 3000.gpu: [drm:adreno_gpu_init] *ERROR* Could not find the GPU 
powerlevels

As the "qcom,gpu-pwrlevels" property is optional and never present on
i.MX5, turn the message into debug level instead.

Signed-off-by: Fabio Estevam 
Reviewed-by: Jeffrey Hugo 
Reviewed-by: Jordan Crouse 
---
Trying once again :-)

 drivers/gpu/drm/msm/adreno/adreno_gpu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c 
b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index 0783e4b5486a..5d7bdb4c83cc 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -826,7 +826,7 @@ static int adreno_get_legacy_pwrlevels(struct device *dev)
 
node = of_get_compatible_child(dev->of_node, "qcom,gpu-pwrlevels");
if (!node) {
-   DRM_DEV_ERROR(dev, "Could not find the GPU powerlevels\n");
+   DRM_DEV_DEBUG(dev, "Could not find the GPU powerlevels\n");
return -ENXIO;
}
 
-- 
2.17.1

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v2] drm/panfrost: Add the panfrost_gem_mapping concept

2019-12-10 Thread Rob Herring
From: Boris Brezillon 

With the introduction of per-FD address space, the same BO can be mapped
in different address space if the BO is globally visible (GEM_FLINK)
and opened in different context or if the dmabuf is self-imported. The
current implementation does not take that case into account, and 
attaches the mapping directly to the panfrost_gem_object.

Let's create a panfrost_gem_mapping struct and allow multiple mappings
per BO.

The mappings are refcounted which helps solve another problem where
mappings were torn down (GEM handle closed by userspace) while GPU
jobs accessing those BOs were still in-flight. Jobs now keep a
reference on the mappings they use.

v2 (robh):
- Minor review comment clean-ups from Steven
- Use list_is_singular helper
- Just WARN if we add a mapping when madvise state is not WILLNEED.
  With that, drop the use of object_name_lock.

Fixes: a5efb4c9a562 ("drm/panfrost: Restructure the GEM object creation")
Fixes: 7282f7645d06 ("drm/panfrost: Implement per FD address spaces")
Cc: 
Signed-off-by: Boris Brezillon 
Signed-off-by: Rob Herring 
---
I've hacked up IGT prime_self_import test to run on panfrost other than 
the 2 test which depend on i915 debugfs files (export-vs-gem_close-race, 
reimport-vs-gem_close-race). With this patch, they now pass.

I'm not adding the test to IGT which is just a copy-n-paste of the 
original except for different wrappers for BO alloc and mmap. That 
should be fixed first IMO.

Rob

 drivers/gpu/drm/panfrost/panfrost_drv.c   |  91 +++--
 drivers/gpu/drm/panfrost/panfrost_gem.c   | 123 +++---
 drivers/gpu/drm/panfrost/panfrost_gem.h   |  41 +-
 .../gpu/drm/panfrost/panfrost_gem_shrinker.c  |   3 +-
 drivers/gpu/drm/panfrost/panfrost_job.c   |  13 +-
 drivers/gpu/drm/panfrost/panfrost_job.h   |   1 +
 drivers/gpu/drm/panfrost/panfrost_mmu.c   |  61 +
 drivers/gpu/drm/panfrost/panfrost_mmu.h   |   6 +-
 drivers/gpu/drm/panfrost/panfrost_perfcnt.c   |  34 +++--
 9 files changed, 299 insertions(+), 74 deletions(-)

diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c 
b/drivers/gpu/drm/panfrost/panfrost_drv.c
index f61364f7c471..88b431a267af 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -78,8 +78,10 @@ static int panfrost_ioctl_get_param(struct drm_device *ddev, 
void *data, struct
 static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,
struct drm_file *file)
 {
+   struct panfrost_file_priv *priv = file->driver_priv;
struct panfrost_gem_object *bo;
struct drm_panfrost_create_bo *args = data;
+   struct panfrost_gem_mapping *mapping;
 
if (!args->size || args->pad ||
(args->flags & ~(PANFROST_BO_NOEXEC | PANFROST_BO_HEAP)))
@@ -95,7 +97,14 @@ static int panfrost_ioctl_create_bo(struct drm_device *dev, 
void *data,
if (IS_ERR(bo))
return PTR_ERR(bo);
 
-   args->offset = bo->node.start << PAGE_SHIFT;
+   mapping = panfrost_gem_mapping_get(bo, priv);
+   if (!mapping) {
+   drm_gem_object_put_unlocked(>base.base);
+   return -EINVAL;
+   }
+
+   args->offset = mapping->mmnode.start << PAGE_SHIFT;
+   panfrost_gem_mapping_put(mapping);
 
return 0;
 }
@@ -119,6 +128,11 @@ panfrost_lookup_bos(struct drm_device *dev,
  struct drm_panfrost_submit *args,
  struct panfrost_job *job)
 {
+   struct panfrost_file_priv *priv = file_priv->driver_priv;
+   struct panfrost_gem_object *bo;
+   unsigned int i;
+   int ret;
+
job->bo_count = args->bo_handle_count;
 
if (!job->bo_count)
@@ -130,9 +144,32 @@ panfrost_lookup_bos(struct drm_device *dev,
if (!job->implicit_fences)
return -ENOMEM;
 
-   return drm_gem_objects_lookup(file_priv,
- (void __user 
*)(uintptr_t)args->bo_handles,
- job->bo_count, >bos);
+   ret = drm_gem_objects_lookup(file_priv,
+(void __user *)(uintptr_t)args->bo_handles,
+job->bo_count, >bos);
+   if (ret)
+   return ret;
+
+   job->mappings = kvmalloc_array(job->bo_count,
+  sizeof(struct panfrost_gem_mapping *),
+  GFP_KERNEL | __GFP_ZERO);
+   if (!job->mappings)
+   return -ENOMEM;
+
+   for (i = 0; i < job->bo_count; i++) {
+   struct panfrost_gem_mapping *mapping;
+
+   bo = to_panfrost_bo(job->bos[i]);
+   mapping = panfrost_gem_mapping_get(bo, priv);
+   if (!mapping) {
+   ret = -EINVAL;
+   break;
+   }
+
+   job->mappings[i] = mapping;
+   }
+
+   return ret;
 }
 
 /**
@@ -320,7 +357,9 @@ static int 

[PATCH v3 39/50] drm/omap: venc: Remove omap_dss_device operations

2019-12-10 Thread Laurent Pinchart
Now that the VENC output is driven fully through the drm_bridge API its
omap_dss_device operations are not used anymore. Remove them.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/venc.c | 45 --
 1 file changed, 45 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c 
b/drivers/gpu/drm/omapdrm/dss/venc.c
index 49499e2a9d31..37ac41c9d085 100644
--- a/drivers/gpu/drm/omapdrm/dss/venc.c
+++ b/drivers/gpu/drm/omapdrm/dss/venc.c
@@ -308,7 +308,6 @@ struct venc_device {
struct drm_bridge bridge;
 };
 
-#define dssdev_to_venc(dssdev) container_of(dssdev, struct venc_device, output)
 #define drm_bridge_to_venc(b) container_of(b, struct venc_device, bridge)
 
 static inline void venc_write_reg(struct venc_device *venc, int idx, u32 val)
@@ -481,30 +480,6 @@ static void venc_power_off(struct venc_device *venc)
venc_runtime_put(venc);
 }
 
-static int venc_get_modes(struct omap_dss_device *dssdev,
- struct drm_connector *connector)
-{
-   static const struct drm_display_mode *modes[] = {
-   _dss_pal_mode,
-   _dss_ntsc_mode,
-   };
-   unsigned int i;
-
-   for (i = 0; i < ARRAY_SIZE(modes); ++i) {
-   struct drm_display_mode *mode;
-
-   mode = drm_mode_duplicate(connector->dev, modes[i]);
-   if (!mode)
-   return i;
-
-   mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
-   drm_mode_set_name(mode);
-   drm_mode_probed_add(connector, mode);
-   }
-
-   return ARRAY_SIZE(modes);
-}
-
 static enum venc_videomode venc_get_videomode(const struct drm_display_mode 
*mode)
 {
if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
@@ -600,25 +575,6 @@ static int venc_get_clocks(struct venc_device *venc)
return 0;
 }
 
-static int venc_connect(struct omap_dss_device *src,
-   struct omap_dss_device *dst)
-{
-   return omapdss_device_connect(dst->dss, dst, dst->next);
-}
-
-static void venc_disconnect(struct omap_dss_device *src,
-   struct omap_dss_device *dst)
-{
-   omapdss_device_disconnect(dst, dst->next);
-}
-
-static const struct omap_dss_device_ops venc_ops = {
-   .connect = venc_connect,
-   .disconnect = venc_disconnect,
-
-   .get_modes = venc_get_modes,
-};
-
 /* 
-
  * DRM Bridge Operations
  */
@@ -827,7 +783,6 @@ static int venc_init_output(struct venc_device *venc)
out->type = OMAP_DISPLAY_TYPE_VENC;
out->name = "venc.0";
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
-   out->ops = _ops;
out->owner = THIS_MODULE;
out->of_port = 0;
out->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 46/50] drm/omap: sdi: Sort includes alphabetically

2019-12-10 Thread Laurent Pinchart
This makes it easier to quickly locate duplicate includes.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/sdi.c | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c 
b/drivers/gpu/drm/omapdrm/dss/sdi.c
index 11aa2f712ff4..7dedfcc86922 100644
--- a/drivers/gpu/drm/omapdrm/dss/sdi.c
+++ b/drivers/gpu/drm/omapdrm/dss/sdi.c
@@ -6,17 +6,17 @@
 
 #define DSS_SUBSYS_NAME "SDI"
 
-#include 
 #include 
 #include 
-#include 
 #include 
+#include 
+#include 
 #include 
+#include 
 #include 
-#include 
 
-#include "omapdss.h"
 #include "dss.h"
+#include "omapdss.h"
 
 struct sdi_device {
struct platform_device *pdev;
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 47/50] drm/omap: sdi: Register a drm_bridge

2019-12-10 Thread Laurent Pinchart
In order to integrate with a chain of drm_bridge, the internal SDI
output has to expose its operations through the drm_bridge API.
Register a bridge at initialisation time to do so and remove the
omap_dss_device operations that are now unused.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Remove unused omapdss_device_connector_type() function
- Unregister bridge if port initialisation fails
---
 drivers/gpu/drm/omapdrm/dss/base.c   |  23 
 drivers/gpu/drm/omapdrm/dss/omapdss.h|   1 -
 drivers/gpu/drm/omapdrm/dss/sdi.c| 168 +++
 drivers/gpu/drm/omapdrm/omap_connector.c |  31 +
 4 files changed, 111 insertions(+), 112 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/base.c 
b/drivers/gpu/drm/omapdrm/dss/base.c
index 2db3bd2f19db..455b410f7401 100644
--- a/drivers/gpu/drm/omapdrm/dss/base.c
+++ b/drivers/gpu/drm/omapdrm/dss/base.c
@@ -286,29 +286,6 @@ void omapdss_device_post_disable(struct omap_dss_device 
*dssdev)
 }
 EXPORT_SYMBOL_GPL(omapdss_device_post_disable);
 
-unsigned int omapdss_device_connector_type(enum omap_display_type type)
-{
-   switch (type) {
-   case OMAP_DISPLAY_TYPE_HDMI:
-   return DRM_MODE_CONNECTOR_HDMIA;
-   case OMAP_DISPLAY_TYPE_DVI:
-   return DRM_MODE_CONNECTOR_DVID;
-   case OMAP_DISPLAY_TYPE_DSI:
-   return DRM_MODE_CONNECTOR_DSI;
-   case OMAP_DISPLAY_TYPE_DPI:
-   case OMAP_DISPLAY_TYPE_DBI:
-   return DRM_MODE_CONNECTOR_DPI;
-   case OMAP_DISPLAY_TYPE_VENC:
-   /* TODO: This could also be composite */
-   return DRM_MODE_CONNECTOR_SVIDEO;
-   case OMAP_DISPLAY_TYPE_SDI:
-   return DRM_MODE_CONNECTOR_LVDS;
-   default:
-   return DRM_MODE_CONNECTOR_Unknown;
-   }
-}
-EXPORT_SYMBOL_GPL(omapdss_device_connector_type);
-
 /* 
-
  * Components Handling
  */
diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h 
b/drivers/gpu/drm/omapdrm/dss/omapdss.h
index cb79e05c902d..2e5453df2293 100644
--- a/drivers/gpu/drm/omapdrm/dss/omapdss.h
+++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h
@@ -454,7 +454,6 @@ void omapdss_device_pre_enable(struct omap_dss_device 
*dssdev);
 void omapdss_device_enable(struct omap_dss_device *dssdev);
 void omapdss_device_disable(struct omap_dss_device *dssdev);
 void omapdss_device_post_disable(struct omap_dss_device *dssdev);
-unsigned int omapdss_device_connector_type(enum omap_display_type type);
 
 int omap_dss_get_num_overlay_managers(void);
 
diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c 
b/drivers/gpu/drm/omapdrm/dss/sdi.c
index 7dedfcc86922..6ad9d1b94ec0 100644
--- a/drivers/gpu/drm/omapdrm/dss/sdi.c
+++ b/drivers/gpu/drm/omapdrm/dss/sdi.c
@@ -15,6 +15,8 @@
 #include 
 #include 
 
+#include 
+
 #include "dss.h"
 #include "omapdss.h"
 
@@ -30,9 +32,11 @@ struct sdi_device {
int datapairs;
 
struct omap_dss_device output;
+   struct drm_bridge bridge;
 };
 
-#define dssdev_to_sdi(dssdev) container_of(dssdev, struct sdi_device, output)
+#define drm_bridge_to_sdi(bridge) \
+   container_of(bridge, struct sdi_device, bridge)
 
 struct sdi_clk_calc_ctx {
struct sdi_device *sdi;
@@ -118,9 +122,82 @@ static void sdi_config_lcd_manager(struct sdi_device *sdi)
dss_mgr_set_lcd_config(>output, >mgr_config);
 }
 
-static void sdi_display_enable(struct omap_dss_device *dssdev)
+/* 
-
+ * DRM Bridge Operations
+ */
+
+static int sdi_bridge_attach(struct drm_bridge *bridge,
+enum drm_bridge_attach_flags flags)
+{
+   struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
+
+   if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
+   return -EINVAL;
+
+   return drm_bridge_attach(bridge->encoder, sdi->output.next_bridge,
+bridge, flags);
+}
+
+static enum drm_mode_status
+sdi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode)
+{
+   struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
+   unsigned long pixelclock = mode->clock * 1000;
+   struct dispc_clock_info dispc_cinfo;
+   unsigned long fck;
+   int ret;
+
+   if (pixelclock == 0)
+   return MODE_NOCLOCK;
+
+   ret = sdi_calc_clock_div(sdi, pixelclock, , _cinfo);
+   if (ret < 0)
+   return MODE_CLOCK_RANGE;
+
+   return MODE_OK;
+}
+
+static bool sdi_bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+   struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
+   unsigned long pixelclock = mode->clock * 1000;
+   struct dispc_clock_info dispc_cinfo;
+   unsigned long fck;
+   unsigned long 

[PATCH v3 24/50] drm/omap: dss: Make omap_dss_device_ops optional

2019-12-10 Thread Laurent Pinchart
As part of the move to drm_bridge ops, the dssdev ops will become empty
for some of the internal encoders. Make them optional in the driver to
allow them to be removed completely, easing the transition.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Tomi Valkeinen 
---
 drivers/gpu/drm/omapdrm/dss/base.c   | 21 -
 drivers/gpu/drm/omapdrm/dss/dss.c|  3 ++-
 drivers/gpu/drm/omapdrm/omap_connector.c |  2 +-
 drivers/gpu/drm/omapdrm/omap_encoder.c   | 12 +++-
 4 files changed, 22 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/base.c 
b/drivers/gpu/drm/omapdrm/dss/base.c
index 80d48936d177..2db3bd2f19db 100644
--- a/drivers/gpu/drm/omapdrm/dss/base.c
+++ b/drivers/gpu/drm/omapdrm/dss/base.c
@@ -195,10 +195,12 @@ int omapdss_device_connect(struct dss_device *dss,
 
dst->dss = dss;
 
-   ret = dst->ops->connect(src, dst);
-   if (ret < 0) {
-   dst->dss = NULL;
-   return ret;
+   if (dst->ops && dst->ops->connect) {
+   ret = dst->ops->connect(src, dst);
+   if (ret < 0) {
+   dst->dss = NULL;
+   return ret;
+   }
}
 
return 0;
@@ -226,7 +228,8 @@ void omapdss_device_disconnect(struct omap_dss_device *src,
 
WARN_ON(dst->state != OMAP_DSS_DISPLAY_DISABLED);
 
-   dst->ops->disconnect(src, dst);
+   if (dst->ops && dst->ops->disconnect)
+   dst->ops->disconnect(src, dst);
dst->dss = NULL;
 }
 EXPORT_SYMBOL_GPL(omapdss_device_disconnect);
@@ -238,7 +241,7 @@ void omapdss_device_pre_enable(struct omap_dss_device 
*dssdev)
 
omapdss_device_pre_enable(dssdev->next);
 
-   if (dssdev->ops->pre_enable)
+   if (dssdev->ops && dssdev->ops->pre_enable)
dssdev->ops->pre_enable(dssdev);
 }
 EXPORT_SYMBOL_GPL(omapdss_device_pre_enable);
@@ -248,7 +251,7 @@ void omapdss_device_enable(struct omap_dss_device *dssdev)
if (!dssdev)
return;
 
-   if (dssdev->ops->enable)
+   if (dssdev->ops && dssdev->ops->enable)
dssdev->ops->enable(dssdev);
 
omapdss_device_enable(dssdev->next);
@@ -264,7 +267,7 @@ void omapdss_device_disable(struct omap_dss_device *dssdev)
 
omapdss_device_disable(dssdev->next);
 
-   if (dssdev->ops->disable)
+   if (dssdev->ops && dssdev->ops->disable)
dssdev->ops->disable(dssdev);
 }
 EXPORT_SYMBOL_GPL(omapdss_device_disable);
@@ -274,7 +277,7 @@ void omapdss_device_post_disable(struct omap_dss_device 
*dssdev)
if (!dssdev)
return;
 
-   if (dssdev->ops->post_disable)
+   if (dssdev->ops && dssdev->ops->post_disable)
dssdev->ops->post_disable(dssdev);
 
omapdss_device_post_disable(dssdev->next);
diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c 
b/drivers/gpu/drm/omapdrm/dss/dss.c
index 67b92b5d8dd7..b76fc2b56227 100644
--- a/drivers/gpu/drm/omapdrm/dss/dss.c
+++ b/drivers/gpu/drm/omapdrm/dss/dss.c
@@ -1552,7 +1552,8 @@ static void dss_shutdown(struct platform_device *pdev)
DSSDBG("shutdown\n");
 
for_each_dss_output(dssdev) {
-   if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+   if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE &&
+   dssdev->ops && dssdev->ops->disable)
dssdev->ops->disable(dssdev);
}
 }
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c 
b/drivers/gpu/drm/omapdrm/omap_connector.c
index b0cb2ecb30ab..a24cec4b0bb9 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -228,7 +228,7 @@ enum drm_mode_status omap_connector_mode_fixup(struct 
omap_dss_device *dssdev,
drm_mode_copy(adjusted_mode, mode);
 
for (; dssdev; dssdev = dssdev->next) {
-   if (!dssdev->ops->check_timings)
+   if (!dssdev->ops || !dssdev->ops->check_timings)
continue;
 
ret = dssdev->ops->check_timings(dssdev, adjusted_mode);
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c 
b/drivers/gpu/drm/omapdrm/omap_encoder.c
index a270173a2411..b232acd3bc3d 100644
--- a/drivers/gpu/drm/omapdrm/omap_encoder.c
+++ b/drivers/gpu/drm/omapdrm/omap_encoder.c
@@ -77,10 +77,10 @@ static void omap_encoder_hdmi_mode_set(struct drm_connector 
*connector,
struct omap_dss_device *dssdev = omap_encoder->output;
bool hdmi_mode = connector->display_info.is_hdmi;
 
-   if (dssdev->ops->hdmi.set_hdmi_mode)
+   if (dssdev->ops && dssdev->ops->hdmi.set_hdmi_mode)
dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode);
 
-   if (hdmi_mode && dssdev->ops->hdmi.set_infoframe) {
+   if (hdmi_mode && dssdev->ops && dssdev->ops->hdmi.set_infoframe) {
struct hdmi_avi_infoframe avi;
int r;
 
@@ -139,7 +139,7 @@ static void omap_encoder_mode_set(struct drm_encoder 

[PATCH v3 36/50] drm/omap: Switch the HDMI and VENC outputs to drm_bridge

2019-12-10 Thread Laurent Pinchart
The TPD12S015, OPA362 and analog and HDMI connectors are now supported
by DRM bridge drivers, and the omapdrm HDMI and VENC outputs can be
handled through the drm_bridge API. Switch the outputs to drm_bridge by
making the next bridge mandatory and removing the related
omapdrm-specific display drivers.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/displays/Kconfig  |  22 --
 drivers/gpu/drm/omapdrm/displays/Makefile |   4 -
 .../omapdrm/displays/connector-analog-tv.c|  97 
 .../gpu/drm/omapdrm/displays/connector-hdmi.c | 183 ---
 .../gpu/drm/omapdrm/displays/encoder-opa362.c | 137 ---
 .../drm/omapdrm/displays/encoder-tpd12s015.c  | 217 --
 drivers/gpu/drm/omapdrm/dss/hdmi4.c   |   4 +-
 drivers/gpu/drm/omapdrm/dss/hdmi5.c   |   4 +-
 .../gpu/drm/omapdrm/dss/omapdss-boot-init.c   |   5 -
 drivers/gpu/drm/omapdrm/dss/output.c  |   5 +
 drivers/gpu/drm/omapdrm/dss/venc.c|   4 +-
 11 files changed, 11 insertions(+), 671 deletions(-)
 delete mode 100644 drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c
 delete mode 100644 drivers/gpu/drm/omapdrm/displays/connector-hdmi.c
 delete mode 100644 drivers/gpu/drm/omapdrm/displays/encoder-opa362.c
 delete mode 100644 drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c

diff --git a/drivers/gpu/drm/omapdrm/displays/Kconfig 
b/drivers/gpu/drm/omapdrm/displays/Kconfig
index b562a8cd61bf..f2be594c7eff 100644
--- a/drivers/gpu/drm/omapdrm/displays/Kconfig
+++ b/drivers/gpu/drm/omapdrm/displays/Kconfig
@@ -1,28 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menu "OMAPDRM External Display Device Drivers"
 
-config DRM_OMAP_ENCODER_OPA362
-   tristate "OPA362 external analog amplifier"
-   help
- Driver for OPA362 external analog TV amplifier controlled
- through a GPIO.
-
-config DRM_OMAP_ENCODER_TPD12S015
-   tristate "TPD12S015 HDMI ESD protection and level shifter"
-   help
- Driver for TPD12S015, which offers HDMI ESD protection and level
- shifting.
-
-config DRM_OMAP_CONNECTOR_HDMI
-   tristate "HDMI Connector"
-   help
- Driver for a generic HDMI connector.
-
-config DRM_OMAP_CONNECTOR_ANALOG_TV
-   tristate "Analog TV Connector"
-   help
- Driver for a generic analog TV connector.
-
 config DRM_OMAP_PANEL_DSI_CM
tristate "Generic DSI Command Mode Panel"
depends on BACKLIGHT_CLASS_DEVICE
diff --git a/drivers/gpu/drm/omapdrm/displays/Makefile 
b/drivers/gpu/drm/omapdrm/displays/Makefile
index cb76859dc574..488ddf153613 100644
--- a/drivers/gpu/drm/omapdrm/displays/Makefile
+++ b/drivers/gpu/drm/omapdrm/displays/Makefile
@@ -1,6 +1,2 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_DRM_OMAP_ENCODER_OPA362) += encoder-opa362.o
-obj-$(CONFIG_DRM_OMAP_ENCODER_TPD12S015) += encoder-tpd12s015.o
-obj-$(CONFIG_DRM_OMAP_CONNECTOR_HDMI) += connector-hdmi.o
-obj-$(CONFIG_DRM_OMAP_CONNECTOR_ANALOG_TV) += connector-analog-tv.o
 obj-$(CONFIG_DRM_OMAP_PANEL_DSI_CM) += panel-dsi-cm.o
diff --git a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c 
b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c
deleted file mode 100644
index f36aa1885d39..
--- a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c
+++ /dev/null
@@ -1,97 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Analog TV Connector driver
- *
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Tomi Valkeinen 
- */
-
-#include 
-#include 
-#include 
-#include 
-
-#include "../dss/omapdss.h"
-
-struct panel_drv_data {
-   struct omap_dss_device dssdev;
-
-   struct device *dev;
-};
-
-#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
-
-static int tvc_connect(struct omap_dss_device *src,
-  struct omap_dss_device *dst)
-{
-   return 0;
-}
-
-static void tvc_disconnect(struct omap_dss_device *src,
-  struct omap_dss_device *dst)
-{
-}
-
-static const struct omap_dss_device_ops tvc_ops = {
-   .connect= tvc_connect,
-   .disconnect = tvc_disconnect,
-};
-
-static int tvc_probe(struct platform_device *pdev)
-{
-   struct panel_drv_data *ddata;
-   struct omap_dss_device *dssdev;
-
-   ddata = devm_kzalloc(>dev, sizeof(*ddata), GFP_KERNEL);
-   if (!ddata)
-   return -ENOMEM;
-
-   platform_set_drvdata(pdev, ddata);
-   ddata->dev = >dev;
-
-   dssdev = >dssdev;
-   dssdev->ops = _ops;
-   dssdev->dev = >dev;
-   dssdev->type = OMAP_DISPLAY_TYPE_VENC;
-   dssdev->display = true;
-   dssdev->owner = THIS_MODULE;
-   dssdev->of_port = 0;
-
-   omapdss_display_init(dssdev);
-   omapdss_device_register(dssdev);
-
-   return 0;
-}
-
-static int __exit tvc_remove(struct platform_device *pdev)
-{
-   struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-
-   

[PATCH v3 19/50] drm/omap: Simplify HDMI mode and infoframe configuration

2019-12-10 Thread Laurent Pinchart
Remove the omap_connector_get_hdmi_mode() function as the HDMI mode can
be accessed directly from the connector's display info.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Tomi Valkeinen 
---
 drivers/gpu/drm/omapdrm/omap_connector.c | 11 ---
 drivers/gpu/drm/omapdrm/omap_connector.h |  1 -
 drivers/gpu/drm/omapdrm/omap_encoder.c   |  4 +---
 3 files changed, 1 insertion(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c 
b/drivers/gpu/drm/omapdrm/omap_connector.c
index 94cded387174..88dbf3fa473f 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -21,7 +21,6 @@ struct omap_connector {
struct drm_connector base;
struct omap_dss_device *output;
struct omap_dss_device *hpd;
-   bool hdmi_mode;
 };
 
 static void omap_connector_hpd_notify(struct drm_connector *connector,
@@ -84,13 +83,6 @@ void omap_connector_disable_hpd(struct drm_connector 
*connector)
hpd->ops->unregister_hpd_cb(hpd);
 }
 
-bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
-{
-   struct omap_connector *omap_connector = to_omap_connector(connector);
-
-   return omap_connector->hdmi_mode;
-}
-
 static struct omap_dss_device *
 omap_connector_find_device(struct drm_connector *connector,
   enum omap_dss_device_ops_flag op)
@@ -167,7 +159,6 @@ static void omap_connector_destroy(struct drm_connector 
*connector)
 static int omap_connector_get_modes_edid(struct drm_connector *connector,
 struct omap_dss_device *dssdev)
 {
-   struct omap_connector *omap_connector = to_omap_connector(connector);
enum drm_connector_status status;
void *edid;
int n;
@@ -189,8 +180,6 @@ static int omap_connector_get_modes_edid(struct 
drm_connector *connector,
drm_connector_update_edid_property(connector, edid);
n = drm_add_edid_modes(connector, edid);
 
-   omap_connector->hdmi_mode = drm_detect_hdmi_monitor(edid);
-
kfree(edid);
return n;
 
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.h 
b/drivers/gpu/drm/omapdrm/omap_connector.h
index 13607bda33d8..4aa5608f4bbe 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.h
+++ b/drivers/gpu/drm/omapdrm/omap_connector.h
@@ -21,7 +21,6 @@ struct omap_dss_device;
 struct drm_connector *omap_connector_init(struct drm_device *dev,
  struct omap_dss_device *output,
  struct drm_encoder *encoder);
-bool omap_connector_get_hdmi_mode(struct drm_connector *connector);
 void omap_connector_enable_hpd(struct drm_connector *connector);
 void omap_connector_disable_hpd(struct drm_connector *connector);
 enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev,
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c 
b/drivers/gpu/drm/omapdrm/omap_encoder.c
index 4f2165a37795..cb5aa01d2f87 100644
--- a/drivers/gpu/drm/omapdrm/omap_encoder.c
+++ b/drivers/gpu/drm/omapdrm/omap_encoder.c
@@ -76,9 +76,7 @@ static void omap_encoder_hdmi_mode_set(struct drm_connector 
*connector,
 {
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
struct omap_dss_device *dssdev = omap_encoder->output;
-   bool hdmi_mode;
-
-   hdmi_mode = omap_connector_get_hdmi_mode(connector);
+   bool hdmi_mode = connector->display_info.is_hdmi;
 
if (dssdev->ops->hdmi.set_hdmi_mode)
dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode);
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 50/50] drm/omap: dss: Remove unused omapdss_of_find_connected_device() function

2019-12-10 Thread Laurent Pinchart
The omapdss_of_find_connected_device() function isn't used anymore,
remove it.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/Makefile  |  2 +-
 drivers/gpu/drm/omapdrm/dss/dss-of.c  | 28 ---
 drivers/gpu/drm/omapdrm/dss/omapdss.h |  3 ---
 3 files changed, 1 insertion(+), 32 deletions(-)
 delete mode 100644 drivers/gpu/drm/omapdrm/dss/dss-of.c

diff --git a/drivers/gpu/drm/omapdrm/dss/Makefile 
b/drivers/gpu/drm/omapdrm/dss/Makefile
index 5950c3f52c2e..f967e6948f2e 100644
--- a/drivers/gpu/drm/omapdrm/dss/Makefile
+++ b/drivers/gpu/drm/omapdrm/dss/Makefile
@@ -2,7 +2,7 @@
 obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o
 
 obj-$(CONFIG_OMAP_DSS_BASE) += omapdss-base.o
-omapdss-base-y := base.o display.o dss-of.o output.o
+omapdss-base-y := base.o display.o output.o
 
 obj-$(CONFIG_OMAP2_DSS) += omapdss.o
 # Core DSS files
diff --git a/drivers/gpu/drm/omapdrm/dss/dss-of.c 
b/drivers/gpu/drm/omapdrm/dss/dss-of.c
deleted file mode 100644
index b7981f3b80ad..
--- a/drivers/gpu/drm/omapdrm/dss/dss-of.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Tomi Valkeinen 
- */
-
-#include 
-#include 
-#include 
-
-#include "omapdss.h"
-
-struct omap_dss_device *
-omapdss_of_find_connected_device(struct device_node *node, unsigned int port)
-{
-   struct device_node *remote_node;
-   struct omap_dss_device *dssdev;
-
-   remote_node = of_graph_get_remote_node(node, port, 0);
-   if (!remote_node)
-   return NULL;
-
-   dssdev = omapdss_find_device_by_node(remote_node);
-   of_node_put(remote_node);
-
-   return dssdev ? dssdev : ERR_PTR(-EPROBE_DEFER);
-}
-EXPORT_SYMBOL_GPL(omapdss_of_find_connected_device);
diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h 
b/drivers/gpu/drm/omapdrm/dss/omapdss.h
index 6ecbc7273032..ab19d4af8de7 100644
--- a/drivers/gpu/drm/omapdrm/dss/omapdss.h
+++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h
@@ -471,9 +471,6 @@ static inline bool omapdss_device_is_enabled(struct 
omap_dss_device *dssdev)
return dssdev->state == OMAP_DSS_DISPLAY_ACTIVE;
 }
 
-struct omap_dss_device *
-omapdss_of_find_connected_device(struct device_node *node, unsigned int port);
-
 enum dss_writeback_channel {
DSS_WB_LCD1_MGR =   0,
DSS_WB_LCD2_MGR =   1,
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 16/50] drm: Add helper to create a connector for a chain of bridges

2019-12-10 Thread Laurent Pinchart
Most bridge drivers create a DRM connector to model the connector at the
output of the bridge. This model is historical and has worked pretty
well so far, but causes several issues:

- It prevents supporting more complex display pipelines where DRM
connector operations are split over multiple components. For instance a
pipeline with a bridge connected to the DDC signals to read EDID data,
and another one connected to the HPD signal to detect connection and
disconnection, will not be possible to support through this model.

- It requires every bridge driver to implement similar connector
handling code, resulting in code duplication.

- It assumes that a bridge will either be wired to a connector or to
another bridge, but doesn't support bridges that can be used in both
positions very well (although there is some ad-hoc support for this in
the analogix_dp bridge driver).

In order to solve these issues, ownership of the connector needs to be
moved to the display controller driver.

To avoid code duplication in display controller drivers, add a new
helper to create and manage a DRM connector backed by a chain of
bridges. All connector operations are delegating to the appropriate
bridge in the chain.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Fixed typo in documentation
- Rebased on top of Boris' drm_bridge chaining rework
- Pass drm_encoder instead of brm_bridge to drm_bridge_connector_init()

Changes since v1:

- Removed the unused MAX_EDID macro
- Removed the unused drm_bridge_connector.hdmi_mode field
- Use drm_connector_init_with_ddc()
---
 drivers/gpu/drm/Makefile   |   3 +-
 drivers/gpu/drm/drm_bridge_connector.c | 373 +
 include/drm/drm_bridge_connector.h |  18 ++
 3 files changed, 393 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/drm_bridge_connector.c
 create mode 100644 include/drm/drm_bridge_connector.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 6493088a0fdd..ec946856a27f 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -39,7 +39,8 @@ obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o
 drm_ttm_helper-y := drm_gem_ttm_helper.o
 obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o
 
-drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o 
drm_probe_helper.o \
+drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o drm_dp_helper.o \
+   drm_dsc.o drm_probe_helper.o \
drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
drm_kms_helper_common.o drm_dp_dual_mode_helper.o \
drm_simple_kms_helper.o drm_modeset_helper.o \
diff --git a/drivers/gpu/drm/drm_bridge_connector.c 
b/drivers/gpu/drm/drm_bridge_connector.c
new file mode 100644
index ..c033a343844f
--- /dev/null
+++ b/drivers/gpu/drm/drm_bridge_connector.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Laurent Pinchart 
+ */
+
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/**
+ * DOC: overview
+ *
+ * The DRM bridge connector helper object provides a DRM connector
+ * implementation that wraps a chain of  drm_bridge. The connector
+ * operations are fully implemented based on the operations of the bridges in
+ * the chain, and don't require any intervention from the display controller
+ * driver at runtime.
+ *
+ * To use the helper, display controller drivers create a bridge connector with
+ * a call to drm_bridge_connector_init(). This associates the newly created
+ * connector with the chain of bridges passed to the function and registers it
+ * with the DRM device. At that point the connector becomes fully usable, no
+ * further operation is needed.
+ *
+ * The DRM bridge connector operations are implemented based on the operations
+ * provided by the bridges in the chain. Each connector operation is delegated
+ * to the bridge closest to the connector (at the end of the chain) that
+ * provides the relevant functionality.
+ *
+ * To make use of this helper, all bridges in the chain shall report bridge
+ * operation flags (_bridge->ops) and bridge output type
+ * (_bridge->type), and none of them may create a DRM connector directly.
+ */
+
+/**
+ * struct drm_bridge_connector - A connector backed by a chain of bridges
+ */
+struct drm_bridge_connector {
+   /**
+* @base: The base DRM connector
+*/
+   struct drm_connector base;
+   /**
+* @encoder:
+*
+* The encoder at the start of the bridges chain.
+*/
+   struct drm_encoder *encoder;
+   /**
+* @bridge_edid:
+*
+* The last bridge in the chain (closest to the connector) that provides
+* EDID read support, if any (see _BRIDGE_OP_EDID).
+*/
+   struct drm_bridge *bridge_edid;
+   /**
+* @bridge_hpd:
+*
+* The last bridge in the 

[PATCH v3 29/50] drm/omap: hdmi5: Register a drm_bridge for EDID read

2019-12-10 Thread Laurent Pinchart
In order to integrate with a chain of drm_bridge, the internal HDMI5
encoder has to expose the EDID read operation through the drm_bridge
API. Register a bridge at initialisation time to do so.

For the time being make the next bridge in the chain optional as the
HDMI output is still based on omap_dss_device. The create_connector
argument to the bridge attach function is also ignored for the same
reason. This will be changed later when removing the related
omapdrm-specific display drivers.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Unregister bridge if output initialisation fails
---
 drivers/gpu/drm/omapdrm/dss/hdmi5.c | 79 ++---
 1 file changed, 73 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index 2b02b0a11696..e7fe2a24a3e1 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -388,8 +388,10 @@ static void hdmi_disconnect(struct omap_dss_device *src,
 
 #define MAX_EDID   512
 
-static struct edid *hdmi_read_edid_data(struct hdmi_core_data *core)
+static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi,
+   struct drm_connector *connector)
 {
+   struct hdmi_core_data *core = >core;
int max_ext_blocks = 3;
int r, n, i;
u8 *edid;
@@ -421,9 +423,12 @@ static struct edid *hdmi_read_edid_data(struct 
hdmi_core_data *core)
return NULL;
 }
 
-static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
+static struct edid *
+hdmi_do_read_edid(struct omap_hdmi *hdmi,
+ struct edid *(*read)(struct omap_hdmi *hdmi,
+  struct drm_connector *connector),
+ struct drm_connector *connector)
 {
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
struct edid *edid;
bool need_enable;
int idlemode;
@@ -447,7 +452,7 @@ static struct edid *hdmi_read_edid(struct omap_dss_device 
*dssdev)
 
hdmi5_core_ddc_init(>core);
 
-   edid = hdmi_read_edid_data(>core);
+   edid = read(hdmi, connector);
 
hdmi5_core_ddc_uninit(>core);
 
@@ -462,6 +467,12 @@ static struct edid *hdmi_read_edid(struct omap_dss_device 
*dssdev)
return (struct edid *)edid;
 }
 
+static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
+{
+   return hdmi_do_read_edid(dssdev_to_hdmi(dssdev), hdmi_read_edid_data,
+NULL);
+}
+
 static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
const struct hdmi_avi_infoframe *avi)
 {
@@ -497,6 +508,56 @@ static const struct omap_dss_device_ops hdmi_ops = {
},
 };
 
+/* 
-
+ * DRM Bridge Operations
+ */
+
+static int hdmi5_bridge_attach(struct drm_bridge *bridge,
+  enum drm_bridge_attach_flags flags)
+{
+   struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
+
+   if (!hdmi->output.next_bridge)
+   return 0;
+
+   return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge,
+bridge, flags);
+}
+
+static struct edid *hdmi5_bridge_read_edid(struct omap_hdmi *hdmi,
+  struct drm_connector *connector)
+{
+   return drm_do_get_edid(connector, hdmi5_core_ddc_read, >core);
+}
+
+static struct edid *hdmi5_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+   struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
+
+   return hdmi_do_read_edid(hdmi, hdmi5_bridge_read_edid, connector);
+}
+
+static const struct drm_bridge_funcs hdmi5_bridge_funcs = {
+   .attach = hdmi5_bridge_attach,
+   .get_edid = hdmi5_bridge_get_edid,
+};
+
+static void hdmi5_bridge_init(struct omap_hdmi *hdmi)
+{
+   hdmi->bridge.funcs = _bridge_funcs;
+   hdmi->bridge.of_node = hdmi->pdev->dev.of_node;
+   hdmi->bridge.ops = DRM_BRIDGE_OP_EDID;
+   hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+
+   drm_bridge_add(>bridge);
+}
+
+static void hdmi5_bridge_cleanup(struct omap_hdmi *hdmi)
+{
+   drm_bridge_remove(>bridge);
+}
+
 /* 
-
  * Audio Callbacks
  */
@@ -679,6 +740,8 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi)
struct omap_dss_device *out = >output;
int r;
 
+   hdmi5_bridge_init(hdmi);
+
out->dev = >pdev->dev;
out->id = OMAP_DSS_OUTPUT_HDMI;
out->type = OMAP_DISPLAY_TYPE_HDMI;
@@ -689,9 +752,11 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi)
out->of_port = 0;
out->ops_flags = OMAP_DSS_DEVICE_OP_EDID;
 
-   r = omapdss_device_init_output(out, NULL);
-   if (r < 0)
+   r = omapdss_device_init_output(out, >bridge);
+   if (r < 0) {

[PATCH v3 23/50] drm/omap: Add infrastructure to support drm_bridge local to DSS outputs

2019-12-10 Thread Laurent Pinchart
In order to support drm_bridge-based pipeline, the internal HDMI
encoders will need to expose the EDID read operation through the
drm_bridge API, and thus to expose a drm_bridge instance corresponding
to the encoder. The HDMI encoders are however handled as omap_dss_device
instances, which conflicts with this requirement.

In order to move forward with the drm_bridge transition, add support for
creating drm_bridge instances local to DSS outputs. If a local bridge is
passed to the omapdss_device_init_output() function, it is used as the
first bridge in the chain, and the omap_dss_device.next_bridge field is
set to the next bridge for the use of the internal encoders' bridges.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Tomi Valkeinen 
---
 drivers/gpu/drm/omapdrm/dss/dpi.c |  2 +-
 drivers/gpu/drm/omapdrm/dss/dsi.c |  2 +-
 drivers/gpu/drm/omapdrm/dss/hdmi4.c   |  2 +-
 drivers/gpu/drm/omapdrm/dss/hdmi5.c   |  2 +-
 drivers/gpu/drm/omapdrm/dss/omapdss.h |  4 +++-
 drivers/gpu/drm/omapdrm/dss/output.c  | 20 
 drivers/gpu/drm/omapdrm/dss/sdi.c |  2 +-
 drivers/gpu/drm/omapdrm/dss/venc.c|  2 +-
 drivers/gpu/drm/omapdrm/omap_drv.c|  2 +-
 9 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c 
b/drivers/gpu/drm/omapdrm/dss/dpi.c
index 462ed6f3118a..2d0eb5fcbb5b 100644
--- a/drivers/gpu/drm/omapdrm/dss/dpi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dpi.c
@@ -629,7 +629,7 @@ static int dpi_init_output_port(struct dpi_data *dpi, 
struct device_node *port)
out->ops = _ops;
out->owner = THIS_MODULE;
 
-   r = omapdss_device_init_output(out);
+   r = omapdss_device_init_output(out, NULL);
if (r < 0)
return r;
 
diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c 
b/drivers/gpu/drm/omapdrm/dss/dsi.c
index 6379eea124d1..79ddfbfd1b58 100644
--- a/drivers/gpu/drm/omapdrm/dss/dsi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dsi.c
@@ -5121,7 +5121,7 @@ static int dsi_init_output(struct dsi_data *dsi)
   | DRM_BUS_FLAG_DE_HIGH
   | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE;
 
-   r = omapdss_device_init_output(out);
+   r = omapdss_device_init_output(out, NULL);
if (r < 0)
return r;
 
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index 44075718407b..dd4a14fe7e59 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -676,7 +676,7 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi)
out->of_port = 0;
out->ops_flags = OMAP_DSS_DEVICE_OP_EDID;
 
-   r = omapdss_device_init_output(out);
+   r = omapdss_device_init_output(out, NULL);
if (r < 0)
return r;
 
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index 1b5bd44ee09d..8e3790dd8b98 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -660,7 +660,7 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi)
out->of_port = 0;
out->ops_flags = OMAP_DSS_DEVICE_OP_EDID;
 
-   r = omapdss_device_init_output(out);
+   r = omapdss_device_init_output(out, NULL);
if (r < 0)
return r;
 
diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h 
b/drivers/gpu/drm/omapdrm/dss/omapdss.h
index b48a51d11310..82e9bfa5530a 100644
--- a/drivers/gpu/drm/omapdrm/dss/omapdss.h
+++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h
@@ -400,6 +400,7 @@ struct omap_dss_device {
struct dss_device *dss;
struct omap_dss_device *next;
struct drm_bridge *bridge;
+   struct drm_bridge *next_bridge;
struct drm_panel *panel;
 
struct list_head list;
@@ -488,7 +489,8 @@ int omap_dss_get_num_overlays(void);
 #define for_each_dss_output(d) \
while ((d = omapdss_device_next_output(d)) != NULL)
 struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device 
*from);
-int omapdss_device_init_output(struct omap_dss_device *out);
+int omapdss_device_init_output(struct omap_dss_device *out,
+  struct drm_bridge *local_bridge);
 void omapdss_device_cleanup_output(struct omap_dss_device *out);
 
 typedef void (*omap_dispc_isr_t) (void *arg, u32 mask);
diff --git a/drivers/gpu/drm/omapdrm/dss/output.c 
b/drivers/gpu/drm/omapdrm/dss/output.c
index c1ec9d343e53..9ba7cc8539a1 100644
--- a/drivers/gpu/drm/omapdrm/dss/output.c
+++ b/drivers/gpu/drm/omapdrm/dss/output.c
@@ -17,7 +17,8 @@
 #include "dss.h"
 #include "omapdss.h"
 
-int omapdss_device_init_output(struct omap_dss_device *out)
+int omapdss_device_init_output(struct omap_dss_device *out,
+  struct drm_bridge *local_bridge)
 {
struct device_node *remote_node;
int ret;
@@ -58,10 +59,20 @@ int omapdss_device_init_output(struct omap_dss_device *out)
out->bridge = bridge;
}
 
-   return out->next || 

[PATCH v3 21/50] drm/omap: Use the drm_panel_bridge API

2019-12-10 Thread Laurent Pinchart
Replace the manual panel handling code by a drm_panel_bridge. This
simplifies the driver and allows all components in the display pipeline
to be treated as bridges, paving the way to generic connector handling.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Tomi Valkeinen 
---
Changes since v1:

- Keep #include 
---
 drivers/gpu/drm/omapdrm/dss/base.c   | 12 -
 drivers/gpu/drm/omapdrm/dss/output.c | 31 +---
 drivers/gpu/drm/omapdrm/omap_connector.c | 10 
 drivers/gpu/drm/omapdrm/omap_drv.c   | 13 --
 drivers/gpu/drm/omapdrm/omap_encoder.c   | 13 --
 5 files changed, 32 insertions(+), 47 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/base.c 
b/drivers/gpu/drm/omapdrm/dss/base.c
index cae5687822e2..80d48936d177 100644
--- a/drivers/gpu/drm/omapdrm/dss/base.c
+++ b/drivers/gpu/drm/omapdrm/dss/base.c
@@ -149,8 +149,7 @@ struct omap_dss_device *omapdss_device_next_output(struct 
omap_dss_device *from)
goto done;
}
 
-   if (dssdev->id &&
-   (dssdev->next || dssdev->bridge || dssdev->panel))
+   if (dssdev->id && (dssdev->next || dssdev->bridge))
goto done;
}
 
@@ -185,11 +184,10 @@ int omapdss_device_connect(struct dss_device *dss,
if (!dst) {
/*
 * The destination is NULL when the source is connected to a
-* bridge or panel instead of a DSS device. Stop here, we will
-* attach the bridge or panel later when we will have a DRM
-* encoder.
+* bridge instead of a DSS device. Stop here, we will attach
+* the bridge later when we will have a DRM encoder.
 */
-   return src && (src->bridge || src->panel) ? 0 : -EINVAL;
+   return src && src->bridge ? 0 : -EINVAL;
}
 
if (omapdss_device_is_connected(dst))
@@ -217,7 +215,7 @@ void omapdss_device_disconnect(struct omap_dss_device *src,
dst ? dev_name(dst->dev) : "NULL");
 
if (!dst) {
-   WARN_ON(!src->bridge && !src->panel);
+   WARN_ON(!src->bridge);
return;
}
 
diff --git a/drivers/gpu/drm/omapdrm/dss/output.c 
b/drivers/gpu/drm/omapdrm/dss/output.c
index 0693d34fca1b..99a253a424c1 100644
--- a/drivers/gpu/drm/omapdrm/dss/output.c
+++ b/drivers/gpu/drm/omapdrm/dss/output.c
@@ -21,6 +21,7 @@
 int omapdss_device_init_output(struct omap_dss_device *out)
 {
struct device_node *remote_node;
+   int ret;
 
remote_node = of_graph_get_remote_node(out->dev->of_node,
   ffs(out->of_ports) - 1, 0);
@@ -39,17 +40,39 @@ int omapdss_device_init_output(struct omap_dss_device *out)
 
if (out->next && out->type != out->next->type) {
dev_err(out->dev, "output type and display type don't match\n");
-   omapdss_device_put(out->next);
-   out->next = NULL;
-   return -EINVAL;
+   ret = -EINVAL;
+   goto error;
}
 
-   return out->next || out->bridge || out->panel ? 0 : -EPROBE_DEFER;
+   if (out->panel) {
+   struct drm_bridge *bridge;
+
+   bridge = drm_panel_bridge_add(out->panel);
+   if (IS_ERR(bridge)) {
+   dev_err(out->dev,
+   "unable to create panel bridge (%ld)\n",
+   PTR_ERR(bridge));
+   ret = PTR_ERR(bridge);
+   goto error;
+   }
+
+   out->bridge = bridge;
+   }
+
+   return out->next || out->bridge ? 0 : -EPROBE_DEFER;
+
+error:
+   omapdss_device_put(out->next);
+   out->next = NULL;
+   return ret;
 }
 EXPORT_SYMBOL(omapdss_device_init_output);
 
 void omapdss_device_cleanup_output(struct omap_dss_device *out)
 {
+   if (out->bridge && out->panel)
+   drm_panel_bridge_remove(out->bridge);
+
if (out->next)
omapdss_device_put(out->next);
 }
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c 
b/drivers/gpu/drm/omapdrm/omap_connector.c
index 38c7a79c5d4a..b0cb2ecb30ab 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -6,7 +6,6 @@
 
 #include 
 #include 
-#include 
 #include 
 
 #include "omap_drv.h"
@@ -190,7 +189,6 @@ static int omap_connector_get_modes_edid(struct 
drm_connector *connector,
 
 static int omap_connector_get_modes(struct drm_connector *connector)
 {
-   struct omap_connector *omap_connector = to_omap_connector(connector);
struct omap_dss_device *dssdev;
 
DBG("%s", connector->name);
@@ -213,14 +211,6 @@ static int omap_connector_get_modes(struct drm_connector 
*connector)
if (dssdev)
return dssdev->ops->get_modes(dssdev, connector);
 
-   

[PATCH v3 32/50] drm/omap: hdmi4: Implement drm_bridge .hpd_notify() operation

2019-12-10 Thread Laurent Pinchart
The HDMI4 encoder is transitioning to the drm_bridge API, implement the
last missing operation.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/hdmi4.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index a5c6054158eb..17759b6a191a 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -551,6 +551,15 @@ static void hdmi4_bridge_disable(struct drm_bridge *bridge,
mutex_unlock(>lock);
 }
 
+static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge,
+   enum drm_connector_status status)
+{
+   struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
+
+   if (status == connector_status_disconnected)
+   hdmi4_cec_set_phys_addr(>core, CEC_PHYS_ADDR_INVALID);
+}
+
 static struct edid *hdmi4_bridge_read_edid(struct omap_hdmi *hdmi,
   struct drm_connector *connector)
 {
@@ -570,6 +579,7 @@ static const struct drm_bridge_funcs hdmi4_bridge_funcs = {
.mode_set = hdmi4_bridge_mode_set,
.atomic_enable = hdmi4_bridge_enable,
.atomic_disable = hdmi4_bridge_disable,
+   .hpd_notify = hdmi4_bridge_hpd_notify,
.get_edid = hdmi4_bridge_get_edid,
 };
 
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 38/50] drm/omap: hdmi: Remove omap_dss_device operations

2019-12-10 Thread Laurent Pinchart
Now that the HDMI outputs are driven fully through the drm_bridge API
their omap_dss_device operations are not used anymore. Remove them.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/hdmi.h  |  1 -
 drivers/gpu/drm/omapdrm/dss/hdmi4.c | 18 --
 drivers/gpu/drm/omapdrm/dss/hdmi5.c | 18 --
 3 files changed, 37 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi.h 
b/drivers/gpu/drm/omapdrm/dss/hdmi.h
index bd43f6abf27b..3a40833d3368 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi.h
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi.h
@@ -380,7 +380,6 @@ struct omap_hdmi {
bool display_enabled;
 };
 
-#define dssdev_to_hdmi(dssdev) container_of(dssdev, struct omap_hdmi, output)
 #define drm_bridge_to_hdmi(b) container_of(b, struct omap_hdmi, bridge)
 
 #endif
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index 7d411f5c4193..34db86ba6193 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -308,18 +308,6 @@ void hdmi4_core_disable(struct hdmi_core_data *core)
mutex_unlock(>lock);
 }
 
-static int hdmi_connect(struct omap_dss_device *src,
-   struct omap_dss_device *dst)
-{
-   return omapdss_device_connect(dst->dss, dst, dst->next);
-}
-
-static void hdmi_disconnect(struct omap_dss_device *src,
-   struct omap_dss_device *dst)
-{
-   omapdss_device_disconnect(dst, dst->next);
-}
-
 static struct edid *
 hdmi_do_read_edid(struct omap_hdmi *hdmi,
  struct edid *(*read)(struct omap_hdmi *hdmi,
@@ -369,11 +357,6 @@ hdmi_do_read_edid(struct omap_hdmi *hdmi,
return edid;
 }
 
-static const struct omap_dss_device_ops hdmi_ops = {
-   .connect= hdmi_connect,
-   .disconnect = hdmi_disconnect,
-};
-
 /* 
-
  * DRM Bridge Operations
  */
@@ -736,7 +719,6 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi)
out->type = OMAP_DISPLAY_TYPE_HDMI;
out->name = "hdmi.0";
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
-   out->ops = _ops;
out->owner = THIS_MODULE;
out->of_port = 0;
 
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index 0490393101fd..69b0d0cc7593 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -306,18 +306,6 @@ static void hdmi_core_disable(struct omap_hdmi *hdmi)
mutex_unlock(>lock);
 }
 
-static int hdmi_connect(struct omap_dss_device *src,
-   struct omap_dss_device *dst)
-{
-   return omapdss_device_connect(dst->dss, dst, dst->next);
-}
-
-static void hdmi_disconnect(struct omap_dss_device *src,
-   struct omap_dss_device *dst)
-{
-   omapdss_device_disconnect(dst, dst->next);
-}
-
 static struct edid *
 hdmi_do_read_edid(struct omap_hdmi *hdmi,
  struct edid *(*read)(struct omap_hdmi *hdmi,
@@ -362,11 +350,6 @@ hdmi_do_read_edid(struct omap_hdmi *hdmi,
return (struct edid *)edid;
 }
 
-static const struct omap_dss_device_ops hdmi_ops = {
-   .connect= hdmi_connect,
-   .disconnect = hdmi_disconnect,
-};
-
 /* 
-
  * DRM Bridge Operations
  */
@@ -710,7 +693,6 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi)
out->type = OMAP_DISPLAY_TYPE_HDMI;
out->name = "hdmi.0";
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
-   out->ops = _ops;
out->owner = THIS_MODULE;
out->of_port = 0;
 
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 35/50] drm/omap: Create connector for bridges

2019-12-10 Thread Laurent Pinchart
Use the drm_bridge_connector helper to create a connector for pipelines
that use drm_bridge. This allows splitting connector operations across
multiple bridges when necessary, instead of having the last bridge in
the chain creating the connector and handling all connector operations
internally.

Signed-off-by: Laurent Pinchart 
---
Changes since v1:

- Squash with patch "drm/omap: Detach from panels at remove time"
---
 drivers/gpu/drm/omapdrm/omap_drv.c | 79 +-
 1 file changed, 67 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c 
b/drivers/gpu/drm/omapdrm/omap_drv.c
index 1df509342b5d..097fbbaa5df0 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -12,10 +12,12 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -291,9 +293,14 @@ static int omap_modeset_init(struct drm_device *dev)
 
if (pipe->output->bridge) {
ret = drm_bridge_attach(pipe->encoder,
-   pipe->output->bridge, NULL, 0);
-   if (ret < 0)
+   pipe->output->bridge, NULL,
+   DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+   if (ret < 0) {
+   dev_err(priv->dev,
+   "unable to attach bridge %pOF\n",
+   pipe->output->bridge->of_node);
return ret;
+   }
}
 
id = omap_display_id(pipe->output);
@@ -329,8 +336,28 @@ static int omap_modeset_init(struct drm_device *dev)
  encoder);
if (!pipe->connector)
return -ENOMEM;
+   } else {
+   pipe->connector = drm_bridge_connector_init(dev, 
encoder);
+   if (IS_ERR(pipe->connector)) {
+   dev_err(priv->dev,
+   "unable to create bridge connector for 
%s\n",
+   pipe->output->name);
+   return PTR_ERR(pipe->connector);
+   }
+   }
 
-   drm_connector_attach_encoder(pipe->connector, encoder);
+   drm_connector_attach_encoder(pipe->connector, encoder);
+
+   /*
+* FIXME: drm_panel should not store the drm_connector pointer
+* internally but should receive it in its .get_modes()
+* operation.
+*/
+   if (pipe->output->panel) {
+   ret = drm_panel_attach(pipe->output->panel,
+  pipe->connector);
+   if (ret < 0)
+   return ret;
}
 
crtc = omap_crtc_init(dev, pipe, priv->planes[i]);
@@ -369,6 +396,23 @@ static int omap_modeset_init(struct drm_device *dev)
return 0;
 }
 
+static void omap_modeset_fini(struct drm_device *ddev)
+{
+   struct omap_drm_private *priv = ddev->dev_private;
+   unsigned int i;
+
+   omap_drm_irq_uninstall(ddev);
+
+   for (i = 0; i < priv->num_pipes; i++) {
+   struct omap_drm_pipeline *pipe = >pipes[i];
+
+   if (pipe->output->panel)
+   drm_panel_detach(pipe->output->panel);
+   }
+
+   drm_mode_config_cleanup(ddev);
+}
+
 /*
  * Enable the HPD in external components if supported
  */
@@ -378,8 +422,15 @@ static void omap_modeset_enable_external_hpd(struct 
drm_device *ddev)
unsigned int i;
 
for (i = 0; i < priv->num_pipes; i++) {
-   if (priv->pipes[i].connector)
-   omap_connector_enable_hpd(priv->pipes[i].connector);
+   struct drm_connector *connector = priv->pipes[i].connector;
+
+   if (!connector)
+   continue;
+
+   if (priv->pipes[i].output->next)
+   omap_connector_enable_hpd(connector);
+   else
+   drm_bridge_connector_enable_hpd(connector);
}
 }
 
@@ -392,8 +443,15 @@ static void omap_modeset_disable_external_hpd(struct 
drm_device *ddev)
unsigned int i;
 
for (i = 0; i < priv->num_pipes; i++) {
-   if (priv->pipes[i].connector)
-   omap_connector_disable_hpd(priv->pipes[i].connector);
+   struct drm_connector *connector = priv->pipes[i].connector;
+
+   if (!connector)
+   continue;
+
+   if (priv->pipes[i].output->next)
+   omap_connector_disable_hpd(connector);
+  

[PATCH v3 26/50] drm/omap: hdmi4: Rework EDID read to isolate data read

2019-12-10 Thread Laurent Pinchart
In preparation of adding DRM bridge support to the hdmi4 encoder code,
rework the EDID read to isolate data read.

The hdmi_read_edid() function is the main entry point. It performs all
initialisation steps required prior to reading the EDID (such as
ensuring the device is powered on), as well as corresponding cleanup
steps afterwards. EDID read itself is handled by hdmi_read_edid_data()
that calls the hdmi4_core_ddc_read() function to read individual blocks.

This new code architecture will allow reusing hdmi_read_edid() and
hdmi4_core_ddc_read() for the drm_bridge EDID read implementation, while
swapping out hdmi_read_edid_data() for the DRM drm_do_get_edid()
function.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Expand commit message
---
 drivers/gpu/drm/omapdrm/dss/hdmi4.c  | 94 +++-
 drivers/gpu/drm/omapdrm/dss/hdmi4_core.c | 59 +++
 drivers/gpu/drm/omapdrm/dss/hdmi4_core.h |  4 +-
 3 files changed, 73 insertions(+), 84 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index e15fa3862922..37536b9f3114 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -272,23 +272,6 @@ static int hdmi_dump_regs(struct seq_file *s, void *p)
return 0;
 }
 
-static int read_edid(struct omap_hdmi *hdmi, u8 *buf, int len)
-{
-   int r;
-
-   mutex_lock(>lock);
-
-   r = hdmi_runtime_get(hdmi);
-   BUG_ON(r);
-
-   r = hdmi4_read_edid(>core,  buf, len);
-
-   hdmi_runtime_put(hdmi);
-   mutex_unlock(>lock);
-
-   return r;
-}
-
 static void hdmi_start_audio_stream(struct omap_hdmi *hd)
 {
hdmi_wp_audio_enable(>wp, true);
@@ -407,10 +390,8 @@ static void hdmi_disconnect(struct omap_dss_device *src,
 
 #define MAX_EDID   512
 
-static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
+static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi)
 {
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-   bool need_enable;
u8 *edid;
int r;
 
@@ -418,32 +399,79 @@ static struct edid *hdmi_read_edid(struct omap_dss_device 
*dssdev)
if (!edid)
return NULL;
 
+   r = hdmi4_core_ddc_read(>core, edid, 0, EDID_LENGTH);
+   if (r)
+   goto error;
+
+   if (edid[0x7e] > 0) {
+   char checksum = 0;
+   unsigned int i;
+
+   r = hdmi4_core_ddc_read(>core, edid + EDID_LENGTH, 1,
+   EDID_LENGTH);
+   if (r)
+   goto error;
+
+   for (i = 0; i < EDID_LENGTH; ++i)
+   checksum += edid[EDID_LENGTH + i];
+
+   if (checksum != 0) {
+   DSSERR("E-EDID checksum failed!!\n");
+   goto error;
+   }
+   }
+
+   return (struct edid *)edid;
+
+error:
+   kfree(edid);
+   return NULL;
+}
+
+static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
+{
+   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
+   struct edid *edid = NULL;
+   unsigned int cec_addr;
+   bool need_enable;
+   int r;
+
need_enable = hdmi->core_enabled == false;
 
if (need_enable) {
r = hdmi4_core_enable(>core);
-   if (r) {
-   kfree(edid);
+   if (r)
return NULL;
-   }
}
 
-   r = read_edid(hdmi, edid, MAX_EDID);
-   if (r < 0) {
-   kfree(edid);
-   edid = NULL;
-   } else {
-   unsigned int cec_addr;
+   mutex_lock(>lock);
+   r = hdmi_runtime_get(hdmi);
+   BUG_ON(r);
+
+   r = hdmi4_core_ddc_init(>core);
+   if (r)
+   goto done;
 
-   cec_addr = r >= 256 ? cec_get_edid_phys_addr(edid, r, NULL)
-: CEC_PHYS_ADDR_INVALID;
-   hdmi4_cec_set_phys_addr(>core, cec_addr);
+   edid = hdmi_read_edid_data(hdmi);
+
+done:
+   hdmi_runtime_put(hdmi);
+   mutex_unlock(>lock);
+
+   if (edid && edid->extensions) {
+   unsigned int len = (edid->extensions + 1) * EDID_LENGTH;
+
+   cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL);
+   } else {
+   cec_addr = CEC_PHYS_ADDR_INVALID;
}
 
+   hdmi4_cec_set_phys_addr(>core, cec_addr);
+
if (need_enable)
hdmi4_core_disable(>core);
 
-   return (struct edid *)edid;
+   return edid;
 }
 
 static void hdmi_lost_hotplug(struct omap_dss_device *dssdev)
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c
index ea5d5c228534..751985a2679a 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c
@@ -32,7 +32,7 @@ static inline void __iomem *hdmi_av_base(struct 
hdmi_core_data *core)
return 

[PATCH v3 44/50] drm/omap: dpi: Simplify clock setting API

2019-12-10 Thread Laurent Pinchart
The dpi_set_pll_clk() and dpi_set_dispc_clk() return various information
through pointer arguments that are never used by the callers. Remove
them to simplify the clock setting API.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/dpi.c | 32 ---
 1 file changed, 8 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c 
b/drivers/gpu/drm/omapdrm/dss/dpi.c
index dccf81e4ce64..c167bd1116ec 100644
--- a/drivers/gpu/drm/omapdrm/dss/dpi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dpi.c
@@ -287,9 +287,7 @@ static bool dpi_dss_clk_calc(struct dpi_data *dpi, unsigned 
long pck,
 
 
 
-static int dpi_set_pll_clk(struct dpi_data *dpi, enum omap_channel channel,
-   unsigned long pck_req, unsigned long *fck, int *lck_div,
-   int *pck_div)
+static int dpi_set_pll_clk(struct dpi_data *dpi, unsigned long pck_req)
 {
struct dpi_clk_calc_ctx ctx;
int r;
@@ -303,19 +301,15 @@ static int dpi_set_pll_clk(struct dpi_data *dpi, enum 
omap_channel channel,
if (r)
return r;
 
-   dss_select_lcd_clk_source(dpi->dss, channel, dpi->clk_src);
+   dss_select_lcd_clk_source(dpi->dss, dpi->output.dispc_channel,
+ dpi->clk_src);
 
dpi->mgr_config.clock_info = ctx.dispc_cinfo;
 
-   *fck = ctx.pll_cinfo.clkout[ctx.clkout_idx];
-   *lck_div = ctx.dispc_cinfo.lck_div;
-   *pck_div = ctx.dispc_cinfo.pck_div;
-
return 0;
 }
 
-static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req,
-   unsigned long *fck, int *lck_div, int *pck_div)
+static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req)
 {
struct dpi_clk_calc_ctx ctx;
int r;
@@ -331,29 +325,19 @@ static int dpi_set_dispc_clk(struct dpi_data *dpi, 
unsigned long pck_req,
 
dpi->mgr_config.clock_info = ctx.dispc_cinfo;
 
-   *fck = ctx.fck;
-   *lck_div = ctx.dispc_cinfo.lck_div;
-   *pck_div = ctx.dispc_cinfo.pck_div;
-
return 0;
 }
 
 static int dpi_set_mode(struct dpi_data *dpi)
 {
-   int lck_div = 0, pck_div = 0;
-   unsigned long fck = 0;
-   int r = 0;
+   int r;
 
if (dpi->pll)
-   r = dpi_set_pll_clk(dpi, dpi->output.dispc_channel,
-   dpi->pixelclock, , _div, _div);
+   r = dpi_set_pll_clk(dpi, dpi->pixelclock);
else
-   r = dpi_set_dispc_clk(dpi, dpi->pixelclock, ,
-   _div, _div);
-   if (r)
-   return r;
+   r = dpi_set_dispc_clk(dpi, dpi->pixelclock);
 
-   return 0;
+   return r;
 }
 
 static void dpi_config_lcd_manager(struct dpi_data *dpi)
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 22/50] drm/omap: dss: Fix output next device lookup in DT

2019-12-10 Thread Laurent Pinchart
The DSS core looks up the next device connected to an output by
traversing the OF graph. It currently hardcodes the local port number to
0, which breaks any output with a different port number (SDI on OMAP3
and any DPI output but the first one). Fix this by repurposing the
currently unused of_ports bitmask in omap_dss_device with an of_port
output port number, and use it to traverse the OF graph.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Tomi Valkeinen 
---
 drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c | 2 +-
 drivers/gpu/drm/omapdrm/displays/connector-hdmi.c  | 2 +-
 drivers/gpu/drm/omapdrm/displays/encoder-opa362.c  | 2 +-
 drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c   | 2 +-
 drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c| 2 +-
 drivers/gpu/drm/omapdrm/dss/dpi.c  | 2 +-
 drivers/gpu/drm/omapdrm/dss/dsi.c  | 2 +-
 drivers/gpu/drm/omapdrm/dss/hdmi4.c| 2 +-
 drivers/gpu/drm/omapdrm/dss/hdmi5.c| 2 +-
 drivers/gpu/drm/omapdrm/dss/omapdss.h  | 4 ++--
 drivers/gpu/drm/omapdrm/dss/output.c   | 3 +--
 drivers/gpu/drm/omapdrm/dss/sdi.c  | 2 +-
 drivers/gpu/drm/omapdrm/dss/venc.c | 2 +-
 13 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c 
b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c
index 0d20fab605d7..f36aa1885d39 100644
--- a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c
+++ b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c
@@ -55,7 +55,7 @@ static int tvc_probe(struct platform_device *pdev)
dssdev->type = OMAP_DISPLAY_TYPE_VENC;
dssdev->display = true;
dssdev->owner = THIS_MODULE;
-   dssdev->of_ports = BIT(0);
+   dssdev->of_port = 0;
 
omapdss_display_init(dssdev);
omapdss_device_register(dssdev);
diff --git a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c 
b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c
index f5d69d810bb8..37c212491cd3 100644
--- a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c
+++ b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c
@@ -139,7 +139,7 @@ static int hdmic_probe(struct platform_device *pdev)
dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
dssdev->display = true;
dssdev->owner = THIS_MODULE;
-   dssdev->of_ports = BIT(0);
+   dssdev->of_port = 0;
dssdev->ops_flags = ddata->hpd_gpio
  ? OMAP_DSS_DEVICE_OP_DETECT | OMAP_DSS_DEVICE_OP_HPD
  : 0;
diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c 
b/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c
index b992387ed674..252705222ef1 100644
--- a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c
+++ b/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c
@@ -86,7 +86,7 @@ static int opa362_probe(struct platform_device *pdev)
dssdev->dev = >dev;
dssdev->type = OMAP_DISPLAY_TYPE_VENC;
dssdev->owner = THIS_MODULE;
-   dssdev->of_ports = BIT(1) | BIT(0);
+   dssdev->of_port = 1;
 
dssdev->next = omapdss_of_find_connected_device(pdev->dev.of_node, 1);
if (IS_ERR(dssdev->next)) {
diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c 
b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c
index 089105c5aa0a..857ae84cd7d1 100644
--- a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c
+++ b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c
@@ -165,7 +165,7 @@ static int tpd_probe(struct platform_device *pdev)
dssdev->dev = >dev;
dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
dssdev->owner = THIS_MODULE;
-   dssdev->of_ports = BIT(1) | BIT(0);
+   dssdev->of_port = 1;
dssdev->ops_flags = OMAP_DSS_DEVICE_OP_DETECT
  | OMAP_DSS_DEVICE_OP_HPD;
 
diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c 
b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c
index 564e3e1a1891..939ea0578f6a 100644
--- a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c
+++ b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c
@@ -1265,7 +1265,7 @@ static int dsicm_probe(struct platform_device *pdev)
dssdev->type = OMAP_DISPLAY_TYPE_DSI;
dssdev->display = true;
dssdev->owner = THIS_MODULE;
-   dssdev->of_ports = BIT(0);
+   dssdev->of_port = 0;
dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
 
dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c 
b/drivers/gpu/drm/omapdrm/dss/dpi.c
index 95147437b990..462ed6f3118a 100644
--- a/drivers/gpu/drm/omapdrm/dss/dpi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dpi.c
@@ -625,7 +625,7 @@ static int dpi_init_output_port(struct dpi_data *dpi, 
struct device_node *port)
out->id = OMAP_DSS_OUTPUT_DPI;
out->type = OMAP_DISPLAY_TYPE_DPI;
out->dispc_channel = dpi_get_channel(dpi);
-   

[PATCH v3 45/50] drm/omap: dpi: Register a drm_bridge

2019-12-10 Thread Laurent Pinchart
In order to integrate with a chain of drm_bridge, the internal DPI
output has to expose its operations through the drm_bridge API.
Register a bridge at initialisation time to do so and remove the
omap_dss_device operations that are now unused.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Unregister bridge if port initialisation fails
---
 drivers/gpu/drm/omapdrm/dss/dpi.c | 197 ++
 1 file changed, 119 insertions(+), 78 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c 
b/drivers/gpu/drm/omapdrm/dss/dpi.c
index c167bd1116ec..e228766f613d 100644
--- a/drivers/gpu/drm/omapdrm/dss/dpi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dpi.c
@@ -21,6 +21,8 @@
 #include 
 #include 
 
+#include 
+
 #include "dss.h"
 #include "omapdss.h"
 
@@ -41,12 +43,10 @@ struct dpi_data {
int data_lines;
 
struct omap_dss_device output;
+   struct drm_bridge bridge;
 };
 
-static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device 
*dssdev)
-{
-   return container_of(dssdev, struct dpi_data, output);
-}
+#define drm_bridge_to_dpi(bridge) container_of(bridge, struct dpi_data, bridge)
 
 /* 
-
  * Clock Handling and PLL
@@ -354,6 +354,32 @@ static void dpi_config_lcd_manager(struct dpi_data *dpi)
dss_mgr_set_lcd_config(>output, >mgr_config);
 }
 
+static int dpi_clock_update(struct dpi_data *dpi, unsigned long *clock)
+{
+   int lck_div, pck_div;
+   unsigned long fck;
+   struct dpi_clk_calc_ctx ctx;
+
+   if (dpi->pll) {
+   if (!dpi_pll_clk_calc(dpi, *clock, ))
+   return -EINVAL;
+
+   fck = ctx.pll_cinfo.clkout[ctx.clkout_idx];
+   } else {
+   if (!dpi_dss_clk_calc(dpi, *clock, ))
+   return -EINVAL;
+
+   fck = ctx.fck;
+   }
+
+   lck_div = ctx.dispc_cinfo.lck_div;
+   pck_div = ctx.dispc_cinfo.pck_div;
+
+   *clock = fck / lck_div / pck_div;
+
+   return 0;
+}
+
 static int dpi_verify_pll(struct dss_pll *pll)
 {
int r;
@@ -391,29 +417,76 @@ static void dpi_init_pll(struct dpi_data *dpi)
 }
 
 /* 
-
- * omap_dss_device Operations
+ * DRM Bridge Operations
  */
 
-static int dpi_connect(struct omap_dss_device *src,
-  struct omap_dss_device *dst)
+static int dpi_bridge_attach(struct drm_bridge *bridge,
+enum drm_bridge_attach_flags flags)
 {
-   struct dpi_data *dpi = dpi_get_data_from_dssdev(dst);
+   struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
+
+   if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
+   return -EINVAL;
 
dpi_init_pll(dpi);
 
-   return omapdss_device_connect(dst->dss, dst, dst->next);
+   return drm_bridge_attach(bridge->encoder, dpi->output.next_bridge,
+bridge, flags);
 }
 
-static void dpi_disconnect(struct omap_dss_device *src,
-  struct omap_dss_device *dst)
+static enum drm_mode_status
+dpi_bridge_mode_valid(struct drm_bridge *bridge,
+  const struct drm_display_mode *mode)
 {
-   omapdss_device_disconnect(dst, dst->next);
+   struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
+   unsigned long clock = mode->clock * 1000;
+   int ret;
+
+   if (mode->hdisplay % 8 != 0)
+   return MODE_BAD_WIDTH;
+
+   if (mode->clock == 0)
+   return MODE_NOCLOCK;
+
+   ret = dpi_clock_update(dpi, );
+   if (ret < 0)
+   return MODE_CLOCK_RANGE;
+
+   return MODE_OK;
 }
 
-static void dpi_display_enable(struct omap_dss_device *dssdev)
+static bool dpi_bridge_mode_fixup(struct drm_bridge *bridge,
+  const struct drm_display_mode *mode,
+  struct drm_display_mode *adjusted_mode)
 {
-   struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
-   struct omap_dss_device *out = >output;
+   struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
+   unsigned long clock = mode->clock * 1000;
+   int ret;
+
+   ret = dpi_clock_update(dpi, );
+   if (ret < 0)
+   return false;
+
+   adjusted_mode->clock = clock / 1000;
+
+   return true;
+}
+
+static void dpi_bridge_mode_set(struct drm_bridge *bridge,
+const struct drm_display_mode *mode,
+const struct drm_display_mode *adjusted_mode)
+{
+   struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
+
+   mutex_lock(>lock);
+   dpi->pixelclock = adjusted_mode->clock * 1000;
+   mutex_unlock(>lock);
+}
+
+static void dpi_bridge_enable(struct drm_bridge *bridge,
+  struct drm_atomic_state *state)
+{
+   struct dpi_data *dpi = drm_bridge_to_dpi(bridge);
int r;
 
  

[PATCH v3 25/50] drm/omap: hdmi: Allocate EDID in the .read_edid() operation

2019-12-10 Thread Laurent Pinchart
Bring the omapdss-specific .read_edid() operation in sync with the
drm_bridge .get_edid() operation to ease code reuse.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Tomi Valkeinen 
---
Changes since v1:

- Keep MAX_EDID macro
---
 drivers/gpu/drm/omapdrm/dss/hdmi4.c  | 36 
 drivers/gpu/drm/omapdrm/dss/hdmi5.c  | 24 
 drivers/gpu/drm/omapdrm/dss/omapdss.h|  2 +-
 drivers/gpu/drm/omapdrm/omap_connector.c | 12 ++--
 4 files changed, 47 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index dd4a14fe7e59..e15fa3862922 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -405,31 +405,45 @@ static void hdmi_disconnect(struct omap_dss_device *src,
omapdss_device_disconnect(dst, dst->next);
 }
 
-static int hdmi_read_edid(struct omap_dss_device *dssdev,
-   u8 *edid, int len)
+#define MAX_EDID   512
+
+static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
 {
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
bool need_enable;
+   u8 *edid;
int r;
 
+   edid = kzalloc(MAX_EDID, GFP_KERNEL);
+   if (!edid)
+   return NULL;
+
need_enable = hdmi->core_enabled == false;
 
if (need_enable) {
r = hdmi4_core_enable(>core);
-   if (r)
-   return r;
+   if (r) {
+   kfree(edid);
+   return NULL;
+   }
+   }
+
+   r = read_edid(hdmi, edid, MAX_EDID);
+   if (r < 0) {
+   kfree(edid);
+   edid = NULL;
+   } else {
+   unsigned int cec_addr;
+
+   cec_addr = r >= 256 ? cec_get_edid_phys_addr(edid, r, NULL)
+: CEC_PHYS_ADDR_INVALID;
+   hdmi4_cec_set_phys_addr(>core, cec_addr);
}
 
-   r = read_edid(hdmi, edid, len);
-   if (r >= 256)
-   hdmi4_cec_set_phys_addr(>core,
-   cec_get_edid_phys_addr(edid, r, NULL));
-   else
-   hdmi4_cec_set_phys_addr(>core, CEC_PHYS_ADDR_INVALID);
if (need_enable)
hdmi4_core_disable(>core);
 
-   return r;
+   return (struct edid *)edid;
 }
 
 static void hdmi_lost_hotplug(struct omap_dss_device *dssdev)
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index 8e3790dd8b98..99720dfc5769 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -410,27 +410,39 @@ static void hdmi_disconnect(struct omap_dss_device *src,
omapdss_device_disconnect(dst, dst->next);
 }
 
-static int hdmi_read_edid(struct omap_dss_device *dssdev,
-   u8 *edid, int len)
+#define MAX_EDID   512
+
+static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
 {
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
bool need_enable;
+   u8 *edid;
int r;
 
+   edid = kzalloc(MAX_EDID, GFP_KERNEL);
+   if (!edid)
+   return NULL;
+
need_enable = hdmi->core_enabled == false;
 
if (need_enable) {
r = hdmi_core_enable(hdmi);
-   if (r)
-   return r;
+   if (r) {
+   kfree(edid);
+   return NULL;
+   }
}
 
-   r = read_edid(hdmi, edid, len);
+   r = read_edid(hdmi, edid, MAX_EDID);
+   if (r < 0) {
+   kfree(edid);
+   edid = NULL;
+   }
 
if (need_enable)
hdmi_core_disable(hdmi);
 
-   return r;
+   return (struct edid *)edid;
 }
 
 static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h 
b/drivers/gpu/drm/omapdrm/dss/omapdss.h
index 82e9bfa5530a..269e143d57be 100644
--- a/drivers/gpu/drm/omapdrm/dss/omapdss.h
+++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h
@@ -367,7 +367,7 @@ struct omap_dss_device_ops {
void *cb_data);
void (*unregister_hpd_cb)(struct omap_dss_device *dssdev);
 
-   int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len);
+   struct edid *(*read_edid)(struct omap_dss_device *dssdev);
 
int (*get_modes)(struct omap_dss_device *dssdev,
 struct drm_connector *connector);
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c 
b/drivers/gpu/drm/omapdrm/omap_connector.c
index a24cec4b0bb9..c636ae228130 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -153,25 +153,19 @@ static void omap_connector_destroy(struct drm_connector 
*connector)
kfree(omap_connector);
 }
 
-#define MAX_EDID  512
-
 static int omap_connector_get_modes_edid(struct drm_connector *connector,
   

[PATCH v3 42/50] drm/omap: dpi: Sort includes alphabetically

2019-12-10 Thread Laurent Pinchart
This makes it easier to quickly locate duplicate includes.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/dpi.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c 
b/drivers/gpu/drm/omapdrm/dss/dpi.c
index 2d0eb5fcbb5b..f8354271ce6f 100644
--- a/drivers/gpu/drm/omapdrm/dss/dpi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dpi.c
@@ -9,20 +9,20 @@
 
 #define DSS_SUBSYS_NAME "DPI"
 
-#include 
+#include 
 #include 
-#include 
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 #include 
 #include 
-#include 
-#include 
 #include 
 
-#include "omapdss.h"
 #include "dss.h"
+#include "omapdss.h"
 
 struct dpi_data {
struct platform_device *pdev;
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 48/50] drm/omap: dss: Remove unused omap_dss_device operations

2019-12-10 Thread Laurent Pinchart
The omap_dss_device .pre_enable(), .post_disable() and .set_timings()
are not used anymore. Remove them.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/base.c | 26 ---
 drivers/gpu/drm/omapdrm/dss/omapdss.h  |  6 
 drivers/gpu/drm/omapdrm/omap_encoder.c | 44 +++---
 3 files changed, 5 insertions(+), 71 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/base.c 
b/drivers/gpu/drm/omapdrm/dss/base.c
index 455b410f7401..c7650a7c155d 100644
--- a/drivers/gpu/drm/omapdrm/dss/base.c
+++ b/drivers/gpu/drm/omapdrm/dss/base.c
@@ -234,18 +234,6 @@ void omapdss_device_disconnect(struct omap_dss_device *src,
 }
 EXPORT_SYMBOL_GPL(omapdss_device_disconnect);
 
-void omapdss_device_pre_enable(struct omap_dss_device *dssdev)
-{
-   if (!dssdev)
-   return;
-
-   omapdss_device_pre_enable(dssdev->next);
-
-   if (dssdev->ops && dssdev->ops->pre_enable)
-   dssdev->ops->pre_enable(dssdev);
-}
-EXPORT_SYMBOL_GPL(omapdss_device_pre_enable);
-
 void omapdss_device_enable(struct omap_dss_device *dssdev)
 {
if (!dssdev)
@@ -272,20 +260,6 @@ void omapdss_device_disable(struct omap_dss_device *dssdev)
 }
 EXPORT_SYMBOL_GPL(omapdss_device_disable);
 
-void omapdss_device_post_disable(struct omap_dss_device *dssdev)
-{
-   if (!dssdev)
-   return;
-
-   if (dssdev->ops && dssdev->ops->post_disable)
-   dssdev->ops->post_disable(dssdev);
-
-   omapdss_device_post_disable(dssdev->next);
-
-   dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
-}
-EXPORT_SYMBOL_GPL(omapdss_device_post_disable);
-
 /* 
-
  * Components Handling
  */
diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h 
b/drivers/gpu/drm/omapdrm/dss/omapdss.h
index 2e5453df2293..64aedc50cb0b 100644
--- a/drivers/gpu/drm/omapdrm/dss/omapdss.h
+++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h
@@ -342,15 +342,11 @@ struct omap_dss_device_ops {
void (*disconnect)(struct omap_dss_device *dssdev,
struct omap_dss_device *dst);
 
-   void (*pre_enable)(struct omap_dss_device *dssdev);
void (*enable)(struct omap_dss_device *dssdev);
void (*disable)(struct omap_dss_device *dssdev);
-   void (*post_disable)(struct omap_dss_device *dssdev);
 
int (*check_timings)(struct omap_dss_device *dssdev,
 struct drm_display_mode *mode);
-   void (*set_timings)(struct omap_dss_device *dssdev,
-   const struct drm_display_mode *mode);
 
int (*get_modes)(struct omap_dss_device *dssdev,
 struct drm_connector *connector);
@@ -450,10 +446,8 @@ int omapdss_device_connect(struct dss_device *dss,
   struct omap_dss_device *dst);
 void omapdss_device_disconnect(struct omap_dss_device *src,
   struct omap_dss_device *dst);
-void omapdss_device_pre_enable(struct omap_dss_device *dssdev);
 void omapdss_device_enable(struct omap_dss_device *dssdev);
 void omapdss_device_disable(struct omap_dss_device *dssdev);
-void omapdss_device_post_disable(struct omap_dss_device *dssdev);
 
 int omap_dss_get_num_overlay_managers(void);
 
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c 
b/drivers/gpu/drm/omapdrm/omap_encoder.c
index 18a79dde6815..ae4b867a67a3 100644
--- a/drivers/gpu/drm/omapdrm/omap_encoder.c
+++ b/drivers/gpu/drm/omapdrm/omap_encoder.c
@@ -113,13 +113,8 @@ static void omap_encoder_mode_set(struct drm_encoder 
*encoder,
bus_flags = connector->display_info.bus_flags;
omap_encoder_update_videomode_flags(, bus_flags);
 
-   /* Set timings for all devices in the display pipeline. */
+   /* Set timings for the dss manager. */
dss_mgr_set_timings(output, );
-
-   for (dssdev = output; dssdev; dssdev = dssdev->next) {
-   if (dssdev->ops && dssdev->ops->set_timings)
-   dssdev->ops->set_timings(dssdev, adjusted_mode);
-   }
 }
 
 static void omap_encoder_disable(struct drm_encoder *encoder)
@@ -132,26 +127,10 @@ static void omap_encoder_disable(struct drm_encoder 
*encoder)
 
/*
 * Disable the chain of external devices, starting at the one at the
-* internal encoder's output.
+* internal encoder's output. This is used for DSI outputs only, as
+* dssdev->next is NULL for all other outputs.
 */
omapdss_device_disable(dssdev->next);
-
-   /*
-* Disable the internal encoder. This will disable the DSS output. The
-* DSI is treated as an exception as DSI pipelines still use the legacy
-* flow where the pipeline output controls the encoder.
-*/
-   if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
-   if (dssdev->ops && dssdev->ops->disable)
-   dssdev->ops->disable(dssdev);
-   dssdev->state = 

[PATCH v3 33/50] drm/omap: dss: Remove .set_hdmi_mode() and .set_infoframe() operations

2019-12-10 Thread Laurent Pinchart
The omapdss_hdmi_ops .set_hdmi_mode() and .set_infoframe() operations
operations are not used anymore, remove them.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/omapdss.h  |  3 ---
 drivers/gpu/drm/omapdrm/omap_encoder.c | 26 --
 2 files changed, 29 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h 
b/drivers/gpu/drm/omapdrm/dss/omapdss.h
index 269e143d57be..30a12cf91cbb 100644
--- a/drivers/gpu/drm/omapdrm/dss/omapdss.h
+++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h
@@ -287,9 +287,6 @@ struct omap_dss_writeback_info {
 
 struct omapdss_hdmi_ops {
void (*lost_hotplug)(struct omap_dss_device *dssdev);
-   int (*set_hdmi_mode)(struct omap_dss_device *dssdev, bool hdmi_mode);
-   int (*set_infoframe)(struct omap_dss_device *dssdev,
-   const struct hdmi_avi_infoframe *avi);
 };
 
 struct omapdss_dsi_ops {
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c 
b/drivers/gpu/drm/omapdrm/omap_encoder.c
index b232acd3bc3d..18a79dde6815 100644
--- a/drivers/gpu/drm/omapdrm/omap_encoder.c
+++ b/drivers/gpu/drm/omapdrm/omap_encoder.c
@@ -69,28 +69,6 @@ static void omap_encoder_update_videomode_flags(struct 
videomode *vm,
}
 }
 
-static void omap_encoder_hdmi_mode_set(struct drm_connector *connector,
-  struct drm_encoder *encoder,
-  struct drm_display_mode *adjusted_mode)
-{
-   struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-   struct omap_dss_device *dssdev = omap_encoder->output;
-   bool hdmi_mode = connector->display_info.is_hdmi;
-
-   if (dssdev->ops && dssdev->ops->hdmi.set_hdmi_mode)
-   dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode);
-
-   if (hdmi_mode && dssdev->ops && dssdev->ops->hdmi.set_infoframe) {
-   struct hdmi_avi_infoframe avi;
-   int r;
-
-   r = drm_hdmi_avi_infoframe_from_display_mode(, connector,
-adjusted_mode);
-   if (r == 0)
-   dssdev->ops->hdmi.set_infoframe(dssdev, );
-   }
-}
-
 static void omap_encoder_mode_set(struct drm_encoder *encoder,
  struct drm_display_mode *mode,
  struct drm_display_mode *adjusted_mode)
@@ -142,10 +120,6 @@ static void omap_encoder_mode_set(struct drm_encoder 
*encoder,
if (dssdev->ops && dssdev->ops->set_timings)
dssdev->ops->set_timings(dssdev, adjusted_mode);
}
-
-   /* Set the HDMI mode and HDMI infoframe if applicable. */
-   if (output->type == OMAP_DISPLAY_TYPE_HDMI)
-   omap_encoder_hdmi_mode_set(connector, encoder, adjusted_mode);
 }
 
 static void omap_encoder_disable(struct drm_encoder *encoder)
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 49/50] drm/omap: dss: Inline the omapdss_display_get() function

2019-12-10 Thread Laurent Pinchart
Inline the omapdss_display_get() in its only caller to simplify the
code.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/display.c | 9 -
 drivers/gpu/drm/omapdrm/dss/omapdss.h | 1 -
 drivers/gpu/drm/omapdrm/omap_drv.c| 7 ---
 3 files changed, 4 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/display.c 
b/drivers/gpu/drm/omapdrm/dss/display.c
index 8a3f61f5825f..3b82158b1bfd 100644
--- a/drivers/gpu/drm/omapdrm/dss/display.c
+++ b/drivers/gpu/drm/omapdrm/dss/display.c
@@ -40,15 +40,6 @@ void omapdss_display_init(struct omap_dss_device *dssdev)
 }
 EXPORT_SYMBOL_GPL(omapdss_display_init);
 
-struct omap_dss_device *omapdss_display_get(struct omap_dss_device *output)
-{
-   while (output->next)
-   output = output->next;
-
-   return omapdss_device_get(output);
-}
-EXPORT_SYMBOL_GPL(omapdss_display_get);
-
 int omapdss_display_get_modes(struct drm_connector *connector,
  const struct videomode *vm)
 {
diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h 
b/drivers/gpu/drm/omapdrm/dss/omapdss.h
index 64aedc50cb0b..6ecbc7273032 100644
--- a/drivers/gpu/drm/omapdrm/dss/omapdss.h
+++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h
@@ -432,7 +432,6 @@ static inline bool omapdss_is_initialized(void)
 }
 
 void omapdss_display_init(struct omap_dss_device *dssdev);
-struct omap_dss_device *omapdss_display_get(struct omap_dss_device *output);
 int omapdss_display_get_modes(struct drm_connector *connector,
  const struct videomode *vm);
 
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c 
b/drivers/gpu/drm/omapdrm/omap_drv.c
index be2430f63630..7931ddaf94e1 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -207,11 +207,12 @@ static int omap_display_id(struct omap_dss_device *output)
struct device_node *node = NULL;
 
if (output->next) {
-   struct omap_dss_device *display;
+   struct omap_dss_device *display = output;
+
+   while (display->next)
+   display = display->next;
 
-   display = omapdss_display_get(output);
node = display->dev->of_node;
-   omapdss_device_put(display);
} else if (output->bridge) {
struct drm_bridge *bridge = output->bridge;
 
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 43/50] drm/omap: dpi: Reorder functions in sections

2019-12-10 Thread Laurent Pinchart
Group functions based on their purpose and split them in sections to
make the source code easier to navigate.

No functional change is included.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/dpi.c | 146 --
 1 file changed, 79 insertions(+), 67 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c 
b/drivers/gpu/drm/omapdrm/dss/dpi.c
index f8354271ce6f..dccf81e4ce64 100644
--- a/drivers/gpu/drm/omapdrm/dss/dpi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dpi.c
@@ -48,6 +48,10 @@ static struct dpi_data *dpi_get_data_from_dssdev(struct 
omap_dss_device *dssdev)
return container_of(dssdev, struct dpi_data, output);
 }
 
+/* 
-
+ * Clock Handling and PLL
+ */
+
 static enum dss_clk_source dpi_get_clk_src_dra7xx(struct dpi_data *dpi,
  enum omap_channel channel)
 {
@@ -366,6 +370,62 @@ static void dpi_config_lcd_manager(struct dpi_data *dpi)
dss_mgr_set_lcd_config(>output, >mgr_config);
 }
 
+static int dpi_verify_pll(struct dss_pll *pll)
+{
+   int r;
+
+   /* do initial setup with the PLL to see if it is operational */
+
+   r = dss_pll_enable(pll);
+   if (r)
+   return r;
+
+   dss_pll_disable(pll);
+
+   return 0;
+}
+
+static void dpi_init_pll(struct dpi_data *dpi)
+{
+   struct dss_pll *pll;
+
+   if (dpi->pll)
+   return;
+
+   dpi->clk_src = dpi_get_clk_src(dpi);
+
+   pll = dss_pll_find_by_src(dpi->dss, dpi->clk_src);
+   if (!pll)
+   return;
+
+   if (dpi_verify_pll(pll)) {
+   DSSWARN("PLL not operational\n");
+   return;
+   }
+
+   dpi->pll = pll;
+}
+
+/* 
-
+ * omap_dss_device Operations
+ */
+
+static int dpi_connect(struct omap_dss_device *src,
+  struct omap_dss_device *dst)
+{
+   struct dpi_data *dpi = dpi_get_data_from_dssdev(dst);
+
+   dpi_init_pll(dpi);
+
+   return omapdss_device_connect(dst->dss, dst, dst->next);
+}
+
+static void dpi_disconnect(struct omap_dss_device *src,
+  struct omap_dss_device *dst)
+{
+   omapdss_device_disconnect(dst, dst->next);
+}
+
 static void dpi_display_enable(struct omap_dss_device *dssdev)
 {
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
@@ -446,20 +506,6 @@ static void dpi_display_disable(struct omap_dss_device 
*dssdev)
mutex_unlock(>lock);
 }
 
-static void dpi_set_timings(struct omap_dss_device *dssdev,
-   const struct drm_display_mode *mode)
-{
-   struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
-
-   DSSDBG("dpi_set_timings\n");
-
-   mutex_lock(>lock);
-
-   dpi->pixelclock = mode->clock * 1000;
-
-   mutex_unlock(>lock);
-}
-
 static int dpi_check_timings(struct omap_dss_device *dssdev,
 struct drm_display_mode *mode)
 {
@@ -500,41 +546,30 @@ static int dpi_check_timings(struct omap_dss_device 
*dssdev,
return 0;
 }
 
-static int dpi_verify_pll(struct dss_pll *pll)
+static void dpi_set_timings(struct omap_dss_device *dssdev,
+   const struct drm_display_mode *mode)
 {
-   int r;
+   struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
 
-   /* do initial setup with the PLL to see if it is operational */
+   DSSDBG("dpi_set_timings\n");
 
-   r = dss_pll_enable(pll);
-   if (r)
-   return r;
+   mutex_lock(>lock);
 
-   dss_pll_disable(pll);
+   dpi->pixelclock = mode->clock * 1000;
 
-   return 0;
+   mutex_unlock(>lock);
 }
 
-static void dpi_init_pll(struct dpi_data *dpi)
-{
-   struct dss_pll *pll;
-
-   if (dpi->pll)
-   return;
-
-   dpi->clk_src = dpi_get_clk_src(dpi);
-
-   pll = dss_pll_find_by_src(dpi->dss, dpi->clk_src);
-   if (!pll)
-   return;
+static const struct omap_dss_device_ops dpi_ops = {
+   .connect = dpi_connect,
+   .disconnect = dpi_disconnect,
 
-   if (dpi_verify_pll(pll)) {
-   DSSWARN("PLL not operational\n");
-   return;
-   }
+   .enable = dpi_display_enable,
+   .disable = dpi_display_disable,
 
-   dpi->pll = pll;
-}
+   .check_timings = dpi_check_timings,
+   .set_timings = dpi_set_timings,
+};
 
 /*
  * Return a hardcoded channel for the DPI output. This should work for
@@ -572,33 +607,6 @@ static enum omap_channel dpi_get_channel(struct dpi_data 
*dpi)
}
 }
 
-static int dpi_connect(struct omap_dss_device *src,
-  struct omap_dss_device *dst)
-{
-   struct dpi_data *dpi = dpi_get_data_from_dssdev(dst);
-
-   dpi_init_pll(dpi);
-
-   return omapdss_device_connect(dst->dss, dst, dst->next);
-}
-
-static void dpi_disconnect(struct 

[PATCH v3 40/50] drm/omap: hdmi4: Simplify EDID read

2019-12-10 Thread Laurent Pinchart
Now that the omap_dss_device EDID read operation has been removed,
simplify the bridge-based EDID access by merging multiple functions
together.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/hdmi4.c | 96 -
 1 file changed, 40 insertions(+), 56 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index 34db86ba6193..aa253d4902fe 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -308,55 +308,6 @@ void hdmi4_core_disable(struct hdmi_core_data *core)
mutex_unlock(>lock);
 }
 
-static struct edid *
-hdmi_do_read_edid(struct omap_hdmi *hdmi,
- struct edid *(*read)(struct omap_hdmi *hdmi,
-  struct drm_connector *connector),
- struct drm_connector *connector)
-{
-   struct edid *edid = NULL;
-   unsigned int cec_addr;
-   bool need_enable;
-   int r;
-
-   need_enable = hdmi->core_enabled == false;
-
-   if (need_enable) {
-   r = hdmi4_core_enable(>core);
-   if (r)
-   return NULL;
-   }
-
-   mutex_lock(>lock);
-   r = hdmi_runtime_get(hdmi);
-   BUG_ON(r);
-
-   r = hdmi4_core_ddc_init(>core);
-   if (r)
-   goto done;
-
-   edid = read(hdmi, connector);
-
-done:
-   hdmi_runtime_put(hdmi);
-   mutex_unlock(>lock);
-
-   if (edid && edid->extensions) {
-   unsigned int len = (edid->extensions + 1) * EDID_LENGTH;
-
-   cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL);
-   } else {
-   cec_addr = CEC_PHYS_ADDR_INVALID;
-   }
-
-   hdmi4_cec_set_phys_addr(>core, cec_addr);
-
-   if (need_enable)
-   hdmi4_core_disable(>core);
-
-   return edid;
-}
-
 /* 
-
  * DRM Bridge Operations
  */
@@ -483,18 +434,51 @@ static void hdmi4_bridge_hpd_notify(struct drm_bridge 
*bridge,
hdmi4_cec_set_phys_addr(>core, CEC_PHYS_ADDR_INVALID);
 }
 
-static struct edid *hdmi4_bridge_read_edid(struct omap_hdmi *hdmi,
-  struct drm_connector *connector)
-{
-   return drm_do_get_edid(connector, hdmi4_core_ddc_read, >core);
-}
-
 static struct edid *hdmi4_bridge_get_edid(struct drm_bridge *bridge,
  struct drm_connector *connector)
 {
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
+   struct edid *edid = NULL;
+   unsigned int cec_addr;
+   bool need_enable;
+   int r;
+
+   need_enable = hdmi->core_enabled == false;
+
+   if (need_enable) {
+   r = hdmi4_core_enable(>core);
+   if (r)
+   return NULL;
+   }
+
+   mutex_lock(>lock);
+   r = hdmi_runtime_get(hdmi);
+   BUG_ON(r);
+
+   r = hdmi4_core_ddc_init(>core);
+   if (r)
+   goto done;
+
+   edid = drm_do_get_edid(connector, hdmi4_core_ddc_read, >core);
 
-   return hdmi_do_read_edid(hdmi, hdmi4_bridge_read_edid, connector);
+done:
+   hdmi_runtime_put(hdmi);
+   mutex_unlock(>lock);
+
+   if (edid && edid->extensions) {
+   unsigned int len = (edid->extensions + 1) * EDID_LENGTH;
+
+   cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL);
+   } else {
+   cec_addr = CEC_PHYS_ADDR_INVALID;
+   }
+
+   hdmi4_cec_set_phys_addr(>core, cec_addr);
+
+   if (need_enable)
+   hdmi4_core_disable(>core);
+
+   return edid;
 }
 
 static const struct drm_bridge_funcs hdmi4_bridge_funcs = {
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 37/50] drm/omap: Remove HPD, detect and EDID omapdss operations

2019-12-10 Thread Laurent Pinchart
Due to the removal of several omapdrm display drivers, the omapdss HPD,
detected and EDID operations are not used anymore. Remove them and all
related code.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/hdmi4.c  |  61 
 drivers/gpu/drm/omapdrm/dss/hdmi5.c  |  46 --
 drivers/gpu/drm/omapdrm/dss/omapdss.h|  25 +--
 drivers/gpu/drm/omapdrm/omap_connector.c | 190 +++
 drivers/gpu/drm/omapdrm/omap_connector.h |   2 -
 drivers/gpu/drm/omapdrm/omap_drv.c   |   8 +-
 6 files changed, 22 insertions(+), 310 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index 6430ec216787..7d411f5c4193 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -320,47 +320,6 @@ static void hdmi_disconnect(struct omap_dss_device *src,
omapdss_device_disconnect(dst, dst->next);
 }
 
-#define MAX_EDID   512
-
-static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi,
-   struct drm_connector *connector)
-{
-   u8 *edid;
-   int r;
-
-   edid = kzalloc(MAX_EDID, GFP_KERNEL);
-   if (!edid)
-   return NULL;
-
-   r = hdmi4_core_ddc_read(>core, edid, 0, EDID_LENGTH);
-   if (r)
-   goto error;
-
-   if (edid[0x7e] > 0) {
-   char checksum = 0;
-   unsigned int i;
-
-   r = hdmi4_core_ddc_read(>core, edid + EDID_LENGTH, 1,
-   EDID_LENGTH);
-   if (r)
-   goto error;
-
-   for (i = 0; i < EDID_LENGTH; ++i)
-   checksum += edid[EDID_LENGTH + i];
-
-   if (checksum != 0) {
-   DSSERR("E-EDID checksum failed!!\n");
-   goto error;
-   }
-   }
-
-   return (struct edid *)edid;
-
-error:
-   kfree(edid);
-   return NULL;
-}
-
 static struct edid *
 hdmi_do_read_edid(struct omap_hdmi *hdmi,
  struct edid *(*read)(struct omap_hdmi *hdmi,
@@ -410,28 +369,9 @@ hdmi_do_read_edid(struct omap_hdmi *hdmi,
return edid;
 }
 
-static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
-{
-   return hdmi_do_read_edid(dssdev_to_hdmi(dssdev), hdmi_read_edid_data,
-NULL);
-}
-
-static void hdmi_lost_hotplug(struct omap_dss_device *dssdev)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-
-   hdmi4_cec_set_phys_addr(>core, CEC_PHYS_ADDR_INVALID);
-}
-
 static const struct omap_dss_device_ops hdmi_ops = {
.connect= hdmi_connect,
.disconnect = hdmi_disconnect,
-
-   .read_edid  = hdmi_read_edid,
-
-   .hdmi = {
-   .lost_hotplug   = hdmi_lost_hotplug,
-   },
 };
 
 /* 
-
@@ -799,7 +739,6 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi)
out->ops = _ops;
out->owner = THIS_MODULE;
out->of_port = 0;
-   out->ops_flags = OMAP_DSS_DEVICE_OP_EDID;
 
r = omapdss_device_init_output(out, >bridge);
if (r < 0) {
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index 4ecec2d71a93..0490393101fd 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -318,43 +318,6 @@ static void hdmi_disconnect(struct omap_dss_device *src,
omapdss_device_disconnect(dst, dst->next);
 }
 
-#define MAX_EDID   512
-
-static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi,
-   struct drm_connector *connector)
-{
-   struct hdmi_core_data *core = >core;
-   int max_ext_blocks = 3;
-   int r, n, i;
-   u8 *edid;
-
-   edid = kzalloc(MAX_EDID, GFP_KERNEL);
-   if (!edid)
-   return NULL;
-
-   r = hdmi5_core_ddc_read(core, edid, 0, EDID_LENGTH);
-   if (r)
-   goto error;
-
-   n = edid[0x7e];
-
-   if (n > max_ext_blocks)
-   n = max_ext_blocks;
-
-   for (i = 1; i <= n; i++) {
-   r = hdmi5_core_ddc_read(core, edid + i * EDID_LENGTH, i,
-   EDID_LENGTH);
-   if (r)
-   goto error;
-   }
-
-   return (struct edid *)edid;
-
-error:
-   kfree(edid);
-   return NULL;
-}
-
 static struct edid *
 hdmi_do_read_edid(struct omap_hdmi *hdmi,
  struct edid *(*read)(struct omap_hdmi *hdmi,
@@ -399,17 +362,9 @@ hdmi_do_read_edid(struct omap_hdmi *hdmi,
return (struct edid *)edid;
 }
 
-static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
-{
-   return hdmi_do_read_edid(dssdev_to_hdmi(dssdev), hdmi_read_edid_data,
-NULL);
-}
-
 static const struct omap_dss_device_ops 

[PATCH v3 31/50] drm/omap: hdmi5: Move mode set, enable and disable operations to bridge

2019-12-10 Thread Laurent Pinchart
Move the omap_dss_device .set_timings(), .enable() and .disable()
operations to the drm_bridge functions. As the drm_bridge for the HDMI
encoder is unconditionally registered and attached, those operations
will be called at the appropriate time.

The omapdss device .set_infoframe() and .set_hdmi_mode() operations have
no equivalent in drm_bridge. Thir content is thus moved to the bridge
.enable() operation as the data they store is not needed before the HDMI
encoder gets enabled.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Detail .set_infoframe() and .set_hdmi_mode() handling in the commit
  message
---
 drivers/gpu/drm/omapdrm/dss/hdmi5.c | 204 +++-
 1 file changed, 106 insertions(+), 98 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index e7fe2a24a3e1..88b637e894fa 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -31,6 +31,8 @@
 #include 
 #include 
 
+#include 
+
 #include "omapdss.h"
 #include "hdmi5_core.h"
 #include "dss.h"
@@ -236,20 +238,6 @@ static void hdmi_power_off_full(struct omap_hdmi *hdmi)
hdmi_power_off_core(hdmi);
 }
 
-static void hdmi_display_set_timings(struct omap_dss_device *dssdev,
-const struct drm_display_mode *mode)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-
-   mutex_lock(>lock);
-
-   drm_display_mode_to_videomode(mode, >cfg.vm);
-
-   dispc_set_tv_pclk(hdmi->dss->dispc, mode->clock * 1000);
-
-   mutex_unlock(>lock);
-}
-
 static int hdmi_dump_regs(struct seq_file *s, void *p)
 {
struct omap_hdmi *hdmi = s->private;
@@ -285,62 +273,6 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
 }
 
-static void hdmi_display_enable(struct omap_dss_device *dssdev)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-   unsigned long flags;
-   int r;
-
-   DSSDBG("ENTER hdmi_display_enable\n");
-
-   mutex_lock(>lock);
-
-   r = hdmi_power_on_full(hdmi);
-   if (r) {
-   DSSERR("failed to power on device\n");
-   goto done;
-   }
-
-   if (hdmi->audio_configured) {
-   r = hdmi5_audio_config(>core, >wp,
-  >audio_config,
-  hdmi->cfg.vm.pixelclock);
-   if (r) {
-   DSSERR("Error restoring audio configuration: %d", r);
-   hdmi->audio_abort_cb(>pdev->dev);
-   hdmi->audio_configured = false;
-   }
-   }
-
-   spin_lock_irqsave(>audio_playing_lock, flags);
-   if (hdmi->audio_configured && hdmi->audio_playing)
-   hdmi_start_audio_stream(hdmi);
-   hdmi->display_enabled = true;
-   spin_unlock_irqrestore(>audio_playing_lock, flags);
-
-done:
-   mutex_unlock(>lock);
-}
-
-static void hdmi_display_disable(struct omap_dss_device *dssdev)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-   unsigned long flags;
-
-   DSSDBG("Enter hdmi_display_disable\n");
-
-   mutex_lock(>lock);
-
-   spin_lock_irqsave(>audio_playing_lock, flags);
-   hdmi_stop_audio_stream(hdmi);
-   hdmi->display_enabled = false;
-   spin_unlock_irqrestore(>audio_playing_lock, flags);
-
-   hdmi_power_off_full(hdmi);
-
-   mutex_unlock(>lock);
-}
-
 static int hdmi_core_enable(struct omap_hdmi *hdmi)
 {
int r = 0;
@@ -473,39 +405,11 @@ static struct edid *hdmi_read_edid(struct omap_dss_device 
*dssdev)
 NULL);
 }
 
-static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
-   const struct hdmi_avi_infoframe *avi)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-
-   hdmi->cfg.infoframe = *avi;
-   return 0;
-}
-
-static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
-   bool hdmi_mode)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-
-   hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
-   return 0;
-}
-
 static const struct omap_dss_device_ops hdmi_ops = {
.connect= hdmi_connect,
.disconnect = hdmi_disconnect,
 
-   .enable = hdmi_display_enable,
-   .disable= hdmi_display_disable,
-
-   .set_timings= hdmi_display_set_timings,
-
.read_edid  = hdmi_read_edid,
-
-   .hdmi = {
-   .set_infoframe  = hdmi_set_infoframe,
-   .set_hdmi_mode  = hdmi_set_hdmi_mode,
-   },
 };
 
 /* 
-
@@ -524,6 +428,107 @@ static int hdmi5_bridge_attach(struct drm_bridge *bridge,
 bridge, flags);
 }
 
+static void 

[PATCH v3 27/50] drm/omap: hdmi5: Rework EDID read to isolate data read

2019-12-10 Thread Laurent Pinchart
In preparation of adding DRM bridge support to the hdmi5 encoder code,
rework the EDID read to isolate data read.

The hdmi_read_edid() function is the main entry point. It performs all
initialisation steps required prior to reading the EDID (such as
ensuring the device is powered on), as well as corresponding cleanup
steps afterwards. EDID read itself is handled by hdmi_read_edid_data()
that calls the hdmi5_core_ddc_read() function to read individual blocks.

This new code architecture will allow reusing hdmi_read_edid() and
hdmi5_core_ddc_read() for the drm_bridge EDID read implementation, while
swapping out hdmi_read_edid_data() for the DRM drm_do_get_edid()
function.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Expand commit message
---
 drivers/gpu/drm/omapdrm/dss/hdmi5.c  | 89 ++--
 drivers/gpu/drm/omapdrm/dss/hdmi5_core.c | 48 +++--
 drivers/gpu/drm/omapdrm/dss/hdmi5_core.h |  5 +-
 3 files changed, 65 insertions(+), 77 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index 99720dfc5769..2b02b0a11696 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -271,30 +271,6 @@ static int hdmi_dump_regs(struct seq_file *s, void *p)
return 0;
 }
 
-static int read_edid(struct omap_hdmi *hdmi, u8 *buf, int len)
-{
-   int r;
-   int idlemode;
-
-   mutex_lock(>lock);
-
-   r = hdmi_runtime_get(hdmi);
-   BUG_ON(r);
-
-   idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2);
-   /* No-idle mode */
-   REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
-
-   r = hdmi5_read_edid(>core,  buf, len);
-
-   REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
-
-   hdmi_runtime_put(hdmi);
-   mutex_unlock(>lock);
-
-   return r;
-}
-
 static void hdmi_start_audio_stream(struct omap_hdmi *hd)
 {
REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
@@ -412,32 +388,73 @@ static void hdmi_disconnect(struct omap_dss_device *src,
 
 #define MAX_EDID   512
 
-static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
+static struct edid *hdmi_read_edid_data(struct hdmi_core_data *core)
 {
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-   bool need_enable;
+   int max_ext_blocks = 3;
+   int r, n, i;
u8 *edid;
-   int r;
 
edid = kzalloc(MAX_EDID, GFP_KERNEL);
if (!edid)
return NULL;
 
+   r = hdmi5_core_ddc_read(core, edid, 0, EDID_LENGTH);
+   if (r)
+   goto error;
+
+   n = edid[0x7e];
+
+   if (n > max_ext_blocks)
+   n = max_ext_blocks;
+
+   for (i = 1; i <= n; i++) {
+   r = hdmi5_core_ddc_read(core, edid + i * EDID_LENGTH, i,
+   EDID_LENGTH);
+   if (r)
+   goto error;
+   }
+
+   return (struct edid *)edid;
+
+error:
+   kfree(edid);
+   return NULL;
+}
+
+static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
+{
+   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
+   struct edid *edid;
+   bool need_enable;
+   int idlemode;
+   int r;
+
need_enable = hdmi->core_enabled == false;
 
if (need_enable) {
r = hdmi_core_enable(hdmi);
-   if (r) {
-   kfree(edid);
+   if (r)
return NULL;
-   }
}
 
-   r = read_edid(hdmi, edid, MAX_EDID);
-   if (r < 0) {
-   kfree(edid);
-   edid = NULL;
-   }
+   mutex_lock(>lock);
+   r = hdmi_runtime_get(hdmi);
+   BUG_ON(r);
+
+   idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2);
+   /* No-idle mode */
+   REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
+
+   hdmi5_core_ddc_init(>core);
+
+   edid = hdmi_read_edid_data(>core);
+
+   hdmi5_core_ddc_uninit(>core);
+
+   REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
+
+   hdmi_runtime_put(hdmi);
+   mutex_unlock(>lock);
 
if (need_enable)
hdmi_core_disable(hdmi);
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c
index ff4d35c8771f..7dd587035160 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c
@@ -23,7 +23,7 @@
 
 #include "hdmi5_core.h"
 
-static void hdmi_core_ddc_init(struct hdmi_core_data *core)
+void hdmi5_core_ddc_init(struct hdmi_core_data *core)
 {
void __iomem *base = core->base;
const unsigned long long iclk = 26600;  /* DSS L3 ICLK */
@@ -102,7 +102,7 @@ static void hdmi_core_ddc_init(struct hdmi_core_data *core)
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x0, 2, 2);
 }
 
-static void hdmi_core_ddc_uninit(struct hdmi_core_data *core)
+void hdmi5_core_ddc_uninit(struct 

[PATCH v3 18/50] drm/omap: dss: Cleanup DSS ports on initialisation failure

2019-12-10 Thread Laurent Pinchart
When the DSS initialises its output DPI and SDI ports, failures don't
clean up previous successfully initialised ports. This can lead to
resource leak or memory corruption. Fix it.

Reported-by: Hans Verkuil 
Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/dss.c | 43 +++
 1 file changed, 26 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c 
b/drivers/gpu/drm/omapdrm/dss/dss.c
index 225ec808b01a..67b92b5d8dd7 100644
--- a/drivers/gpu/drm/omapdrm/dss/dss.c
+++ b/drivers/gpu/drm/omapdrm/dss/dss.c
@@ -1151,46 +1151,38 @@ static const struct dss_features dra7xx_dss_feats = {
.has_lcd_clk_src=   true,
 };
 
-static int dss_init_ports(struct dss_device *dss)
+static void __dss_uninit_ports(struct dss_device *dss, unsigned int num_ports)
 {
struct platform_device *pdev = dss->pdev;
struct device_node *parent = pdev->dev.of_node;
struct device_node *port;
unsigned int i;
-   int r;
 
-   for (i = 0; i < dss->feat->num_ports; i++) {
+   for (i = 0; i < num_ports; i++) {
port = of_graph_get_port_by_id(parent, i);
if (!port)
continue;
 
switch (dss->feat->ports[i]) {
case OMAP_DISPLAY_TYPE_DPI:
-   r = dpi_init_port(dss, pdev, port, dss->feat->model);
-   if (r)
-   return r;
+   dpi_uninit_port(port);
break;
-
case OMAP_DISPLAY_TYPE_SDI:
-   r = sdi_init_port(dss, pdev, port);
-   if (r)
-   return r;
+   sdi_uninit_port(port);
break;
-
default:
break;
}
}
-
-   return 0;
 }
 
-static void dss_uninit_ports(struct dss_device *dss)
+static int dss_init_ports(struct dss_device *dss)
 {
struct platform_device *pdev = dss->pdev;
struct device_node *parent = pdev->dev.of_node;
struct device_node *port;
-   int i;
+   unsigned int i;
+   int r;
 
for (i = 0; i < dss->feat->num_ports; i++) {
port = of_graph_get_port_by_id(parent, i);
@@ -1199,15 +1191,32 @@ static void dss_uninit_ports(struct dss_device *dss)
 
switch (dss->feat->ports[i]) {
case OMAP_DISPLAY_TYPE_DPI:
-   dpi_uninit_port(port);
+   r = dpi_init_port(dss, pdev, port, dss->feat->model);
+   if (r)
+   goto error;
break;
+
case OMAP_DISPLAY_TYPE_SDI:
-   sdi_uninit_port(port);
+   r = sdi_init_port(dss, pdev, port);
+   if (r)
+   goto error;
break;
+
default:
break;
}
}
+
+   return 0;
+
+error:
+   __dss_uninit_ports(dss, i);
+   return r;
+}
+
+static void dss_uninit_ports(struct dss_device *dss)
+{
+   __dss_uninit_ports(dss, dss->feat->num_ports);
 }
 
 static int dss_video_pll_probe(struct dss_device *dss)
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 41/50] drm/omap: hdmi5: Simplify EDID read

2019-12-10 Thread Laurent Pinchart
Now that the omap_dss_device EDID read operation has been removed,
simplify the bridge-based EDID access by merging multiple functions
together.

Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/hdmi5.c | 86 -
 1 file changed, 35 insertions(+), 51 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index 69b0d0cc7593..6f284581d35c 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -306,50 +306,6 @@ static void hdmi_core_disable(struct omap_hdmi *hdmi)
mutex_unlock(>lock);
 }
 
-static struct edid *
-hdmi_do_read_edid(struct omap_hdmi *hdmi,
- struct edid *(*read)(struct omap_hdmi *hdmi,
-  struct drm_connector *connector),
- struct drm_connector *connector)
-{
-   struct edid *edid;
-   bool need_enable;
-   int idlemode;
-   int r;
-
-   need_enable = hdmi->core_enabled == false;
-
-   if (need_enable) {
-   r = hdmi_core_enable(hdmi);
-   if (r)
-   return NULL;
-   }
-
-   mutex_lock(>lock);
-   r = hdmi_runtime_get(hdmi);
-   BUG_ON(r);
-
-   idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2);
-   /* No-idle mode */
-   REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
-
-   hdmi5_core_ddc_init(>core);
-
-   edid = read(hdmi, connector);
-
-   hdmi5_core_ddc_uninit(>core);
-
-   REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
-
-   hdmi_runtime_put(hdmi);
-   mutex_unlock(>lock);
-
-   if (need_enable)
-   hdmi_core_disable(hdmi);
-
-   return (struct edid *)edid;
-}
-
 /* 
-
  * DRM Bridge Operations
  */
@@ -467,18 +423,46 @@ static void hdmi5_bridge_disable(struct drm_bridge 
*bridge,
mutex_unlock(>lock);
 }
 
-static struct edid *hdmi5_bridge_read_edid(struct omap_hdmi *hdmi,
-  struct drm_connector *connector)
-{
-   return drm_do_get_edid(connector, hdmi5_core_ddc_read, >core);
-}
-
 static struct edid *hdmi5_bridge_get_edid(struct drm_bridge *bridge,
  struct drm_connector *connector)
 {
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
+   struct edid *edid;
+   bool need_enable;
+   int idlemode;
+   int r;
+
+   need_enable = hdmi->core_enabled == false;
+
+   if (need_enable) {
+   r = hdmi_core_enable(hdmi);
+   if (r)
+   return NULL;
+   }
+
+   mutex_lock(>lock);
+   r = hdmi_runtime_get(hdmi);
+   BUG_ON(r);
 
-   return hdmi_do_read_edid(hdmi, hdmi5_bridge_read_edid, connector);
+   idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2);
+   /* No-idle mode */
+   REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
+
+   hdmi5_core_ddc_init(>core);
+
+   edid = drm_do_get_edid(connector, hdmi5_core_ddc_read, >core);
+
+   hdmi5_core_ddc_uninit(>core);
+
+   REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
+
+   hdmi_runtime_put(hdmi);
+   mutex_unlock(>lock);
+
+   if (need_enable)
+   hdmi_core_disable(hdmi);
+
+   return (struct edid *)edid;
 }
 
 static const struct drm_bridge_funcs hdmi5_bridge_funcs = {
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 34/50] drm/omap: venc: Register a drm_bridge

2019-12-10 Thread Laurent Pinchart
In order to integrate with a chain of drm_bridge, the internal VENC
encoder has to expose the mode valid, fixup and set, the enable and
disable and the get modes operations through the drm_bridge API.
Register a bridge at initialisation time to do so.

Most of those operations are removed from the omap_dss_device as they
are now called through the drm_bridge API by the DRM atomic helpers. The
only exception is the .get_modes() operation that is still invoked
through the omap_dss_device-based pipeline.

For the time being make the next bridge in the chain optional as the
VENC output is still based on omap_dss_device. The create_connector
argument to the bridge attach function is also ignored for the same
reason. This will be changed later when removing the related
omapdrm-specific display drivers.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Unregister bridge if output initialisation fails
---
 drivers/gpu/drm/omapdrm/dss/venc.c | 247 +++--
 1 file changed, 163 insertions(+), 84 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c 
b/drivers/gpu/drm/omapdrm/dss/venc.c
index 977d8d525b43..c2e2141c8375 100644
--- a/drivers/gpu/drm/omapdrm/dss/venc.c
+++ b/drivers/gpu/drm/omapdrm/dss/venc.c
@@ -26,6 +26,8 @@
 #include 
 #include 
 
+#include 
+
 #include "omapdss.h"
 #include "dss.h"
 
@@ -303,9 +305,11 @@ struct venc_device {
bool requires_tv_dac_clk;
 
struct omap_dss_device output;
+   struct drm_bridge bridge;
 };
 
 #define dssdev_to_venc(dssdev) container_of(dssdev, struct venc_device, output)
+#define drm_bridge_to_venc(b) container_of(b, struct venc_device, bridge)
 
 static inline void venc_write_reg(struct venc_device *venc, int idx, u32 val)
 {
@@ -477,32 +481,6 @@ static void venc_power_off(struct venc_device *venc)
venc_runtime_put(venc);
 }
 
-static void venc_display_enable(struct omap_dss_device *dssdev)
-{
-   struct venc_device *venc = dssdev_to_venc(dssdev);
-
-   DSSDBG("venc_display_enable\n");
-
-   mutex_lock(>venc_lock);
-
-   venc_power_on(venc);
-
-   mutex_unlock(>venc_lock);
-}
-
-static void venc_display_disable(struct omap_dss_device *dssdev)
-{
-   struct venc_device *venc = dssdev_to_venc(dssdev);
-
-   DSSDBG("venc_display_disable\n");
-
-   mutex_lock(>venc_lock);
-
-   venc_power_off(venc);
-
-   mutex_unlock(>venc_lock);
-}
-
 static int venc_get_modes(struct omap_dss_device *dssdev,
  struct drm_connector *connector)
 {
@@ -545,57 +523,6 @@ static enum venc_videomode venc_get_videomode(const struct 
drm_display_mode *mod
return VENC_MODE_UNKNOWN;
 }
 
-static void venc_set_timings(struct omap_dss_device *dssdev,
-const struct drm_display_mode *mode)
-{
-   struct venc_device *venc = dssdev_to_venc(dssdev);
-   enum venc_videomode venc_mode = venc_get_videomode(mode);
-
-   DSSDBG("venc_set_timings\n");
-
-   mutex_lock(>venc_lock);
-
-   switch (venc_mode) {
-   default:
-   WARN_ON_ONCE(1);
-   /* Fall-through */
-   case VENC_MODE_PAL:
-   venc->config = _config_pal_trm;
-   break;
-
-   case VENC_MODE_NTSC:
-   venc->config = _config_ntsc_trm;
-   break;
-   }
-
-   dispc_set_tv_pclk(venc->dss->dispc, 1350);
-
-   mutex_unlock(>venc_lock);
-}
-
-static int venc_check_timings(struct omap_dss_device *dssdev,
- struct drm_display_mode *mode)
-{
-   DSSDBG("venc_check_timings\n");
-
-   switch (venc_get_videomode(mode)) {
-   case VENC_MODE_PAL:
-   drm_mode_copy(mode, _dss_pal_mode);
-   break;
-
-   case VENC_MODE_NTSC:
-   drm_mode_copy(mode, _dss_ntsc_mode);
-   break;
-
-   default:
-   return -EINVAL;
-   }
-
-   drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
-   drm_mode_set_name(mode);
-   return 0;
-}
-
 static int venc_dump_regs(struct seq_file *s, void *p)
 {
struct venc_device *venc = s->private;
@@ -689,15 +616,161 @@ static const struct omap_dss_device_ops venc_ops = {
.connect = venc_connect,
.disconnect = venc_disconnect,
 
-   .enable = venc_display_enable,
-   .disable = venc_display_disable,
+   .get_modes = venc_get_modes,
+};
+
+/* 
-
+ * DRM Bridge Operations
+ */
 
-   .check_timings = venc_check_timings,
-   .set_timings = venc_set_timings,
+static int venc_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+   struct venc_device *venc = drm_bridge_to_venc(bridge);
 
-   .get_modes = venc_get_modes,
+   if (venc->output.next_bridge)
+   return 0;
+
+   return drm_bridge_attach(bridge->encoder, venc->output.next_bridge,
+   

[PATCH v3 20/50] drm/omap: Factor out display type to connector type conversion

2019-12-10 Thread Laurent Pinchart
Move the code that computes the DRM connector type for the
omapdss_device display type to a new omapdss_device_connector_type()
function for later reuse.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Tomi Valkeinen 
---
 drivers/gpu/drm/omapdrm/dss/base.c   | 23 +++
 drivers/gpu/drm/omapdrm/dss/omapdss.h|  1 +
 drivers/gpu/drm/omapdrm/omap_connector.c | 19 +--
 3 files changed, 25 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/base.c 
b/drivers/gpu/drm/omapdrm/dss/base.c
index a1970b9db6ab..cae5687822e2 100644
--- a/drivers/gpu/drm/omapdrm/dss/base.c
+++ b/drivers/gpu/drm/omapdrm/dss/base.c
@@ -285,6 +285,29 @@ void omapdss_device_post_disable(struct omap_dss_device 
*dssdev)
 }
 EXPORT_SYMBOL_GPL(omapdss_device_post_disable);
 
+unsigned int omapdss_device_connector_type(enum omap_display_type type)
+{
+   switch (type) {
+   case OMAP_DISPLAY_TYPE_HDMI:
+   return DRM_MODE_CONNECTOR_HDMIA;
+   case OMAP_DISPLAY_TYPE_DVI:
+   return DRM_MODE_CONNECTOR_DVID;
+   case OMAP_DISPLAY_TYPE_DSI:
+   return DRM_MODE_CONNECTOR_DSI;
+   case OMAP_DISPLAY_TYPE_DPI:
+   case OMAP_DISPLAY_TYPE_DBI:
+   return DRM_MODE_CONNECTOR_DPI;
+   case OMAP_DISPLAY_TYPE_VENC:
+   /* TODO: This could also be composite */
+   return DRM_MODE_CONNECTOR_SVIDEO;
+   case OMAP_DISPLAY_TYPE_SDI:
+   return DRM_MODE_CONNECTOR_LVDS;
+   default:
+   return DRM_MODE_CONNECTOR_Unknown;
+   }
+}
+EXPORT_SYMBOL_GPL(omapdss_device_connector_type);
+
 /* 
-
  * Components Handling
  */
diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h 
b/drivers/gpu/drm/omapdrm/dss/omapdss.h
index 79f6b195c7cf..c5672e5174c5 100644
--- a/drivers/gpu/drm/omapdrm/dss/omapdss.h
+++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h
@@ -479,6 +479,7 @@ void omapdss_device_pre_enable(struct omap_dss_device 
*dssdev);
 void omapdss_device_enable(struct omap_dss_device *dssdev);
 void omapdss_device_disable(struct omap_dss_device *dssdev);
 void omapdss_device_post_disable(struct omap_dss_device *dssdev);
+unsigned int omapdss_device_connector_type(enum omap_display_type type);
 
 int omap_dss_get_num_overlay_managers(void);
 
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c 
b/drivers/gpu/drm/omapdrm/omap_connector.c
index 88dbf3fa473f..38c7a79c5d4a 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -296,24 +296,7 @@ static int omap_connector_get_type(struct omap_dss_device 
*output)
type = display->type;
omapdss_device_put(display);
 
-   switch (type) {
-   case OMAP_DISPLAY_TYPE_HDMI:
-   return DRM_MODE_CONNECTOR_HDMIA;
-   case OMAP_DISPLAY_TYPE_DVI:
-   return DRM_MODE_CONNECTOR_DVID;
-   case OMAP_DISPLAY_TYPE_DSI:
-   return DRM_MODE_CONNECTOR_DSI;
-   case OMAP_DISPLAY_TYPE_DPI:
-   case OMAP_DISPLAY_TYPE_DBI:
-   return DRM_MODE_CONNECTOR_DPI;
-   case OMAP_DISPLAY_TYPE_VENC:
-   /* TODO: This could also be composite */
-   return DRM_MODE_CONNECTOR_SVIDEO;
-   case OMAP_DISPLAY_TYPE_SDI:
-   return DRM_MODE_CONNECTOR_LVDS;
-   default:
-   return DRM_MODE_CONNECTOR_Unknown;
-   }
+   return omapdss_device_connector_type(type);
 }
 
 /* initialize connector */
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 15/50] drm/bridge: tfp410: Allow operation without drm_connector

2019-12-10 Thread Laurent Pinchart
The tfp410 driver can operate as part of a pipeline where the
drm_connector is created by the display controller. Enable this mode of
operation by skipping creation of a drm_connector internally.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Boris Brezillon 
---
 drivers/gpu/drm/bridge/ti-tfp410.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c 
b/drivers/gpu/drm/bridge/ti-tfp410.c
index 9d7a546e5d4d..e5bb7774123e 100644
--- a/drivers/gpu/drm/bridge/ti-tfp410.c
+++ b/drivers/gpu/drm/bridge/ti-tfp410.c
@@ -125,7 +125,7 @@ static int tfp410_attach(struct drm_bridge *bridge,
return ret;
 
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
-   return -EINVAL;
+   return 0;
 
if (!bridge->encoder) {
dev_err(dvi->dev, "Missing encoder\n");
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 05/50] drm/bridge: Extend bridge API to disable connector creation

2019-12-10 Thread Laurent Pinchart
Most bridge drivers create a DRM connector to model the connector at the
output of the bridge. This model is historical and has worked pretty
well so far, but causes several issues:

- It prevents supporting more complex display pipelines where DRM
connector operations are split over multiple components. For instance a
pipeline with a bridge connected to the DDC signals to read EDID data,
and another one connected to the HPD signal to detect connection and
disconnection, will not be possible to support through this model.

- It requires every bridge driver to implement similar connector
handling code, resulting in code duplication.

- It assumes that a bridge will either be wired to a connector or to
another bridge, but doesn't support bridges that can be used in both
positions very well (although there is some ad-hoc support for this in
the analogix_dp bridge driver).

In order to solve these issues, ownership of the connector should be
moved to the display controller driver (where it can be implemented
using helpers provided by the core).

Extend the bridge API to allow disabling connector creation in bridge
drivers as a first step towards the new model. The new flags argument to
the bridge .attach() operation allows instructing the bridge driver to
skip creating a connector. Unconditionally set the new flags argument to
0 for now to keep the existing behaviour, and modify all existing bridge
drivers to return an error when connector creation is not requested as
they don't support this feature yet.

The change is based on the following semantic patch, with manual review
and edits.

@ rule1 @
identifier funcs;
identifier fn;
@@
 struct drm_bridge_funcs funcs = {
...,
.attach = fn
 };

@ depends on rule1 @
identifier rule1.fn;
identifier bridge;
statement S, S1;
@@
 int fn(
struct drm_bridge *bridge
+   , enum drm_bridge_attach_flags flags
 )
 {
... when != S
+   if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+   return -EINVAL;
+
S1
...
 }

@@
expression E1, E2, E3;
@@
 drm_bridge_attach(E1, E2, E3
+   , 0
 )

Signed-off-by: Laurent Pinchart 
Reviewed-by: Boris Brezillon 
---
Changes since v2:

- Update commit message to the new flags argument
- Replace a leftover 'true' with 0
- Update msm edp and hdmi

Changes since v1:

- Replace the create_connector boolean with a flags bitmask
- Update ingenic driver
- Add semantic patch to commit message
---
 drivers/gpu/drm/arc/arcpgu_hdmi.c |  2 +-
 .../gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c  |  2 +-
 drivers/gpu/drm/bridge/adv7511/adv7511_drv.c  |  6 +-
 .../drm/bridge/analogix/analogix-anx6345.c|  6 +-
 .../drm/bridge/analogix/analogix-anx78xx.c|  6 +-
 .../drm/bridge/analogix/analogix_dp_core.c|  8 ++--
 drivers/gpu/drm/bridge/cdns-dsi.c |  6 --
 drivers/gpu/drm/bridge/dumb-vga-dac.c |  6 +-
 drivers/gpu/drm/bridge/lvds-encoder.c |  5 +++--
 .../bridge/megachips-stdp-ge-b850v3-fw.c  |  6 +-
 drivers/gpu/drm/bridge/nxp-ptn3460.c  |  6 +-
 drivers/gpu/drm/bridge/panel.c|  6 +-
 drivers/gpu/drm/bridge/parade-ps8622.c|  6 +-
 drivers/gpu/drm/bridge/sii902x.c  |  6 +-
 drivers/gpu/drm/bridge/sil-sii8620.c  |  3 ++-
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c |  8 ++--
 drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c |  8 +---
 drivers/gpu/drm/bridge/tc358764.c |  6 +-
 drivers/gpu/drm/bridge/tc358767.c |  6 +-
 drivers/gpu/drm/bridge/thc63lvd1024.c |  5 +++--
 drivers/gpu/drm/bridge/ti-sn65dsi86.c |  6 +-
 drivers/gpu/drm/bridge/ti-tfp410.c|  6 +-
 drivers/gpu/drm/drm_bridge.c  |  6 --
 drivers/gpu/drm/drm_simple_kms_helper.c   |  2 +-
 drivers/gpu/drm/exynos/exynos_dp.c|  3 ++-
 drivers/gpu/drm/exynos/exynos_drm_dsi.c   |  4 ++--
 drivers/gpu/drm/exynos/exynos_hdmi.c  |  2 +-
 drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c |  2 +-
 drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c  |  2 +-
 drivers/gpu/drm/i2c/tda998x_drv.c |  8 ++--
 drivers/gpu/drm/imx/imx-ldb.c |  2 +-
 drivers/gpu/drm/imx/parallel-display.c|  2 +-
 drivers/gpu/drm/ingenic/ingenic-drm.c |  2 +-
 drivers/gpu/drm/mcde/mcde_dsi.c   |  5 +++--
 drivers/gpu/drm/mediatek/mtk_dpi.c|  2 +-
 drivers/gpu/drm/mediatek/mtk_dsi.c|  2 +-
 drivers/gpu/drm/mediatek/mtk_hdmi.c   |  8 ++--
 drivers/gpu/drm/msm/dsi/dsi_manager.c |  4 ++--
 drivers/gpu/drm/msm/edp/edp.c |  2 +-
 drivers/gpu/drm/msm/edp/edp_bridge.c  |  2 +-
 drivers/gpu/drm/msm/hdmi/hdmi.c   |  2 +-
 drivers/gpu/drm/msm/hdmi/hdmi_bridge.c|  2 +-
 drivers/gpu/drm/omapdrm/omap_drv.c|  2 +-
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c |  2 +-
 

[PATCH v3 02/50] drm/connector: Add helper to get a connector type name

2019-12-10 Thread Laurent Pinchart
drm_connector.c contains a map of connector types (DRM_MODE_CONNECTOR_*)
to name strings, but doesn't expose it. This leads to drivers having to
store a similar map.

Add a new drm_get_connector_type_name() helper function that return a
name string for a connector type.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Boris Brezillon 
---
 drivers/gpu/drm/drm_connector.c | 15 +++
 include/drm/drm_connector.h |  1 +
 2 files changed, 16 insertions(+)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 2166000ed057..a21ee56c424e 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -111,6 +111,21 @@ void drm_connector_ida_destroy(void)
ida_destroy(_connector_enum_list[i].ida);
 }
 
+/**
+ * drm_get_connector_type_name - return a string for connector type
+ * @type: The connector type (DRM_MODE_CONNECTOR_*)
+ *
+ * Returns: the name of the connector type, or NULL if the type is not valid.
+ */
+const char *drm_get_connector_type_name(unsigned int type)
+{
+   if (type < ARRAY_SIZE(drm_connector_enum_list))
+   return drm_connector_enum_list[type].name;
+
+   return NULL;
+}
+EXPORT_SYMBOL(drm_get_connector_type_name);
+
 /**
  * drm_connector_get_cmdline_mode - reads the user's cmdline mode
  * @connector: connector to quwery
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 5f8c3389d46f..3deeba1f8163 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1504,6 +1504,7 @@ drm_connector_is_unregistered(struct drm_connector 
*connector)
DRM_CONNECTOR_UNREGISTERED;
 }
 
+const char *drm_get_connector_type_name(unsigned int connector_type);
 const char *drm_get_connector_status_name(enum drm_connector_status status);
 const char *drm_get_subpixel_order_name(enum subpixel_order order);
 const char *drm_get_dpms_name(int val);
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 10/50] drm/bridge: simple-bridge: Add support for the TI OP362

2019-12-10 Thread Laurent Pinchart
The TI OP362 is an analog video amplifier controlled through a GPIO. Add
support for it to the simple-bridge driver.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Andrzej Hajda 
Reviewed-by: Boris Brezillon 
Reviewed-by: Maxime Ripard 
Reviewed-by: Tomi Valkeinen 
---
 drivers/gpu/drm/bridge/simple-bridge.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/bridge/simple-bridge.c 
b/drivers/gpu/drm/bridge/simple-bridge.c
index 5d8b1b49798e..45b2ee4aad3d 100644
--- a/drivers/gpu/drm/bridge/simple-bridge.c
+++ b/drivers/gpu/drm/bridge/simple-bridge.c
@@ -303,6 +303,11 @@ static const struct of_device_id simple_bridge_match[] = {
.timings = _bridge_timings,
.connector_type = DRM_MODE_CONNECTOR_VGA,
},
+   }, {
+   .compatible = "ti,opa362",
+   .data = &(const struct simple_bridge_info) {
+   .connector_type = DRM_MODE_CONNECTOR_Composite,
+   },
}, {
.compatible = "ti,ths8135",
.data = &(const struct simple_bridge_info) {
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 03/50] drm/edid: Add flag to drm_display_info to identify HDMI sinks

2019-12-10 Thread Laurent Pinchart
The drm_display_info structure contains many fields related to HDMI
sinks, but none that identifies if a sink compliant with CEA-861 (EDID)
shall be treated as an HDMI sink or a DVI sink. Add such a flag, and
populate it according to section 8.3.3 ("DVI/HDMI Device
Discrimination") of the HDMI v1.3 specification.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Andrzej Hajda 
Reviewed-by: Ville Syrjälä 
Reviewed-by: Daniel Vetter 
Reviewed-by: Boris Brezillon 
---
Changes since v1:

- Link the is_hdmi field doc with drm_detect_hdmi_monitor()
- Add a conversion task in todo.rst
---
 Documentation/gpu/todo.rst  | 14 ++
 drivers/gpu/drm/drm_edid.c  |  6 ++
 include/drm/drm_connector.h |  8 
 3 files changed, 28 insertions(+)

diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index 2d85f37284a1..0f54f327f082 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -385,6 +385,20 @@ Contact: Daniel Vetter
 
 Level: Intermediate
 
+Replace drm_detect_hdmi_monitor() with drm_display_info.is_hdmi
+---
+
+Once EDID is parsed, the monitor HDMI support information is available through
+drm_display_info.is_hdmi. Many drivers still call drm_detect_hdmi_monitor() to
+retrieve the same information, which is less efficient.
+
+Audit each individual driver calling drm_detect_hdmi_monitor() and switch to
+drm_display_info.is_hdmi if applicable.
+
+Contact: Laurent Pinchart, respective driver maintainers
+
+Level: Intermediate
+
 Core refactorings
 =
 
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 7748b4bf539e..1d21149188c5 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -4425,6 +4425,9 @@ EXPORT_SYMBOL(drm_av_sync_delay);
  *
  * Parse the CEA extension according to CEA-861-B.
  *
+ * Drivers that have added the modes parsed from EDID to drm_display_info
+ * should use _display_info.is_hdmi instead of calling this function.
+ *
  * Return: True if the monitor is HDMI, false if not or unknown.
  */
 bool drm_detect_hdmi_monitor(struct edid *edid)
@@ -4659,6 +4662,8 @@ drm_parse_hdmi_vsdb_video(struct drm_connector 
*connector, const u8 *db)
struct drm_display_info *info = >display_info;
u8 len = cea_db_payload_len(db);
 
+   info->is_hdmi = true;
+
if (len >= 6)
info->dvi_dual = db[6] & 1;
if (len >= 7)
@@ -4727,6 +4732,7 @@ drm_reset_display_info(struct drm_connector *connector)
info->cea_rev = 0;
info->max_tmds_clock = 0;
info->dvi_dual = false;
+   info->is_hdmi = false;
info->has_hdmi_infoframe = false;
info->rgb_quant_range_selectable = false;
memset(>hdmi, 0, sizeof(info->hdmi));
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 3deeba1f8163..30054c5d1345 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -434,6 +434,14 @@ struct drm_display_info {
 */
bool dvi_dual;
 
+   /**
+* @is_hdmi: True if the sink is an HDMI device.
+*
+* This field shall be used instead of calling
+* drm_detect_hdmi_monitor() when possible.
+*/
+   bool is_hdmi;
+
/**
 * @has_hdmi_infoframe: Does the sink support the HDMI infoframe?
 */
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 07/50] drm/bridge: dumb-vga-dac: Rename driver to simple-bridge

2019-12-10 Thread Laurent Pinchart
The dumb-vga-dac driver can support simple DRM bridges without being
limited to VGA DACs. Rename it to simple-bridge.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Andrzej Hajda 
Reviewed-by: Boris Brezillon 
Acked-by: Maxime Ripard 
---
 arch/arm/configs/davinci_all_defconfig   |  2 +-
 arch/arm/configs/integrator_defconfig|  2 +-
 arch/arm/configs/multi_v7_defconfig  |  2 +-
 arch/arm/configs/shmobile_defconfig  |  2 +-
 arch/arm/configs/sunxi_defconfig |  2 +-
 arch/arm/configs/versatile_defconfig |  2 +-
 drivers/gpu/drm/bridge/Kconfig   | 16 
 drivers/gpu/drm/bridge/Makefile  |  2 +-
 .../bridge/{dumb-vga-dac.c => simple-bridge.c}   |  2 +-
 9 files changed, 16 insertions(+), 16 deletions(-)
 rename drivers/gpu/drm/bridge/{dumb-vga-dac.c => simple-bridge.c} (99%)

diff --git a/arch/arm/configs/davinci_all_defconfig 
b/arch/arm/configs/davinci_all_defconfig
index 231f8973bbb2..b370958b0579 100644
--- a/arch/arm/configs/davinci_all_defconfig
+++ b/arch/arm/configs/davinci_all_defconfig
@@ -160,7 +160,7 @@ CONFIG_VIDEO_TVP514X=m
 CONFIG_VIDEO_ADV7343=m
 CONFIG_DRM=m
 CONFIG_DRM_TILCDC=m
-CONFIG_DRM_DUMB_VGA_DAC=m
+CONFIG_DRM_SIMPLE_BRIDGE=m
 CONFIG_DRM_TINYDRM=m
 CONFIG_TINYDRM_ST7586=m
 CONFIG_FB=y
diff --git a/arch/arm/configs/integrator_defconfig 
b/arch/arm/configs/integrator_defconfig
index 2f0a762dc3a0..a9755c501bec 100644
--- a/arch/arm/configs/integrator_defconfig
+++ b/arch/arm/configs/integrator_defconfig
@@ -55,7 +55,7 @@ CONFIG_SMC91X=y
 # CONFIG_KEYBOARD_ATKBD is not set
 # CONFIG_SERIO_SERPORT is not set
 CONFIG_DRM=y
-CONFIG_DRM_DUMB_VGA_DAC=y
+CONFIG_DRM_SIMPLE_BRIDGE=y
 CONFIG_DRM_PL111=y
 CONFIG_FB_MODE_HELPERS=y
 CONFIG_FB_MATROX=y
diff --git a/arch/arm/configs/multi_v7_defconfig 
b/arch/arm/configs/multi_v7_defconfig
index 3f1b96dc7faa..59321917d035 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -667,11 +667,11 @@ CONFIG_DRM_PANEL_ORISETECH_OTM8009A=m
 CONFIG_DRM_PANEL_RAYDIUM_RM68200=m
 CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03=m
 CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=m
-CONFIG_DRM_DUMB_VGA_DAC=m
 CONFIG_DRM_NXP_PTN3460=m
 CONFIG_DRM_PARADE_PS8622=m
 CONFIG_DRM_SII902X=m
 CONFIG_DRM_SII9234=m
+CONFIG_DRM_SIMPLE_BRIDGE=m
 CONFIG_DRM_TOSHIBA_TC358764=m
 CONFIG_DRM_I2C_ADV7511=m
 CONFIG_DRM_I2C_ADV7511_AUDIO=y
diff --git a/arch/arm/configs/shmobile_defconfig 
b/arch/arm/configs/shmobile_defconfig
index bda57cafa2bc..3d7e9a6ca85d 100644
--- a/arch/arm/configs/shmobile_defconfig
+++ b/arch/arm/configs/shmobile_defconfig
@@ -123,8 +123,8 @@ CONFIG_VIDEO_ADV7604=y
 CONFIG_VIDEO_ML86V7667=y
 CONFIG_DRM=y
 CONFIG_DRM_RCAR_DU=y
-CONFIG_DRM_DUMB_VGA_DAC=y
 CONFIG_DRM_SII902X=y
+CONFIG_DRM_SIMPLE_BRIDGE=y
 CONFIG_DRM_I2C_ADV7511=y
 CONFIG_DRM_I2C_ADV7511_AUDIO=y
 CONFIG_FB_SH_MOBILE_LCDC=y
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index 3f5d727efc41..17958ff4a2e2 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -100,7 +100,7 @@ CONFIG_RC_DEVICES=y
 CONFIG_IR_SUNXI=y
 CONFIG_DRM=y
 CONFIG_DRM_SUN4I=y
-CONFIG_DRM_DUMB_VGA_DAC=y
+CONFIG_DRM_SIMPLE_BRIDGE=y
 CONFIG_FB_SIMPLE=y
 CONFIG_SOUND=y
 CONFIG_SND=y
diff --git a/arch/arm/configs/versatile_defconfig 
b/arch/arm/configs/versatile_defconfig
index fe4d4b596585..767935337413 100644
--- a/arch/arm/configs/versatile_defconfig
+++ b/arch/arm/configs/versatile_defconfig
@@ -59,7 +59,7 @@ CONFIG_GPIO_PL061=y
 CONFIG_DRM=y
 CONFIG_DRM_PANEL_ARM_VERSATILE=y
 CONFIG_DRM_PANEL_SIMPLE=y
-CONFIG_DRM_DUMB_VGA_DAC=y
+CONFIG_DRM_SIMPLE_BRIDGE=y
 CONFIG_DRM_PL111=y
 CONFIG_FB_MODE_HELPERS=y
 CONFIG_BACKLIGHT_CLASS_DEVICE=y
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index ccc698c44f58..bf1dfc71733b 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -27,14 +27,6 @@ config DRM_CDNS_DSI
  Support Cadence DPI to DSI bridge. This is an internal
  bridge and is meant to be directly embedded in a SoC.
 
-config DRM_DUMB_VGA_DAC
-   tristate "Dumb VGA DAC Bridge support"
-   depends on OF
-   select DRM_KMS_HELPER
-   help
- Support for non-programmable RGB to VGA DAC bridges, such as ADI
- ADV7123, TI THS8134 and THS8135 or passive resistor ladder DACs.
-
 config DRM_LVDS_ENCODER
tristate "Transparent parallel to LVDS encoder support"
depends on OF
@@ -98,6 +90,14 @@ config DRM_SII9234
  It is an I2C driver, that detects connection of MHL bridge
  and starts encapsulation of HDMI signal.
 
+config DRM_SIMPLE_BRIDGE
+   tristate "Simple DRM bridge support"
+   depends on OF
+   select DRM_KMS_HELPER
+   help
+ Support for non-programmable DRM bridges, such as ADI ADV7123, TI
+ THS8134 and THS8135 or passive resistor ladder DACs.
+
 config DRM_THINE_THC63LVD1024
   

[PATCH v3 13/50] drm/bridge: panel: Implement bridge connector operations

2019-12-10 Thread Laurent Pinchart
Implement the newly added bridge connector operations, allowing the
usage of drm_bridge_panel with drm_bridge_connector.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Use the connector type from the panel instead of hardcoding it to DPI
- Rebased on top top of Sam's panel .get_modes() rework
---
 drivers/gpu/drm/bridge/panel.c | 13 -
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 25c77cb24f42..cf4be369d75e 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -61,7 +61,7 @@ static int panel_bridge_attach(struct drm_bridge *bridge,
int ret;
 
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
-   return -EINVAL;
+   return 0;
 
if (!bridge->encoder) {
DRM_ERROR("Missing encoder\n");
@@ -124,6 +124,14 @@ static void panel_bridge_post_disable(struct drm_bridge 
*bridge)
drm_panel_unprepare(panel_bridge->panel);
 }
 
+static int panel_bridge_get_modes(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+   struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+
+   return drm_panel_get_modes(panel_bridge->panel, connector);
+}
+
 static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
.attach = panel_bridge_attach,
.detach = panel_bridge_detach,
@@ -131,6 +139,7 @@ static const struct drm_bridge_funcs 
panel_bridge_bridge_funcs = {
.enable = panel_bridge_enable,
.disable = panel_bridge_disable,
.post_disable = panel_bridge_post_disable,
+   .get_modes = panel_bridge_get_modes,
 };
 
 /**
@@ -200,6 +209,8 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct 
drm_panel *panel,
 #ifdef CONFIG_OF
panel_bridge->bridge.of_node = panel->dev->of_node;
 #endif
+   panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES;
+   panel_bridge->bridge.type = connector_type;
 
drm_bridge_add(_bridge->bridge);
 
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 28/50] drm/omap: hdmi4: Register a drm_bridge for EDID read

2019-12-10 Thread Laurent Pinchart
In order to integrate with a chain of drm_bridge, the internal HDMI4
encoder has to expose the EDID read operation through the drm_bridge
API. Register a bridge at initialisation time to do so.

For the time being make the next bridge in the chain optional as the
HDMI output is still based on omap_dss_device. The create_connector
argument to the bridge attach function is also ignored for the same
reason. This will be changed later when removing the related
omapdrm-specific display drivers.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Unregister bridge if output initialisation fails
---
 drivers/gpu/drm/omapdrm/dss/hdmi.h  |  3 ++
 drivers/gpu/drm/omapdrm/dss/hdmi4.c | 78 ++---
 2 files changed, 75 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi.h 
b/drivers/gpu/drm/omapdrm/dss/hdmi.h
index c867552c925c..bd43f6abf27b 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi.h
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi.h
@@ -14,6 +14,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "omapdss.h"
 #include "dss.h"
@@ -364,6 +365,7 @@ struct omap_hdmi {
bool core_enabled;
 
struct omap_dss_device output;
+   struct drm_bridge bridge;
 
struct platform_device *audio_pdev;
void (*audio_abort_cb)(struct device *dev);
@@ -379,5 +381,6 @@ struct omap_hdmi {
 };
 
 #define dssdev_to_hdmi(dssdev) container_of(dssdev, struct omap_hdmi, output)
+#define drm_bridge_to_hdmi(b) container_of(b, struct omap_hdmi, bridge)
 
 #endif
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index 37536b9f3114..67994287447b 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -390,7 +390,8 @@ static void hdmi_disconnect(struct omap_dss_device *src,
 
 #define MAX_EDID   512
 
-static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi)
+static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi,
+   struct drm_connector *connector)
 {
u8 *edid;
int r;
@@ -428,9 +429,12 @@ static struct edid *hdmi_read_edid_data(struct omap_hdmi 
*hdmi)
return NULL;
 }
 
-static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
+static struct edid *
+hdmi_do_read_edid(struct omap_hdmi *hdmi,
+ struct edid *(*read)(struct omap_hdmi *hdmi,
+  struct drm_connector *connector),
+ struct drm_connector *connector)
 {
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
struct edid *edid = NULL;
unsigned int cec_addr;
bool need_enable;
@@ -452,7 +456,7 @@ static struct edid *hdmi_read_edid(struct omap_dss_device 
*dssdev)
if (r)
goto done;
 
-   edid = hdmi_read_edid_data(hdmi);
+   edid = read(hdmi, connector);
 
 done:
hdmi_runtime_put(hdmi);
@@ -474,6 +478,12 @@ static struct edid *hdmi_read_edid(struct omap_dss_device 
*dssdev)
return edid;
 }
 
+static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev)
+{
+   return hdmi_do_read_edid(dssdev_to_hdmi(dssdev), hdmi_read_edid_data,
+NULL);
+}
+
 static void hdmi_lost_hotplug(struct omap_dss_device *dssdev)
 {
struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
@@ -517,6 +527,56 @@ static const struct omap_dss_device_ops hdmi_ops = {
},
 };
 
+/* 
-
+ * DRM Bridge Operations
+ */
+
+static int hdmi4_bridge_attach(struct drm_bridge *bridge,
+  enum drm_bridge_attach_flags flags)
+{
+   struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
+
+   if (!hdmi->output.next_bridge)
+   return 0;
+
+   return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge,
+bridge, flags);
+}
+
+static struct edid *hdmi4_bridge_read_edid(struct omap_hdmi *hdmi,
+  struct drm_connector *connector)
+{
+   return drm_do_get_edid(connector, hdmi4_core_ddc_read, >core);
+}
+
+static struct edid *hdmi4_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+   struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
+
+   return hdmi_do_read_edid(hdmi, hdmi4_bridge_read_edid, connector);
+}
+
+static const struct drm_bridge_funcs hdmi4_bridge_funcs = {
+   .attach = hdmi4_bridge_attach,
+   .get_edid = hdmi4_bridge_get_edid,
+};
+
+static void hdmi4_bridge_init(struct omap_hdmi *hdmi)
+{
+   hdmi->bridge.funcs = _bridge_funcs;
+   hdmi->bridge.of_node = hdmi->pdev->dev.of_node;
+   hdmi->bridge.ops = DRM_BRIDGE_OP_EDID;
+   hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+
+   drm_bridge_add(>bridge);
+}
+
+static void hdmi4_bridge_cleanup(struct omap_hdmi *hdmi)
+{
+   

[PATCH v3 06/50] drm/bridge: dumb-vga-dac: Rename internal symbols to simple-bridge

2019-12-10 Thread Laurent Pinchart
The dumb-vga-dac driver is a simple DRM bridge driver for simple VGA
DACs that don't require configuration. Other non-VGA bridges fall in a
similar category, and would benefit from a common driver. Prepare for
this by renaming the internal symbols from dumb-vga-dac to
simple-bridge.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Andrzej Hajda 
Reviewed-by: Boris Brezillon 
Reviewed-by: Maxime Ripard 
---
 drivers/gpu/drm/bridge/dumb-vga-dac.c | 154 +-
 1 file changed, 77 insertions(+), 77 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c 
b/drivers/gpu/drm/bridge/dumb-vga-dac.c
index 198094d81ae6..6bfdff31e194 100644
--- a/drivers/gpu/drm/bridge/dumb-vga-dac.c
+++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c
@@ -17,7 +17,7 @@
 #include 
 #include 
 
-struct dumb_vga {
+struct simple_bridge {
struct drm_bridge   bridge;
struct drm_connectorconnector;
 
@@ -25,28 +25,28 @@ struct dumb_vga {
struct regulator*vdd;
 };
 
-static inline struct dumb_vga *
-drm_bridge_to_dumb_vga(struct drm_bridge *bridge)
+static inline struct simple_bridge *
+drm_bridge_to_simple_bridge(struct drm_bridge *bridge)
 {
-   return container_of(bridge, struct dumb_vga, bridge);
+   return container_of(bridge, struct simple_bridge, bridge);
 }
 
-static inline struct dumb_vga *
-drm_connector_to_dumb_vga(struct drm_connector *connector)
+static inline struct simple_bridge *
+drm_connector_to_simple_bridge(struct drm_connector *connector)
 {
-   return container_of(connector, struct dumb_vga, connector);
+   return container_of(connector, struct simple_bridge, connector);
 }
 
-static int dumb_vga_get_modes(struct drm_connector *connector)
+static int simple_bridge_get_modes(struct drm_connector *connector)
 {
-   struct dumb_vga *vga = drm_connector_to_dumb_vga(connector);
+   struct simple_bridge *sbridge = 
drm_connector_to_simple_bridge(connector);
struct edid *edid;
int ret;
 
-   if (!vga->ddc)
+   if (!sbridge->ddc)
goto fallback;
 
-   edid = drm_get_edid(connector, vga->ddc);
+   edid = drm_get_edid(connector, sbridge->ddc);
if (!edid) {
DRM_INFO("EDID readout failed, falling back to standard 
modes\n");
goto fallback;
@@ -70,14 +70,14 @@ static int dumb_vga_get_modes(struct drm_connector 
*connector)
return ret;
 }
 
-static const struct drm_connector_helper_funcs dumb_vga_con_helper_funcs = {
-   .get_modes  = dumb_vga_get_modes,
+static const struct drm_connector_helper_funcs simple_bridge_con_helper_funcs 
= {
+   .get_modes  = simple_bridge_get_modes,
 };
 
 static enum drm_connector_status
-dumb_vga_connector_detect(struct drm_connector *connector, bool force)
+simple_bridge_connector_detect(struct drm_connector *connector, bool force)
 {
-   struct dumb_vga *vga = drm_connector_to_dumb_vga(connector);
+   struct simple_bridge *sbridge = 
drm_connector_to_simple_bridge(connector);
 
/*
 * Even if we have an I2C bus, we can't assume that the cable
@@ -85,14 +85,14 @@ dumb_vga_connector_detect(struct drm_connector *connector, 
bool force)
 * wire the DDC pins, or the I2C bus might not be working at
 * all.
 */
-   if (vga->ddc && drm_probe_ddc(vga->ddc))
+   if (sbridge->ddc && drm_probe_ddc(sbridge->ddc))
return connector_status_connected;
 
return connector_status_unknown;
 }
 
-static const struct drm_connector_funcs dumb_vga_con_funcs = {
-   .detect = dumb_vga_connector_detect,
+static const struct drm_connector_funcs simple_bridge_con_funcs = {
+   .detect = simple_bridge_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy= drm_connector_cleanup,
.reset  = drm_atomic_helper_connector_reset,
@@ -100,10 +100,10 @@ static const struct drm_connector_funcs 
dumb_vga_con_funcs = {
.atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
 };
 
-static int dumb_vga_attach(struct drm_bridge *bridge,
-  enum drm_bridge_attach_flags flags)
+static int simple_bridge_attach(struct drm_bridge *bridge,
+   enum drm_bridge_attach_flags flags)
 {
-   struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge);
+   struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
int ret;
 
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
@@ -114,50 +114,50 @@ static int dumb_vga_attach(struct drm_bridge *bridge,
return -ENODEV;
}
 
-   drm_connector_helper_add(>connector,
-_vga_con_helper_funcs);
-   ret = drm_connector_init_with_ddc(bridge->dev, >connector,
- _vga_con_funcs,
+   drm_connector_helper_add(>connector,
+

[PATCH v3 14/50] drm/bridge: tfp410: Replace manual connector handling with bridge

2019-12-10 Thread Laurent Pinchart
Now that a driver is available for display connectors, replace the
manual connector handling code with usage of the DRM bridge API. The
tfp410 driver doesn't deal with the display connector directly anymore,
but still delegates drm_connector operations to the next bridge. This
brings us one step closer to having the tfp410 driver handling the
TFP410 only.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Use drm_bridge_get_edid() and drm_bridge_detect() helpers
---
 drivers/gpu/drm/bridge/ti-tfp410.c | 216 ++---
 1 file changed, 75 insertions(+), 141 deletions(-)

diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c 
b/drivers/gpu/drm/bridge/ti-tfp410.c
index c36a7e9eb536..9d7a546e5d4d 100644
--- a/drivers/gpu/drm/bridge/ti-tfp410.c
+++ b/drivers/gpu/drm/bridge/ti-tfp410.c
@@ -4,14 +4,12 @@
  * Author: Jyri Sarha 
  */
 
-#include 
-#include 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -24,16 +22,13 @@
 struct tfp410 {
struct drm_bridge   bridge;
struct drm_connectorconnector;
-   unsigned intconnector_type;
 
u32 bus_format;
-   struct i2c_adapter  *ddc;
-   struct gpio_desc*hpd;
-   int hpd_irq;
struct delayed_work hpd_work;
struct gpio_desc*powerdown;
 
struct drm_bridge_timings timings;
+   struct drm_bridge   *next_bridge;
 
struct device *dev;
 };
@@ -56,13 +51,18 @@ static int tfp410_get_modes(struct drm_connector *connector)
struct edid *edid;
int ret;
 
-   if (!dvi->ddc)
-   goto fallback;
+   edid = drm_bridge_get_edid(dvi->next_bridge, connector);
+   if (IS_ERR_OR_NULL(edid)) {
+   if (edid != ERR_PTR(-ENOTSUPP))
+   DRM_INFO("EDID read failed. Fallback to standard 
modes\n");
 
-   edid = drm_get_edid(connector, dvi->ddc);
-   if (!edid) {
-   DRM_INFO("EDID read failed. Fallback to standard modes\n");
-   goto fallback;
+   /*
+* No EDID, fallback on the XGA standard modes and prefer a mode
+* pretty much anything can handle.
+*/
+   ret = drm_add_modes_noedid(connector, 1920, 1200);
+   drm_set_preferred_mode(connector, 1024, 768);
+   return ret;
}
 
drm_connector_update_edid_property(connector, edid);
@@ -71,15 +71,6 @@ static int tfp410_get_modes(struct drm_connector *connector)
 
kfree(edid);
 
-   return ret;
-
-fallback:
-   /* No EDID, fallback on the XGA standard modes */
-   ret = drm_add_modes_noedid(connector, 1920, 1200);
-
-   /* And prefer a mode pretty much anything can handle */
-   drm_set_preferred_mode(connector, 1024, 768);
-
return ret;
 }
 
@@ -92,21 +83,7 @@ tfp410_connector_detect(struct drm_connector *connector, 
bool force)
 {
struct tfp410 *dvi = drm_connector_to_tfp410(connector);
 
-   if (dvi->hpd) {
-   if (gpiod_get_value_cansleep(dvi->hpd))
-   return connector_status_connected;
-   else
-   return connector_status_disconnected;
-   }
-
-   if (dvi->ddc) {
-   if (drm_probe_ddc(dvi->ddc))
-   return connector_status_connected;
-   else
-   return connector_status_disconnected;
-   }
-
-   return connector_status_unknown;
+   return drm_bridge_detect(dvi->next_bridge);
 }
 
 static const struct drm_connector_funcs tfp410_con_funcs = {
@@ -118,12 +95,35 @@ static const struct drm_connector_funcs tfp410_con_funcs = 
{
.atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
 };
 
+static void tfp410_hpd_work_func(struct work_struct *work)
+{
+   struct tfp410 *dvi;
+
+   dvi = container_of(work, struct tfp410, hpd_work.work);
+
+   if (dvi->bridge.dev)
+   drm_helper_hpd_irq_event(dvi->bridge.dev);
+}
+
+static void tfp410_hpd_callback(void *arg, enum drm_connector_status status)
+{
+   struct tfp410 *dvi = arg;
+
+   mod_delayed_work(system_wq, >hpd_work,
+msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
+}
+
 static int tfp410_attach(struct drm_bridge *bridge,
 enum drm_bridge_attach_flags flags)
 {
struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
int ret;
 
+   ret = drm_bridge_attach(bridge->encoder, dvi->next_bridge, bridge,
+   DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+   if (ret < 0)
+   return ret;
+
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return -EINVAL;
 
@@ -132,17 +132,23 @@ static int tfp410_attach(struct drm_bridge *bridge,
return -ENODEV;
}
 
-   if (dvi->hpd_irq >= 0)
+   if 

[PATCH v3 09/50] drm/bridge: simple-bridge: Add support for enable GPIO

2019-12-10 Thread Laurent Pinchart
If an enable GPIO is declared in the firmware, assert it when enabling
the bridge and deassert it when disabling it.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Andrzej Hajda 
Reviewed-by: Stefan Agner 
Reviewed-by: Boris Brezillon 
Reviewed-by: Maxime Ripard 
---
 drivers/gpu/drm/bridge/simple-bridge.c | 22 ++
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/bridge/simple-bridge.c 
b/drivers/gpu/drm/bridge/simple-bridge.c
index 5a290c14b310..5d8b1b49798e 100644
--- a/drivers/gpu/drm/bridge/simple-bridge.c
+++ b/drivers/gpu/drm/bridge/simple-bridge.c
@@ -6,6 +6,7 @@
  * Maxime Ripard 
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -30,6 +31,7 @@ struct simple_bridge {
 
struct i2c_adapter  *ddc;
struct regulator*vdd;
+   struct gpio_desc*enable;
 };
 
 static inline struct simple_bridge *
@@ -141,19 +143,23 @@ static int simple_bridge_attach(struct drm_bridge *bridge,
 static void simple_bridge_enable(struct drm_bridge *bridge)
 {
struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
-   int ret = 0;
+   int ret;
 
-   if (sbridge->vdd)
+   if (sbridge->vdd) {
ret = regulator_enable(sbridge->vdd);
+   if (ret)
+   DRM_ERROR("Failed to enable vdd regulator: %d\n", ret);
+   }
 
-   if (ret)
-   DRM_ERROR("Failed to enable vdd regulator: %d\n", ret);
+   gpiod_set_value_cansleep(sbridge->enable, 1);
 }
 
 static void simple_bridge_disable(struct drm_bridge *bridge)
 {
struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
 
+   gpiod_set_value_cansleep(sbridge->enable, 0);
+
if (sbridge->vdd)
regulator_disable(sbridge->vdd);
 }
@@ -206,6 +212,14 @@ static int simple_bridge_probe(struct platform_device 
*pdev)
dev_dbg(>dev, "No vdd regulator found: %d\n", ret);
}
 
+   sbridge->enable = devm_gpiod_get_optional(>dev, "enable",
+ GPIOD_OUT_LOW);
+   if (IS_ERR(sbridge->enable)) {
+   if (PTR_ERR(sbridge->enable) != -EPROBE_DEFER)
+   dev_err(>dev, "Unable to retrieve enable GPIO\n");
+   return PTR_ERR(sbridge->enable);
+   }
+
sbridge->ddc = simple_bridge_retrieve_ddc(>dev);
if (IS_ERR(sbridge->ddc)) {
if (PTR_ERR(sbridge->ddc) == -ENODEV) {
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 30/50] drm/omap: hdmi4: Move mode set, enable and disable operations to bridge

2019-12-10 Thread Laurent Pinchart
Move the omap_dss_device .set_timings(), .enable() and .disable()
operations to the drm_bridge functions. As the drm_bridge for the HDMI
encoder is unconditionally registered and attached, those operations
will be called at the appropriate time.

The omapdss device .set_infoframe() and .set_hdmi_mode() operations have
no equivalent in drm_bridge. Thir content is thus moved to the bridge
.enable() operation as the data they store is not needed before the HDMI
encoder gets enabled.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Detail .set_infoframe() and .set_hdmi_mode() handling in the commit
  message
---
 drivers/gpu/drm/omapdrm/dss/hdmi4.c | 201 +++-
 1 file changed, 106 insertions(+), 95 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c 
b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index 67994287447b..a5c6054158eb 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -28,6 +28,8 @@
 #include 
 #include 
 
+#include 
+
 #include "omapdss.h"
 #include "hdmi4_core.h"
 #include "hdmi4_cec.h"
@@ -237,20 +239,6 @@ static void hdmi_power_off_full(struct omap_hdmi *hdmi)
hdmi_power_off_core(hdmi);
 }
 
-static void hdmi_display_set_timings(struct omap_dss_device *dssdev,
-const struct drm_display_mode *mode)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-
-   mutex_lock(>lock);
-
-   drm_display_mode_to_videomode(mode, >cfg.vm);
-
-   dispc_set_tv_pclk(hdmi->dss->dispc, mode->clock * 1000);
-
-   mutex_unlock(>lock);
-}
-
 static int hdmi_dump_regs(struct seq_file *s, void *p)
 {
struct omap_hdmi *hdmi = s->private;
@@ -284,62 +272,6 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
hdmi_wp_audio_enable(>wp, false);
 }
 
-static void hdmi_display_enable(struct omap_dss_device *dssdev)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-   unsigned long flags;
-   int r;
-
-   DSSDBG("ENTER hdmi_display_enable\n");
-
-   mutex_lock(>lock);
-
-   r = hdmi_power_on_full(hdmi);
-   if (r) {
-   DSSERR("failed to power on device\n");
-   goto done;
-   }
-
-   if (hdmi->audio_configured) {
-   r = hdmi4_audio_config(>core, >wp,
-  >audio_config,
-  hdmi->cfg.vm.pixelclock);
-   if (r) {
-   DSSERR("Error restoring audio configuration: %d", r);
-   hdmi->audio_abort_cb(>pdev->dev);
-   hdmi->audio_configured = false;
-   }
-   }
-
-   spin_lock_irqsave(>audio_playing_lock, flags);
-   if (hdmi->audio_configured && hdmi->audio_playing)
-   hdmi_start_audio_stream(hdmi);
-   hdmi->display_enabled = true;
-   spin_unlock_irqrestore(>audio_playing_lock, flags);
-
-done:
-   mutex_unlock(>lock);
-}
-
-static void hdmi_display_disable(struct omap_dss_device *dssdev)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-   unsigned long flags;
-
-   DSSDBG("Enter hdmi_display_disable\n");
-
-   mutex_lock(>lock);
-
-   spin_lock_irqsave(>audio_playing_lock, flags);
-   hdmi_stop_audio_stream(hdmi);
-   hdmi->display_enabled = false;
-   spin_unlock_irqrestore(>audio_playing_lock, flags);
-
-   hdmi_power_off_full(hdmi);
-
-   mutex_unlock(>lock);
-}
-
 int hdmi4_core_enable(struct hdmi_core_data *core)
 {
struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core);
@@ -491,39 +423,14 @@ static void hdmi_lost_hotplug(struct omap_dss_device 
*dssdev)
hdmi4_cec_set_phys_addr(>core, CEC_PHYS_ADDR_INVALID);
 }
 
-static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
-   const struct hdmi_avi_infoframe *avi)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-
-   hdmi->cfg.infoframe = *avi;
-   return 0;
-}
-
-static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
-   bool hdmi_mode)
-{
-   struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
-
-   hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
-   return 0;
-}
-
 static const struct omap_dss_device_ops hdmi_ops = {
.connect= hdmi_connect,
.disconnect = hdmi_disconnect,
 
-   .enable = hdmi_display_enable,
-   .disable= hdmi_display_disable,
-
-   .set_timings= hdmi_display_set_timings,
-
.read_edid  = hdmi_read_edid,
 
.hdmi = {
.lost_hotplug   = hdmi_lost_hotplug,
-   .set_infoframe  = hdmi_set_infoframe,
-   .set_hdmi_mode  = hdmi_set_hdmi_mode,
},
 };
 
@@ -543,6 +450,107 @@ static int hdmi4_bridge_attach(struct drm_bridge *bridge,
 bridge, flags);
 }
 
+static 

[PATCH v3 00/50] drm/omap: Replace custom display drivers with drm_bridge and drm_panel

2019-12-10 Thread Laurent Pinchart
Hello,

This patch series is the third attempt to (nearly, see [1]) complete the
rework of the omapdrm driver to move to drm_bridge and drm_panel.

The previous version is available at [2] and explains in its long cover
letter the rationale for the changes. I won't duplicate it here as it is
still valid as-is.

Compared to v2, this version has been rebased on top of drm-misc-next,
to benefit from Boris' and Sam's excellent work on bridges and panels.
This has allowed me to remove a couple of hacks or clean up a few areas.
Other comments on v2 have been addressed, and acks collected.

There is however still one major open issue in the way hotplug detection
and notification is handled. This has been discussed in length with
Andrzej and Daniel in [3] (where we have competed for the largest number
of quote levels in an e-mail, and may have won). I do agree with some of
the points they have raised, but I would like to propose addressing them
on top of this series.

The rationale for this proposal is as follows. Hotplug notification is
an area that hasn't received enough love, and there is quite a lot of
work there to fix the world. The code in this series has already been
reworked to decouple the notification mechanism from both the producers
(as in bridges) and consumers (as in other bridges) of hotplug events,
isolating the implementation of the mechanism in drm_bridge.c and
drm_bridge_connector.c.

The implementation has known shortcomings in that it doesn't support
blocking hotplug notifications along the chain of bridges, or native
notification of encoders or DRM devices. While those are valid concerns,
I'm worried that fixing them as part of this series would not only
massively delay the other parts, but would also have no user as I have
no use case for these features. The implementation would then be largely
untested, and very likely fail to test of real users.

For these reasons I would like to improve the hotplug notification
mechanism on top of this series, when someone will hit for real the
issues that have been previously raised. If that person isn't me, I am
willing to help them solve the problems at that point.

The patches can be found at

git://linuxtv.org/pinchartl/media.git omapdrm/bridge/devel

[1] The only notable exception is the omapdrm-specific DSI panel driver
that implements a large number of custom operations. This is being
addressed separately.

[2] https://patchwork.kernel.org/cover/11102445/

[3] https://patchwork.kernel.org/patch/11034193/

Laurent Pinchart (49):
  video: hdmi: Change return type of hdmi_avi_infoframe_init() to void
  drm/connector: Add helper to get a connector type name
  drm/edid: Add flag to drm_display_info to identify HDMI sinks
  drm/bridge: Add connector-related bridge operations and data
  drm/bridge: Extend bridge API to disable connector creation
  drm/bridge: dumb-vga-dac: Rename internal symbols to simple-bridge
  drm/bridge: dumb-vga-dac: Rename driver to simple-bridge
  drm/bridge: simple-bridge: Add support for non-VGA bridges
  drm/bridge: simple-bridge: Add support for enable GPIO
  drm/bridge: simple-bridge: Add support for the TI OP362
  drm/bridge: Add bridge driver for display connectors
  drm/bridge: Add driver for the TI TPD12S015 HDMI level shifter
  drm/bridge: panel: Implement bridge connector operations
  drm/bridge: tfp410: Replace manual connector handling with bridge
  drm/bridge: tfp410: Allow operation without drm_connector
  drm: Add helper to create a connector for a chain of bridges
  drm/omap: dss: Cleanup DSS ports on initialisation failure
  drm/omap: Simplify HDMI mode and infoframe configuration
  drm/omap: Factor out display type to connector type conversion
  drm/omap: Use the drm_panel_bridge API
  drm/omap: dss: Fix output next device lookup in DT
  drm/omap: Add infrastructure to support drm_bridge local to DSS
outputs
  drm/omap: dss: Make omap_dss_device_ops optional
  drm/omap: hdmi: Allocate EDID in the .read_edid() operation
  drm/omap: hdmi4: Rework EDID read to isolate data read
  drm/omap: hdmi5: Rework EDID read to isolate data read
  drm/omap: hdmi4: Register a drm_bridge for EDID read
  drm/omap: hdmi5: Register a drm_bridge for EDID read
  drm/omap: hdmi4: Move mode set, enable and disable operations to
bridge
  drm/omap: hdmi5: Move mode set, enable and disable operations to
bridge
  drm/omap: hdmi4: Implement drm_bridge .hpd_notify() operation
  drm/omap: dss: Remove .set_hdmi_mode() and .set_infoframe() operations
  drm/omap: venc: Register a drm_bridge
  drm/omap: Create connector for bridges
  drm/omap: Switch the HDMI and VENC outputs to drm_bridge
  drm/omap: Remove HPD, detect and EDID omapdss operations
  drm/omap: hdmi: Remove omap_dss_device operations
  drm/omap: venc: Remove omap_dss_device operations
  drm/omap: hdmi4: Simplify EDID read
  drm/omap: hdmi5: Simplify EDID read
  drm/omap: dpi: Sort includes alphabetically
  drm/omap: dpi: Reorder functions in sections
  

[PATCH v3 17/50] drm/omap: Fix possible object reference leak

2019-12-10 Thread Laurent Pinchart
From: Wen Yang 

The call to of_find_matching_node returns a node pointer with refcount
incremented thus it must be explicitly decremented after the last
usage.

Detected by coccinelle with the following warnings:
drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c:212:2-8: ERROR: missing 
of_node_put; acquired a node pointer with refcount incremented on line 209, but 
without a corresponding object release within this function.
drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c:237:1-7: ERROR: missing 
of_node_put; acquired a node pointer with refcount incremented on line 209, but 
without a corresponding object release within this function.

Signed-off-by: Wen Yang 
Reviewed-by: Laurent Pinchart 
Reviewed-by: Mukesh Ojha 
Signed-off-by: Laurent Pinchart 
---
 drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c 
b/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c
index 31502857f013..ce67891eedd4 100644
--- a/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c
+++ b/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c
@@ -192,7 +192,7 @@ static int __init omapdss_boot_init(void)
dss = of_find_matching_node(NULL, omapdss_of_match);
 
if (dss == NULL || !of_device_is_available(dss))
-   return 0;
+   goto put_node;
 
omapdss_walk_device(dss, true);
 
@@ -217,6 +217,8 @@ static int __init omapdss_boot_init(void)
kfree(n);
}
 
+put_node:
+   of_node_put(dss);
return 0;
 }
 
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 08/50] drm/bridge: simple-bridge: Add support for non-VGA bridges

2019-12-10 Thread Laurent Pinchart
Create a new simple_bridge_info structure that stores information about
the bridge model, and store the bridge timings in there, along with the
connector type. Use that new structure for of_device_id data. This
enables support for non-VGA bridges.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Andrzej Hajda 
Reviewed-by: Stefan Agner 
Reviewed-by: Boris Brezillon 
Reviewed-by: Maxime Ripard 
---
Changes since v1:

- Renamed simple_bridge_info.type field to connector_type
---
 drivers/gpu/drm/bridge/simple-bridge.c | 41 ++
 1 file changed, 29 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/bridge/simple-bridge.c 
b/drivers/gpu/drm/bridge/simple-bridge.c
index ff6684f7edea..5a290c14b310 100644
--- a/drivers/gpu/drm/bridge/simple-bridge.c
+++ b/drivers/gpu/drm/bridge/simple-bridge.c
@@ -17,10 +17,17 @@
 #include 
 #include 
 
+struct simple_bridge_info {
+   const struct drm_bridge_timings *timings;
+   unsigned int connector_type;
+};
+
 struct simple_bridge {
struct drm_bridge   bridge;
struct drm_connectorconnector;
 
+   const struct simple_bridge_info *info;
+
struct i2c_adapter  *ddc;
struct regulator*vdd;
 };
@@ -118,7 +125,7 @@ static int simple_bridge_attach(struct drm_bridge *bridge,
 _bridge_con_helper_funcs);
ret = drm_connector_init_with_ddc(bridge->dev, >connector,
  _bridge_con_funcs,
- DRM_MODE_CONNECTOR_VGA,
+ sbridge->info->connector_type,
  sbridge->ddc);
if (ret) {
DRM_ERROR("Failed to initialize connector\n");
@@ -188,6 +195,8 @@ static int simple_bridge_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, sbridge);
 
+   sbridge->info = of_device_get_match_data(>dev);
+
sbridge->vdd = devm_regulator_get_optional(>dev, "vdd");
if (IS_ERR(sbridge->vdd)) {
int ret = PTR_ERR(sbridge->vdd);
@@ -211,7 +220,7 @@ static int simple_bridge_probe(struct platform_device *pdev)
 
sbridge->bridge.funcs = _bridge_bridge_funcs;
sbridge->bridge.of_node = pdev->dev.of_node;
-   sbridge->bridge.timings = of_device_get_match_data(>dev);
+   sbridge->bridge.timings = sbridge->info->timings;
 
drm_bridge_add(>bridge);
 
@@ -271,19 +280,27 @@ static const struct drm_bridge_timings 
ti_ths8135_bridge_timings = {
 static const struct of_device_id simple_bridge_match[] = {
{
.compatible = "dumb-vga-dac",
-   .data = NULL,
-   },
-   {
+   .data = &(const struct simple_bridge_info) {
+   .connector_type = DRM_MODE_CONNECTOR_VGA,
+   },
+   }, {
.compatible = "adi,adv7123",
-   .data = _bridge_timings,
-   },
-   {
+   .data = &(const struct simple_bridge_info) {
+   .timings = _bridge_timings,
+   .connector_type = DRM_MODE_CONNECTOR_VGA,
+   },
+   }, {
.compatible = "ti,ths8135",
-   .data = _ths8135_bridge_timings,
-   },
-   {
+   .data = &(const struct simple_bridge_info) {
+   .timings = _ths8135_bridge_timings,
+   .connector_type = DRM_MODE_CONNECTOR_VGA,
+   },
+   }, {
.compatible = "ti,ths8134",
-   .data = _ths8134_bridge_timings,
+   .data = &(const struct simple_bridge_info) {
+   .timings = _ths8134_bridge_timings,
+   .connector_type = DRM_MODE_CONNECTOR_VGA,
+   },
},
{},
 };
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 01/50] video: hdmi: Change return type of hdmi_avi_infoframe_init() to void

2019-12-10 Thread Laurent Pinchart
The hdmi_avi_infoframe_init() never needs to return an error, change its
return type to void.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Andrzej Hajda 
Acked-by: Bartlomiej Zolnierkiewicz 
Reviewed-by: Boris Brezillon 
---
Changes since v1:

- Removed documentation of the return value

Cc: Bartlomiej Zolnierkiewicz 
---
 drivers/gpu/drm/drm_edid.c |  5 +
 drivers/video/hdmi.c   | 11 ++-
 include/linux/hdmi.h   |  2 +-
 3 files changed, 4 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 5b33b7cfd645..7748b4bf539e 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -5227,14 +5227,11 @@ drm_hdmi_avi_infoframe_from_display_mode(struct 
hdmi_avi_infoframe *frame,
 {
enum hdmi_picture_aspect picture_aspect;
u8 vic, hdmi_vic;
-   int err;
 
if (!frame || !mode)
return -EINVAL;
 
-   err = hdmi_avi_infoframe_init(frame);
-   if (err < 0)
-   return err;
+   hdmi_avi_infoframe_init(frame);
 
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
frame->pixel_repeat = 1;
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 9c82e2a0a411..856a8c4e84a2 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -53,18 +53,14 @@ static void hdmi_infoframe_set_checksum(void *buffer, 
size_t size)
 /**
  * hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe
  * @frame: HDMI AVI infoframe
- *
- * Returns 0 on success or a negative error code on failure.
  */
-int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame)
+void hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame)
 {
memset(frame, 0, sizeof(*frame));
 
frame->type = HDMI_INFOFRAME_TYPE_AVI;
frame->version = 2;
frame->length = HDMI_AVI_INFOFRAME_SIZE;
-
-   return 0;
 }
 EXPORT_SYMBOL(hdmi_avi_infoframe_init);
 
@@ -1553,7 +1549,6 @@ static int hdmi_avi_infoframe_unpack(struct 
hdmi_avi_infoframe *frame,
 const void *buffer, size_t size)
 {
const u8 *ptr = buffer;
-   int ret;
 
if (size < HDMI_INFOFRAME_SIZE(AVI))
return -EINVAL;
@@ -1566,9 +1561,7 @@ static int hdmi_avi_infoframe_unpack(struct 
hdmi_avi_infoframe *frame,
if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AVI)) != 0)
return -EINVAL;
 
-   ret = hdmi_avi_infoframe_init(frame);
-   if (ret)
-   return ret;
+   hdmi_avi_infoframe_init(frame);
 
ptr += HDMI_INFOFRAME_HEADER_SIZE;
 
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index 9918a6c910c5..9613d796cfb1 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -207,7 +207,7 @@ struct hdmi_drm_infoframe {
u16 max_fall;
 };
 
-int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame);
+void hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame);
 ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
size_t size);
 ssize_t hdmi_avi_infoframe_pack_only(const struct hdmi_avi_infoframe *frame,
-- 
Regards,

Laurent Pinchart

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v3 12/50] drm/bridge: Add driver for the TI TPD12S015 HDMI level shifter

2019-12-10 Thread Laurent Pinchart
The TI TPD12S015 is an HDMI level shifter and ESD protector controlled
through GPIOs. Add a DRM bridge driver for the device.

Signed-off-by: Laurent Pinchart 
---
Changes since v2:

- Control CT_CP_HPD GPIO from .hpd_enable() and .hpd_disable()
- Remove unneeded hpd_gpio zero check
- Update copyright notice

Changes since v1:

- Remove empty .hpd_enable() and .hpd_disable() operations
---
 drivers/gpu/drm/bridge/Kconfig|   8 +
 drivers/gpu/drm/bridge/Makefile   |   1 +
 drivers/gpu/drm/bridge/ti-tpd12s015.c | 211 ++
 3 files changed, 220 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/ti-tpd12s015.c

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index f14f63e0f6df..d020f120cf21 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -150,6 +150,14 @@ config DRM_TI_SN65DSI86
help
  Texas Instruments SN65DSI86 DSI to eDP Bridge driver
 
+config DRM_TI_TPD12S015
+   tristate "TI TPD12S015 HDMI level shifter and ESD protection"
+   depends on OF
+   select DRM_KMS_HELPER
+   help
+ Texas Instruments TPD12S015 HDMI level shifter and ESD protection
+ driver.
+
 source "drivers/gpu/drm/bridge/analogix/Kconfig"
 
 source "drivers/gpu/drm/bridge/adv7511/Kconfig"
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index bef919d3bca6..4fa7786dcc8f 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
 obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
 obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
 obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
+obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o
 
 obj-y += analogix/
 obj-y += synopsys/
diff --git a/drivers/gpu/drm/bridge/ti-tpd12s015.c 
b/drivers/gpu/drm/bridge/ti-tpd12s015.c
new file mode 100644
index ..514cbf0eac75
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ti-tpd12s015.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TPD12S015 HDMI ESD protection & level shifter chip driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific encoder-opa362 driver
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated
+ * Author: Tomi Valkeinen 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+struct tpd12s015_device {
+   struct drm_bridge bridge;
+
+   struct gpio_desc *ct_cp_hpd_gpio;
+   struct gpio_desc *ls_oe_gpio;
+   struct gpio_desc *hpd_gpio;
+   int hpd_irq;
+
+   struct drm_bridge *next_bridge;
+};
+
+static inline struct tpd12s015_device *to_tpd12s015(struct drm_bridge *bridge)
+{
+   return container_of(bridge, struct tpd12s015_device, bridge);
+}
+
+static int tpd12s015_attach(struct drm_bridge *bridge,
+   enum drm_bridge_attach_flags flags)
+{
+   struct tpd12s015_device *tpd = to_tpd12s015(bridge);
+   int ret;
+
+   if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
+   return -EINVAL;
+
+   ret = drm_bridge_attach(bridge->encoder, tpd->next_bridge,
+   bridge, flags);
+   if (ret < 0)
+   return ret;
+
+   gpiod_set_value_cansleep(tpd->ls_oe_gpio, 1);
+
+   /* DC-DC converter needs at max 300us to get to 90% of 5V. */
+   usleep_range(300, 1000);
+
+   return 0;
+}
+
+static void tpd12s015_detach(struct drm_bridge *bridge)
+{
+   struct tpd12s015_device *tpd = to_tpd12s015(bridge);
+
+   gpiod_set_value_cansleep(tpd->ls_oe_gpio, 0);
+}
+
+static enum drm_connector_status tpd12s015_detect(struct drm_bridge *bridge)
+{
+   struct tpd12s015_device *tpd = to_tpd12s015(bridge);
+
+   if (gpiod_get_value_cansleep(tpd->hpd_gpio))
+   return connector_status_connected;
+   else
+   return connector_status_disconnected;
+}
+
+static void tpd12s015_hpd_enable(struct drm_bridge *bridge)
+{
+   struct tpd12s015_device *tpd = to_tpd12s015(bridge);
+
+   gpiod_set_value_cansleep(tpd->ct_cp_hpd_gpio, 1);
+}
+
+static void tpd12s015_hpd_disable(struct drm_bridge *bridge)
+{
+   struct tpd12s015_device *tpd = to_tpd12s015(bridge);
+
+   gpiod_set_value_cansleep(tpd->ct_cp_hpd_gpio, 0);
+}
+
+static const struct drm_bridge_funcs tpd12s015_bridge_funcs = {
+   .attach = tpd12s015_attach,
+   .detach = tpd12s015_detach,
+   .detect = tpd12s015_detect,
+   .hpd_enable = tpd12s015_hpd_enable,
+   .hpd_disable= tpd12s015_hpd_disable,
+};
+
+static irqreturn_t tpd12s015_hpd_isr(int irq, void *data)
+{
+   struct tpd12s015_device *tpd = data;
+   struct drm_bridge *bridge = >bridge;
+
+   drm_bridge_hpd_notify(bridge, tpd12s015_detect(bridge));
+
+   return IRQ_HANDLED;
+}
+
+static int 

[PATCH v3 04/50] drm/bridge: Add connector-related bridge operations and data

2019-12-10 Thread Laurent Pinchart
To support implementation of DRM connectors on top of DRM bridges
instead of by bridges, the drm_bridge needs to expose new operations and
data:

- Output detection, hot-plug notification, mode retrieval and EDID
  retrieval operations
- Bitmask of supported operations
- Bridge output type
- I2C adapter for DDC access

Add and document these.

Three new bridge helper functions are also added to handle hot plug
notification in a way that is as transparent as possible for the
bridges.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Boris Brezillon 
---
Changes since v2:

- Add wrappers around the .detect(), .get_modes() and .get_edid()
  operations
- Warn bridge drivers about valid usage of the connector argument to
  .get_modes() and .get_edid()

Changes since v1:

- Make .hpd_enable() and .hpd_disable() optional
- Rename .lost_hotplug() to .hpd_notify()
- Add ddc field to drm_bridge
---
 drivers/gpu/drm/drm_bridge.c | 162 +
 include/drm/drm_bridge.h | 193 ++-
 2 files changed, 354 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index c2cf0c90fa26..473353bd762f 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -70,6 +70,8 @@ static LIST_HEAD(bridge_list);
  */
 void drm_bridge_add(struct drm_bridge *bridge)
 {
+   mutex_init(>hpd_mutex);
+
mutex_lock(_lock);
list_add_tail(>list, _list);
mutex_unlock(_lock);
@@ -86,6 +88,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
mutex_lock(_lock);
list_del_init(>list);
mutex_unlock(_lock);
+
+   mutex_destroy(>hpd_mutex);
 }
 EXPORT_SYMBOL(drm_bridge_remove);
 
@@ -516,6 +520,164 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge 
*bridge,
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
 
+/**
+ * drm_bridge_detect - check if anything is attached to the bridge output
+ * @bridge: bridge control structure
+ *
+ * If the bridge supports output detection, as reported by the
+ * DRM_BRIDGE_OP_DETECT bridge ops flag, call _bridge_funcs.detect for the
+ * bridge and return the connection status. Otherwise return
+ * connector_status_unknown.
+ *
+ * RETURNS:
+ * The detection status on success, or connector_status_unknown if the bridge
+ * doesn't support output detection.
+ */
+enum drm_connector_status drm_bridge_detect(struct drm_bridge *bridge)
+{
+   if (!(bridge->ops & DRM_BRIDGE_OP_DETECT))
+   return connector_status_unknown;
+
+   return bridge->funcs->detect(bridge);
+}
+EXPORT_SYMBOL_GPL(drm_bridge_detect);
+
+/**
+ * drm_bridge_get_modes - fill all modes currently valid for the sink into the
+ * @connector
+ * @bridge: bridge control structure
+ * @connector: the connector to fill with modes
+ *
+ * If the bridge supports output modes retrieval, as reported by the
+ * DRM_BRIDGE_OP_MODES bridge ops flag, call _bridge_funcs.get_modes to
+ * fill the connector with all valid modes and return the number of modes
+ * added. Otherwise return 0.
+ *
+ * RETURNS:
+ * The number of modes added to the connector.
+ */
+int drm_bridge_get_modes(struct drm_bridge *bridge,
+struct drm_connector *connector)
+{
+   if (!(bridge->ops & DRM_BRIDGE_OP_MODES))
+   return 0;
+
+   return bridge->funcs->get_modes(bridge, connector);
+}
+EXPORT_SYMBOL_GPL(drm_bridge_get_modes);
+
+/**
+ * drm_bridge_get_edid - get the EDID data of the connected display
+ * @bridge: bridge control structure
+ * @connector: the connector to read EDID for
+ *
+ * If the bridge supports output EDID retrieval, as reported by the
+ * DRM_BRIDGE_OP_EDID bridge ops flag, call _bridge_funcs.get_edid to
+ * get the EDID and return it. Otherwise return ERR_PTR(-ENOTSUPP).
+ *
+ * RETURNS:
+ * The retrieved EDID on success, or an error pointer otherwise.
+ */
+struct edid *drm_bridge_get_edid(struct drm_bridge *bridge,
+struct drm_connector *connector)
+{
+   if (!(bridge->ops & DRM_BRIDGE_OP_EDID))
+   return ERR_PTR(-ENOTSUPP);
+
+   return bridge->funcs->get_edid(bridge, connector);
+}
+EXPORT_SYMBOL_GPL(drm_bridge_get_edid);
+
+/**
+ * drm_bridge_hpd_enable - enable hot plug detection for the bridge
+ * @bridge: bridge control structure
+ * @cb: hot-plug detection callback
+ * @data: data to be passed to the hot-plug detection callback
+ *
+ * Call _bridge_funcs.hpd_enable if implemented and register the given @cb
+ * and @data as hot plug notification callback. From now on the @cb will be
+ * called with @data when an output status change is detected by the bridge,
+ * until hot plug notification gets disabled with drm_bridge_hpd_disable().
+ *
+ * Hot plug detection is supported only if the DRM_BRIDGE_OP_HPD flag is set in
+ * bridge->ops. This function shall not be called when the flag is not set.
+ *
+ * Only one hot plug detection callback can be registered at a time, it is 

[PATCH v3 11/50] drm/bridge: Add bridge driver for display connectors

2019-12-10 Thread Laurent Pinchart
Display connectors are modelled in DT as a device node, but have so far
been handled manually in several bridge drivers. This resulted in
duplicate code in several bridge drivers, with slightly different (and
thus confusing) logics.

In order to fix this, implement a bridge driver for display connectors.
The driver centralises logic for the DVI, HDMI, VGAn composite and
S-video connectors and exposes corresponding bridge operations.

This driver in itself doesn't solve the issue completely, changes in
bridge and display controller drivers are needed to make use of the new
connector driver.

Signed-off-by: Laurent Pinchart 
Reviewed-by: Maxime Ripard 
---
Changes since v2:

- Fall back to polling if the GPIO IRQ chip doesn't support
  edge-triggered interrupts

Changes since v1:

- Use drm_get_connector_type_name() instead of open-coding
  display_connector_type_name()
- Remove empty .hpd_enable() and .hpd_disable() operations
- Set bridge.ddc
---
 drivers/gpu/drm/bridge/Kconfig |  11 +
 drivers/gpu/drm/bridge/Makefile|   1 +
 drivers/gpu/drm/bridge/display-connector.c | 292 +
 3 files changed, 304 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/display-connector.c

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index bf1dfc71733b..f14f63e0f6df 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -27,6 +27,17 @@ config DRM_CDNS_DSI
  Support Cadence DPI to DSI bridge. This is an internal
  bridge and is meant to be directly embedded in a SoC.
 
+config DRM_DISPLAY_CONNECTOR
+   tristate "Display connector support"
+   depends on OF
+   help
+ Driver for display connectors with support for DDC and hot-plug
+ detection. Most display controller handle display connectors
+ internally and don't need this driver, but the DRM subsystem is
+ moving towards separating connector handling from display controllers
+ on ARM-based platforms. Saying Y here when this driver is not needed
+ will not cause any issue.
+
 config DRM_LVDS_ENCODER
tristate "Transparent parallel to LVDS encoder support"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index a2892e3018aa..bef919d3bca6 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
+obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
 obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o
 obj-$(CONFIG_DRM_MEGACHIPS_STDP_GE_B850V3_FW) += 
megachips-stdp-ge-b850v3-fw.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
diff --git a/drivers/gpu/drm/bridge/display-connector.c 
b/drivers/gpu/drm/bridge/display-connector.c
new file mode 100644
index ..22407f7a5549
--- /dev/null
+++ b/drivers/gpu/drm/bridge/display-connector.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Laurent Pinchart 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+struct display_connector {
+   struct drm_bridge   bridge;
+
+   const char  *label;
+   struct gpio_desc*hpd_gpio;
+   int hpd_irq;
+};
+
+static inline struct display_connector *
+to_display_connector(struct drm_bridge *bridge)
+{
+   return container_of(bridge, struct display_connector, bridge);
+}
+
+static int display_connector_attach(struct drm_bridge *bridge,
+   enum drm_bridge_attach_flags flags)
+{
+   return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
+}
+
+static enum drm_connector_status
+display_connector_detect(struct drm_bridge *bridge)
+{
+   struct display_connector *conn = to_display_connector(bridge);
+
+   if (conn->hpd_gpio) {
+   if (gpiod_get_value_cansleep(conn->hpd_gpio))
+   return connector_status_connected;
+   else
+   return connector_status_disconnected;
+   }
+
+   if (conn->bridge.ddc && drm_probe_ddc(conn->bridge.ddc))
+   return connector_status_connected;
+
+   switch (conn->bridge.type) {
+   case DRM_MODE_CONNECTOR_DVIA:
+   case DRM_MODE_CONNECTOR_DVID:
+   case DRM_MODE_CONNECTOR_DVII:
+   case DRM_MODE_CONNECTOR_HDMIA:
+   case DRM_MODE_CONNECTOR_HDMIB:
+   /*
+* For DVI and HDMI connectors a DDC probe failure indicates
+* that no cable is connected.
+*/
+   return connector_status_disconnected;
+
+   case DRM_MODE_CONNECTOR_Composite:
+   case DRM_MODE_CONNECTOR_SVIDEO:
+   case DRM_MODE_CONNECTOR_VGA:
+   default:
+   /*
+* Composite and S-Video connectors have no other 

[PATCH v3] drm/mcde: Some fixes to handling video mode

2019-12-10 Thread Linus Walleij
The video DSI mode had not really been tested. These fixes makes
it more likely to work on real hardware:
- Put the active width (x width) in the right bits and the VSA
  (vertical sync active) in the right bits (those were swapped).
- Calculate the packet sizes in bytes as in the vendor driver,
  rather than in bits. Test the calculations agains a
  spreadsheet and confirmed by debug prints to be reasonable.
- Also verified the register values with relative confidence
  to register dumps from the Samsung GT-I8190 boot loader
  graphics. We are not identical but not off by far either.
- Error out if the current mode and refresh frequency doesn't
  work out. (In the future we may simply want to scale down
  the vrefresh.)
- Handle negative result in front/back/sync packages and fall
  back to zero like in the vendor driver.
- Put in lots of clarifying comments and references to the
  documentation where the code is hard to understand.

Cc: Stephan Gerhold 
Fixes: 5fc537bfd000 ("drm/mcde: Add new driver for ST-Ericsson MCDE")
Signed-off-by: Linus Walleij 
---
ChangeLog v2->v3:
- Calculate toward actual HS rate of the clock rather than the
  idealized rate provided by the panel resolution, this is what
  the vendor driver does.
- Add much comments and elaborate with references to the manual
  so the code can be understood as far as possible.
- Compared register dumps to that on the Samsung GT-I8190 (Golden)
  boot loader settings. We are now reasonably close to these,
  it may be that the boot loader driver is using slightly different
  settings for porches and syncs etc. But all figures makes sense.
- Duplicated the vendor code in a spread sheet and compared to
  what this code gives and we have an identical match with one
  small exception that the vendor code adds a small padding of 2
  lines to the vertical blanking area. This looks weird and might
  be some hackishly specified porch.
- Rename the "bpp" variable to "cpp" since it is "chars per pixel"
  this was confusingly named in the vendor driver and it got
  carried over.
- Assign the SETTING2_EXACT_BURST_LIMIT by first shifting
  then masking.
- Also mask with the inverse of DSI_VID_BLKSIZE1_BLKEOL_PCK_MASK
  before writing blkeol into DSI_VID_BLKSIZE1, so we make sure
  to zero these bits first.
- Also mask with DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK_MASK
  when writing event package length.
- Comb through the code and compare it to vendor code and try
  to get closer to doing what the vendor driver is doing.
ChangeLog v1->v2:
- Fix some more comments so we understand what is going on.
- Set up the maximum line limit size in the right register
  instead of setting it in the burst size register portion.
- Set some default wakeup time other than zero (still need
  fixing more).
---
 drivers/gpu/drm/mcde/mcde_dsi.c | 221 +---
 1 file changed, 178 insertions(+), 43 deletions(-)

diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index 21cee4d9d2fd..e9713953c4ac 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -379,13 +379,14 @@ void mcde_dsi_te_request(struct mipi_dsi_device *mdsi)
 static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
  const struct drm_display_mode *mode)
 {
-   u8 bpp = mipi_dsi_pixel_format_to_bpp(d->mdsi->format);
+   /* cpp, characters per pixel, number of bytes per pixel */
+   u8 cpp = mipi_dsi_pixel_format_to_bpp(d->mdsi->format) / 8;
+   u64 pclk;
u64 bpl;
-   u32 hfp;
-   u32 hbp;
-   u32 hsa;
+   int hfp;
+   int hbp;
+   int hsa;
u32 blkline_pck, line_duration;
-   u32 blkeol_pck, blkeol_duration;
u32 val;
 
val = 0;
@@ -422,11 +423,21 @@ static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
return;
}
 
-   /* TODO: TVG could be enabled here */
+   /* TODO: TVG (test video generator) could be enabled here */
 
-   /* Send blanking packet */
+   /*
+* During vertical blanking: go to LP mode
+* Like with the EOL setting, if this is not set, the EOL area will be
+* filled with NULL or blanking packets in the vblank area.
+* FIXME: some Samsung phones and display panels such as s6e63m0 use
+* DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_BLANKING here instead,
+* figure out how to properly configure that from the panel.
+*/
val |= DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_LP_0;
-   /* Send EOL packet */
+   /*
+* During EOL: go to LP mode. If this is not set, the EOL area will be
+* filled with NULL or blanking packets.
+*/
val |= DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_0;
/* Recovery mode 1 */
val |= 1 << DSI_VID_MAIN_CTL_RECOVERY_MODE_SHIFT;
@@ -434,13 +445,13 @@ static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
writel(val, d->regs + DSI_VID_MAIN_CTL);
 
/* 

[PATCH 0/5] DRM and i915 fixes to handle hotplug/unplug for 8K tiled displays

2019-12-10 Thread Manasi Navare
With these patches now DRM and i915 gracefully handles hotplugging and 
unplugging
of either the master or slave connectors in case of tiled displays.

These patches ensure proper handling of just 1 tile connected and also fixes
the teardown (disable sequence)

Case1: Boot with single port connected
drm/ fbcon fallsback to the next lower non tiled mode
Case2: Now hotplug second port
Here if fbcon has been already resized to lower mode we cannot change
the fbcon size and it still displays lower non tiled mode
Case3: With both ports connected
Here it displays the full 8K tiled mode
Case4: Unplug master/slave:
It does a full modeset and displays next lower mode
Case5: Hotplug second port back in:
It now does a full modeset again to display full 8K


Manasi Navare (5):
  drm: Handle connector tile support only for modes that match tile size
  drm/fbdev: Fallback to non tiled mode if all tiles not present
  drm/i915/dp: Make sure all tiled connectors get added to the state
with full modeset
  drm/i915/dp: Make port sync mode assignments only if all tiles present
  drm/i915/dp: Disable Port sync mode correctly on teardown

 drivers/gpu/drm/drm_client_modeset.c |  72 
 drivers/gpu/drm/drm_fb_helper.c  |   4 +-
 drivers/gpu/drm/i915/display/intel_display.c | 109 ++-
 3 files changed, 181 insertions(+), 4 deletions(-)

-- 
2.19.1

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH 5/5] drm/i915/dp: Disable Port sync mode correctly on teardown

2019-12-10 Thread Manasi Navare
While clearing the Ports ync mode enable and master select bits
we need to make sure that we perform a RMW for disable else
it sets the other bits casuing unwanted sideeffects.

Bugzilla: https://gitlab.freedesktop.org/drm/intel/issues/5
Cc: Ville Syrjälä 
Cc: Jani Nikula 
Fixes: 51528afe7c5e ("drm/i915/display/icl: Disable transcoder port sync as 
part of crtc_disable() sequence")
Signed-off-by: Manasi Navare 
---
 drivers/gpu/drm/i915/display/intel_display.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c 
b/drivers/gpu/drm/i915/display/intel_display.c
index c0a2dab3fe67..3fccda0f1f36 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -4599,7 +4599,8 @@ static void icl_disable_transcoder_port_sync(const struct 
intel_crtc_state *old_
  transcoder_name(old_crtc_state->cpu_transcoder));
 
reg = TRANS_DDI_FUNC_CTL2(old_crtc_state->cpu_transcoder);
-   trans_ddi_func_ctl2_val = ~(PORT_SYNC_MODE_ENABLE |
+   trans_ddi_func_ctl2_val = I915_READ(reg);
+   trans_ddi_func_ctl2_val &= ~(PORT_SYNC_MODE_ENABLE |
PORT_SYNC_MODE_MASTER_SELECT_MASK);
I915_WRITE(reg, trans_ddi_func_ctl2_val);
 }
-- 
2.19.1

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH 4/5] drm/i915/dp: Make port sync mode assignments only if all tiles present

2019-12-10 Thread Manasi Navare
Add an extra check before making master slave assignments for tiled
displays to make sure we make these assignments only if all tiled
connectors are present. If not then initialize the state to defaults
so it does a normal non tiled modeset without transcoder port sync.

Bugzilla: https://gitlab.freedesktop.org/drm/intel/issues/5
Cc: Ville Syrjälä 
Signed-off-by: Manasi Navare 
---
 drivers/gpu/drm/i915/display/intel_display.c | 28 ++--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c 
b/drivers/gpu/drm/i915/display/intel_display.c
index 7263eaa66cda..c0a2dab3fe67 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -12048,6 +12048,12 @@ static bool c8_planes_changed(const struct 
intel_crtc_state *new_crtc_state)
return !old_crtc_state->c8_planes != !new_crtc_state->c8_planes;
 }
 
+static void initialize_trans_port_sync_mode_state(struct intel_crtc_state 
*crtc_state)
+{
+   crtc_state->master_transcoder = INVALID_TRANSCODER;
+   crtc_state->sync_mode_slaves_mask = 0;
+}
+
 static int icl_add_sync_mode_crtcs(struct intel_crtc_state *crtc_state)
 {
struct drm_crtc *crtc = crtc_state->uapi.crtc;
@@ -12059,11 +12065,22 @@ static int icl_add_sync_mode_crtcs(struct 
intel_crtc_state *crtc_state)
struct drm_crtc *master_crtc = NULL;
struct drm_crtc_state *master_crtc_state;
struct intel_crtc_state *master_pipe_config;
-   int i, tile_group_id;
+   int i, tile_group_id = 0, num_tiled_conns = 0;
 
if (INTEL_GEN(dev_priv) < 11)
return 0;
 
+   /* If all tiles not present do not make master slave assignments
+* Here we assume all tiles belong to the same tile group for now.
+*/
+   for_each_new_connector_in_state(>base, connector, 
connector_state, i) {
+   if (connector->has_tile) {
+   if (!tile_group_id)
+   tile_group_id = connector->tile_group->id;
+   num_tiled_conns++;
+   }
+   }
+
/*
 * In case of tiled displays there could be one or more slaves but 
there is
 * only one master. Lets make the CRTC used by the connector 
corresponding
@@ -12077,8 +12094,15 @@ static int icl_add_sync_mode_crtcs(struct 
intel_crtc_state *crtc_state)
if (!connector->has_tile)
continue;
if (crtc_state->hw.mode.hdisplay != connector->tile_h_size ||
-   crtc_state->hw.mode.vdisplay != connector->tile_v_size)
+   crtc_state->hw.mode.vdisplay != connector->tile_v_size) {
+   initialize_trans_port_sync_mode_state(crtc_state);
return 0;
+   }
+   if (connector->tile_group->id == tile_group_id &&
+   num_tiled_conns < connector->num_h_tile * 
connector->num_v_tile) {
+   initialize_trans_port_sync_mode_state(crtc_state);
+   return 0;
+   }
if (connector->tile_h_loc == connector->num_h_tile - 1 &&
connector->tile_v_loc == connector->num_v_tile - 1)
continue;
-- 
2.19.1

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH 2/5] drm/fbdev: Fallback to non tiled mode if all tiles not present

2019-12-10 Thread Manasi Navare
In case of tiled displays, if we hotplug just one connector,
fbcon currently just selects the preferred mode and if it is
tiled mode then that becomes a problem if rest of the tiles are
not present.
So in the fbdev driver on hotplug when we probe the client modeset,
if we dont find all the connectors for all tiles, then on a connector
with one tile, just fallback to the first available non tiled mode
to display over a single connector.
On the hotplug of the consecutive tiled connectors, if the tiled mode
no longer exists because of fbcon size limitation, then return
no modes for consecutive tiles but retain the non tiled mode
on the 0th tile.
Use the same logic in case of connected boot case as well.
This has been tested with Dell UP328K tiled monitor.

v3:
* Chcek Num tiled conns that are connected (Manasi)
v2:
* Set the modes on consecutive hotplugged tiles to no mode
if tiled mode is pruned (Dave)
v1:
* Just handle the 1st connector hotplug case
* v1 Reviewed-by: Dave Airlie 

Bugzilla: https://gitlab.freedesktop.org/drm/intel/issues/5
Suggested-by: Ville Syrjälä 
Suggested-by: Dave Airlie 
Cc: Ville Syrjälä 
Cc: Dave Airlie 
Signed-off-by: Manasi Navare 
Reviewed-by: Dave Airlie  (v2)
---
 drivers/gpu/drm/drm_client_modeset.c | 72 
 1 file changed, 72 insertions(+)

diff --git a/drivers/gpu/drm/drm_client_modeset.c 
b/drivers/gpu/drm/drm_client_modeset.c
index 895b73f23079..6d4a29e99ae2 100644
--- a/drivers/gpu/drm/drm_client_modeset.c
+++ b/drivers/gpu/drm/drm_client_modeset.c
@@ -114,6 +114,33 @@ drm_client_find_modeset(struct drm_client_dev *client, 
struct drm_crtc *crtc)
return NULL;
 }
 
+static struct drm_display_mode *
+drm_connector_get_tiled_mode(struct drm_connector *connector)
+{
+   struct drm_display_mode *mode;
+
+   list_for_each_entry(mode, >modes, head) {
+   if (mode->hdisplay == connector->tile_h_size &&
+   mode->vdisplay == connector->tile_v_size)
+   return mode;
+   }
+   return NULL;
+}
+
+static struct drm_display_mode *
+drm_connector_fallback_non_tiled_mode(struct drm_connector *connector)
+{
+   struct drm_display_mode *mode;
+
+   list_for_each_entry(mode, >modes, head) {
+   if (mode->hdisplay == connector->tile_h_size &&
+   mode->vdisplay == connector->tile_v_size)
+   continue;
+   return mode;
+   }
+   return NULL;
+}
+
 static struct drm_display_mode *
 drm_connector_has_preferred_mode(struct drm_connector *connector, int width, 
int height)
 {
@@ -348,8 +375,15 @@ static bool drm_client_target_preferred(struct 
drm_connector **connectors,
struct drm_connector *connector;
u64 conn_configured = 0;
int tile_pass = 0;
+   int num_tiled_conns = 0;
int i;
 
+   for (i = 0; i < connector_count; i++) {
+   if (connectors[i]->has_tile &&
+   connectors[i]->status == connector_status_connected)
+   num_tiled_conns++;
+   }
+
 retry:
for (i = 0; i < connector_count; i++) {
connector = connectors[i];
@@ -399,6 +433,28 @@ static bool drm_client_target_preferred(struct 
drm_connector **connectors,
list_for_each_entry(modes[i], >modes, head)
break;
}
+   /*
+* In case of tiled mode if all tiles not present fallback to
+* first available non tiled mode.
+* After all tiles are present, try to find the tiled mode
+* for all and if tiled mode not present due to fbcon size
+* limitations, use first non tiled mode only for
+* tile 0,0 and set to no mode for all other tiles.
+*/
+   if (connector->has_tile) {
+   if (num_tiled_conns <
+   connector->num_h_tile * connector->num_v_tile ||
+   (connector->tile_h_loc == 0 &&
+connector->tile_v_loc == 0 &&
+!drm_connector_get_tiled_mode(connector))) {
+   DRM_DEBUG_KMS("Falling back to non tiled mode 
on Connector %d\n",
+ connector->base.id);
+   modes[i] = 
drm_connector_fallback_non_tiled_mode(connector);
+   } else {
+   modes[i] = 
drm_connector_get_tiled_mode(connector);
+   }
+   }
+
DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
  "none");
conn_configured |= BIT_ULL(i);
@@ -515,6 +571,7 @@ static bool drm_client_firmware_config(struct 
drm_client_dev *client,
bool fallback = true, ret = true;
int num_connectors_enabled = 0;
int num_connectors_detected 

[PATCH 1/5] drm: Handle connector tile support only for modes that match tile size

2019-12-10 Thread Manasi Navare
DRM Fb driver expects multiple CRTCs if it sees connector->has_tile
is set, but we need to handle tile support and look for multiple CRTCs
only for the modes that match the tile size. The other modes should
be able to be displayed without tile support or uisng single CRTC.

This patch adds the check to match the tile size with requested mode
to handle the tile support.

Cc: Ville Syrjälä 
Cc: Maarten Lankhorst 
Cc: Jani Nikula 
Cc: Dave Airlie 
Signed-off-by: Manasi Navare 
---
 drivers/gpu/drm/drm_fb_helper.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index fb9bff0f4581..4978363714a9 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1558,7 +1558,9 @@ static int drm_fb_helper_single_fb_probe(struct 
drm_fb_helper *fb_helper,
for (j = 0; j < mode_set->num_connectors; j++) {
struct drm_connector *connector = 
mode_set->connectors[j];
 
-   if (connector->has_tile) {
+   if (connector->has_tile &&
+   desired_mode->hdisplay == connector->tile_h_size &&
+   desired_mode->vdisplay == connector->tile_v_size) {
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: */
-- 
2.19.1

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH 3/5] drm/i915/dp: Make sure all tiled connectors get added to the state with full modeset

2019-12-10 Thread Manasi Navare
In case of tiled displays, all the tiles are linke dto each other
for transcoder port sync. So in intel_atomic_check() we need to make
sure that we add all the tiles to the modeset and if one of the
tiles needs a full modeset then mark all other tiles for a full modeset.

Suggested-by: Ville Syrjälä 
Cc: Ville Syrjälä 
Cc: José Roberto de Souza 
Bugzilla: https://gitlab.freedesktop.org/drm/intel/issues/5
Signed-off-by: Manasi Navare 
---
 drivers/gpu/drm/i915/display/intel_display.c | 78 
 1 file changed, 78 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c 
b/drivers/gpu/drm/i915/display/intel_display.c
index 803993a01ca7..7263eaa66cda 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -14066,6 +14066,80 @@ static int intel_atomic_check_crtcs(struct 
intel_atomic_state *state)
return 0;
 }
 
+static int
+intel_dp_modeset_all_tiles(struct drm_i915_private *dev_priv,
+  struct intel_atomic_state *state, int tile_grp_id)
+{
+   struct drm_connector *conn_iter;
+   struct drm_connector_list_iter conn_list_iter;
+   struct drm_crtc_state *crtc_state;
+
+   drm_connector_list_iter_begin(_priv->drm, _list_iter);
+   drm_for_each_connector_iter(conn_iter, _list_iter) {
+   struct drm_connector_state *conn_iter_state;
+
+   if (!conn_iter->has_tile)
+   continue;
+   conn_iter_state = drm_atomic_get_connector_state(>base,
+conn_iter);
+   if (IS_ERR(conn_iter_state)) {
+   drm_connector_list_iter_end(_list_iter);
+   return PTR_ERR(conn_iter_state);
+   }
+
+   if (!conn_iter_state->crtc)
+   continue;
+
+   if (conn_iter->tile_group->id != tile_grp_id)
+   continue;
+
+   crtc_state = drm_atomic_get_crtc_state(>base, 
conn_iter_state->crtc);
+   if (IS_ERR(crtc_state)) {
+   drm_connector_list_iter_end(_list_iter);
+   return PTR_ERR(conn_iter_state);
+   }
+   crtc_state->mode_changed = true;
+   }
+   drm_connector_list_iter_end(_list_iter);
+
+   return 0;
+}
+
+static int
+intel_dp_atomic_trans_port_sync_check(struct drm_i915_private *dev_priv,
+ struct intel_atomic_state *state)
+{
+   struct drm_connector *connector;
+   struct drm_crtc_state *crtc_state;
+   struct drm_connector_state *connector_state;
+   int i, ret, tile_grp_id = 0;
+
+   if (INTEL_GEN(dev_priv) < 11)
+   return 0;
+
+   /* Is tiled, mark all other tiled CRTCs as needing a modeset */
+   for_each_new_connector_in_state(>base, connector, 
connector_state, i) {
+   if (!connector->has_tile)
+   continue;
+   if (connector_state->crtc &&
+   tile_grp_id != connector->tile_group->id) {
+   crtc_state = drm_atomic_get_new_crtc_state(>base,
+  
connector_state->crtc);
+   if (!drm_atomic_crtc_needs_modeset(crtc_state))
+   continue;
+
+   tile_grp_id = connector->tile_group->id;
+   } else
+   continue;
+
+   ret = intel_dp_modeset_all_tiles(dev_priv, state, tile_grp_id);
+   if (ret)
+   return ret;
+   }
+
+   return 0;
+}
+
 /**
  * intel_atomic_check - validate state object
  * @dev: drm device
@@ -14093,6 +14167,10 @@ static int intel_atomic_check(struct drm_device *dev,
if (ret)
goto fail;
 
+   ret = intel_dp_atomic_trans_port_sync_check(dev_priv, state);
+   if (ret)
+   goto fail;
+
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) {
if (!needs_modeset(new_crtc_state)) {
-- 
2.19.1

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH AUTOSEL 4.4 65/71] fbtft: Make sure string is NULL terminated

2019-12-10 Thread Sasha Levin
From: Andy Shevchenko 

[ Upstream commit 21f585480deb4bcf0d92b08879c35d066dfee030 ]

New GCC warns about inappropriate use of strncpy():

drivers/staging/fbtft/fbtft-core.c: In function ‘fbtft_framebuffer_alloc’:
drivers/staging/fbtft/fbtft-core.c:665:2: warning: ‘strncpy’ specified bound 16 
equals destination size [-Wstringop-truncation]
  665 |  strncpy(info->fix.id, dev->driver->name, 16);
  |  ^~~~

Later on the copy is being used with the assumption to be NULL terminated.
Make sure string is NULL terminated by switching to snprintf().

Signed-off-by: Andy Shevchenko 
Link: 
https://lore.kernel.org/r/20191120095716.26628-1-andriy.shevche...@linux.intel.com
Signed-off-by: Greg Kroah-Hartman 
Signed-off-by: Sasha Levin 
---
 drivers/staging/fbtft/fbtft-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/staging/fbtft/fbtft-core.c 
b/drivers/staging/fbtft/fbtft-core.c
index 15937e0ef4d96..36bf71989637d 100644
--- a/drivers/staging/fbtft/fbtft-core.c
+++ b/drivers/staging/fbtft/fbtft-core.c
@@ -765,7 +765,7 @@ struct fb_info *fbtft_framebuffer_alloc(struct 
fbtft_display *display,
fbdefio->deferred_io = fbtft_deferred_io;
fb_deferred_io_init(info);
 
-   strncpy(info->fix.id, dev->driver->name, 16);
+   snprintf(info->fix.id, sizeof(info->fix.id), "%s", dev->driver->name);
info->fix.type =   FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
info->fix.xpanstep =   0;
-- 
2.20.1

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH AUTOSEL 4.4 30/71] drm/gma500: fix memory disclosures due to uninitialized bytes

2019-12-10 Thread Sasha Levin
From: Kangjie Lu 

[ Upstream commit ec3b7b6eb8c90b52f61adff11b6db7a8db34de19 ]

"clock" may be copied to "best_clock". Initializing best_clock
is not sufficient. The fix initializes clock as well to avoid
memory disclosures and informaiton leaks.

Signed-off-by: Kangjie Lu 
Signed-off-by: Daniel Vetter 
Link: 
https://patchwork.freedesktop.org/patch/msgid/20191018044150.1899-1-k...@umn.edu
Signed-off-by: Sasha Levin 
---
 drivers/gpu/drm/gma500/oaktrail_crtc.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c 
b/drivers/gpu/drm/gma500/oaktrail_crtc.c
index 1048f0c7c6ce7..31e0899035f98 100644
--- a/drivers/gpu/drm/gma500/oaktrail_crtc.c
+++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c
@@ -139,6 +139,7 @@ static bool mrst_sdvo_find_best_pll(const struct 
gma_limit_t *limit,
s32 freq_error, min_error = 10;
 
memset(best_clock, 0, sizeof(*best_clock));
+   memset(, 0, sizeof(clock));
 
for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
for (clock.n = limit->n.min; clock.n <= limit->n.max;
@@ -195,6 +196,7 @@ static bool mrst_lvds_find_best_pll(const struct 
gma_limit_t *limit,
int err = target;
 
memset(best_clock, 0, sizeof(*best_clock));
+   memset(, 0, sizeof(clock));
 
for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max;
-- 
2.20.1

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


  1   2   3   4   >