diff options
| author | Dave Airlie <airlied@redhat.com> | 2022-04-22 06:43:52 +1000 | 
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2022-04-22 11:15:30 +1000 | 
| commit | 19df0cfa258cd42f7f106f6085f1e625f26283db (patch) | |
| tree | be212959c494f95279fd160ba04e9b498d60be8d | |
| parent | c54b39a565227538c52ead2349eb17d54aadd6f7 (diff) | |
| parent | 40d8d4bd06720aed6c1125bab7296c57de4f1157 (diff) | |
Merge tag 'drm-misc-next-2022-04-21' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 5.19-rc1
UAPI Changes:
Cross-subsystem Changes:
  - of: Create a platform_device for offb
Core Changes:
  - edid: block read refactoring
  - ttm: Add common debugfs code for resource managers
Driver Changes:
  - bridges:
    - adv7611: Enable DRM_BRIDGE_OP_HPD if there's an interrupt
    - anx7625: Fill ELD if no monitor is connected
    - dw_hdmi: Add General Parallel Audio support
    - icn6211: Add data-lanes DT property
    - new driver: Lontium LT9211
  - nouveau: make some structures static
  - tidss: Reset DISPC on startup
  - solomon: SPI Support and DT bindings improvements
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <maxime@cerno.tech>
Link: https://patchwork.freedesktop.org/patch/msgid/20220421065948.2pyp3j7acxtl6pz5@houat
146 files changed, 4025 insertions, 1843 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/chipone,icn6211.yaml b/Documentation/devicetree/bindings/display/bridge/chipone,icn6211.yaml index 7257fd0ae4da..4f0b7c71313c 100644 --- a/Documentation/devicetree/bindings/display/bridge/chipone,icn6211.yaml +++ b/Documentation/devicetree/bindings/display/bridge/chipone,icn6211.yaml @@ -41,10 +41,26 @@ properties:      properties:        port@0: -        $ref: /schemas/graph.yaml#/properties/port +        $ref: /schemas/graph.yaml#/$defs/port-base +        unevaluatedProperties: false          description:            Video port for MIPI DSI input +        properties: +          endpoint: +            $ref: /schemas/media/video-interfaces.yaml# +            unevaluatedProperties: false + +            properties: +              data-lanes: +                description: array of physical DSI data lane indexes. +                minItems: 1 +                items: +                  - const: 1 +                  - const: 2 +                  - const: 3 +                  - const: 4 +        port@1:          $ref: /schemas/graph.yaml#/properties/port          description: diff --git a/Documentation/devicetree/bindings/display/bridge/lontium,lt9211.yaml b/Documentation/devicetree/bindings/display/bridge/lontium,lt9211.yaml new file mode 100644 index 000000000000..9a6e9b25d14a --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/lontium,lt9211.yaml @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/lontium,lt9211.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lontium LT9211 DSI/LVDS/DPI to DSI/LVDS/DPI bridge. + +maintainers: +  - Marek Vasut <marex@denx.de> + +description: | +  The LT9211 are bridge devices which convert Single/Dual-Link DSI/LVDS +  or Single DPI to Single/Dual-Link DSI/LVDS or Single DPI. + +properties: +  compatible: +    enum: +      - lontium,lt9211 + +  reg: +    maxItems: 1 + +  interrupts: +    maxItems: 1 + +  reset-gpios: +    maxItems: 1 +    description: GPIO connected to active high RESET pin. + +  vccio-supply: +    description: Regulator for 1.8V IO power. + +  ports: +    $ref: /schemas/graph.yaml#/properties/ports + +    properties: +      port@0: +        $ref: /schemas/graph.yaml#/properties/port +        description: +          Primary MIPI DSI port-1 for MIPI input or +          LVDS port-1 for LVDS input or DPI input. + +      port@1: +        $ref: /schemas/graph.yaml#/properties/port +        description: +          Additional MIPI port-2 for MIPI input or LVDS port-2 +          for LVDS input. Used in combination with primary +          port-1 to drive higher resolution displays + +      port@2: +        $ref: /schemas/graph.yaml#/properties/port +        description: +          Primary MIPI DSI port-1 for MIPI output or +          LVDS port-1 for LVDS output or DPI output. + +      port@3: +        $ref: /schemas/graph.yaml#/properties/port +        description: +          Additional MIPI port-2 for MIPI output or LVDS port-2 +          for LVDS output. Used in combination with primary +          port-1 to drive higher resolution displays. + +    required: +      - port@0 +      - port@2 + +required: +  - compatible +  - reg +  - vccio-supply +  - ports + +additionalProperties: false + +examples: +  - | +    #include <dt-bindings/gpio/gpio.h> +    #include <dt-bindings/interrupt-controller/irq.h> + +    i2c { +      #address-cells = <1>; +      #size-cells = <0>; + +      hdmi-bridge@3b { +        compatible = "lontium,lt9211"; +        reg = <0x3b>; + +        reset-gpios = <&tlmm 128 GPIO_ACTIVE_HIGH>; +        interrupts-extended = <&tlmm 84 IRQ_TYPE_EDGE_FALLING>; + +        vccio-supply = <<9211_1v8>; + +        ports { +          #address-cells = <1>; +          #size-cells = <0>; + +          port@0 { +            reg = <0>; + +            endpoint { +              remote-endpoint = <&dsi0_out>; +            }; +          }; + +          port@2 { +            reg = <2>; + +            endpoint { +              remote-endpoint = <&panel_in_lvds>; +            }; +          }; +        }; +      }; +    }; + +... diff --git a/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml b/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml index 9baafd0c42dd..3fbd87c2c120 100644 --- a/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml +++ b/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml @@ -12,11 +12,22 @@ maintainers:  properties:    compatible: -    enum: -      - solomon,ssd1305fb-i2c -      - solomon,ssd1306fb-i2c -      - solomon,ssd1307fb-i2c -      - solomon,ssd1309fb-i2c +    oneOf: +      # Deprecated compatible strings +      - items: +          - enum: +              - solomon,ssd1305fb-i2c +              - solomon,ssd1306fb-i2c +              - solomon,ssd1307fb-i2c +              - solomon,ssd1309fb-i2c +        deprecated: true +      - items: +          - enum: +              - sinowealth,sh1106 +              - solomon,ssd1305 +              - solomon,ssd1306 +              - solomon,ssd1307 +              - solomon,ssd1309    reg:      maxItems: 1 @@ -27,9 +38,20 @@ properties:    reset-gpios:      maxItems: 1 +  # Only required for SPI +  dc-gpios: +    description: +      GPIO connected to the controller's D/C# (Data/Command) pin, +      that is needed for 4-wire SPI to tell the controller if the +      data sent is for a command register or the display data RAM +    maxItems: 1 +    vbat-supply:      description: The supply for VBAT +  # Only required for SPI +  spi-max-frequency: true +    solomon,height:      $ref: /schemas/types.yaml#/definitions/uint32      default: 16 @@ -135,7 +157,21 @@ allOf:        properties:          compatible:            contains: -            const: solomon,ssd1305fb-i2c +            const: sinowealth,sh1106 +    then: +      properties: +        solomon,dclk-div: +          default: 1 +        solomon,dclk-frq: +          default: 5 + +  - if: +      properties: +        compatible: +          contains: +            enum: +              - solomon,ssd1305-i2c +              - solomon,ssd1305      then:        properties:          solomon,dclk-div: @@ -147,7 +183,9 @@ allOf:        properties:          compatible:            contains: -            const: solomon,ssd1306fb-i2c +            enum: +              - solomon,ssd1306-i2c +              - solomon,ssd1306      then:        properties:          solomon,dclk-div: @@ -159,7 +197,9 @@ allOf:        properties:          compatible:            contains: -            const: solomon,ssd1307fb-i2c +            enum: +              - solomon,ssd1307-i2c +              - solomon,ssd1307      then:        properties:          solomon,dclk-div: @@ -173,7 +213,9 @@ allOf:        properties:          compatible:            contains: -            const: solomon,ssd1309fb-i2c +            enum: +              - solomon,ssd1309-i2c +              - solomon,ssd1309      then:        properties:          solomon,dclk-div: @@ -189,15 +231,15 @@ examples:              #address-cells = <1>;              #size-cells = <0>; -            ssd1307: oled@3c { -                    compatible = "solomon,ssd1307fb-i2c"; +            ssd1307_i2c: oled@3c { +                    compatible = "solomon,ssd1307";                      reg = <0x3c>;                      pwms = <&pwm 4 3000>;                      reset-gpios = <&gpio2 7>;              }; -            ssd1306: oled@3d { -                    compatible = "solomon,ssd1306fb-i2c"; +            ssd1306_i2c: oled@3d { +                    compatible = "solomon,ssd1306";                      reg = <0x3c>;                      pwms = <&pwm 4 3000>;                      reset-gpios = <&gpio2 7>; @@ -207,3 +249,30 @@ examples:                      solomon,lookup-table = /bits/ 8 <0x3f 0x3f 0x3f 0x3f>;              };      }; +  - | +    spi { +            #address-cells = <1>; +            #size-cells = <0>; + +            ssd1307_spi: oled@0 { +                    compatible = "solomon,ssd1307"; +                    reg = <0x0>; +                    pwms = <&pwm 4 3000>; +                    reset-gpios = <&gpio2 7>; +                    dc-gpios = <&gpio2 8>; +                    spi-max-frequency = <10000000>; +            }; + +            ssd1306_spi: oled@1 { +                    compatible = "solomon,ssd1306"; +                    reg = <0x1>; +                    pwms = <&pwm 4 3000>; +                    reset-gpios = <&gpio2 7>; +                    dc-gpios = <&gpio2 8>; +                    spi-max-frequency = <10000000>; +                    solomon,com-lrremap; +                    solomon,com-invdir; +                    solomon,com-offset = <32>; +                    solomon,lookup-table = /bits/ 8 <0x3f 0x3f 0x3f 0x3f>; +            }; +    }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 01430973ecec..79b72e370ade 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1130,6 +1130,8 @@ patternProperties:      description: Sinlinx Electronics Technology Co., LTD    "^sinovoip,.*":      description: SinoVoip Co., Ltd +  "^sinowealth,.*": +    description: SINO WEALTH Electronic Ltd.    "^sipeed,.*":      description: Shenzhen Sipeed Technology Co., Ltd.    "^sirf,.*": diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 775d3afb4169..79795857be3e 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -216,7 +216,8 @@ static bool dma_buf_poll_add_cb(struct dma_resv *resv, bool write,  	struct dma_fence *fence;  	int r; -	dma_resv_for_each_fence(&cursor, resv, write, fence) { +	dma_resv_for_each_fence(&cursor, resv, dma_resv_usage_rw(write), +				fence) {  		dma_fence_get(fence);  		r = dma_fence_add_callback(fence, &dcb->cb, dma_buf_poll_cb);  		if (!r) @@ -660,12 +661,24 @@ static struct sg_table * __map_dma_buf(struct dma_buf_attachment *attach,  				       enum dma_data_direction direction)  {  	struct sg_table *sg_table; +	signed long ret;  	sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction); +	if (IS_ERR_OR_NULL(sg_table)) +		return sg_table; + +	if (!dma_buf_attachment_is_dynamic(attach)) { +		ret = dma_resv_wait_timeout(attach->dmabuf->resv, +					    DMA_RESV_USAGE_KERNEL, true, +					    MAX_SCHEDULE_TIMEOUT); +		if (ret < 0) { +			attach->dmabuf->ops->unmap_dma_buf(attach, sg_table, +							   direction); +			return ERR_PTR(ret); +		} +	} -	if (!IS_ERR_OR_NULL(sg_table)) -		mangle_sg_table(sg_table); - +	mangle_sg_table(sg_table);  	return sg_table;  } @@ -1124,7 +1137,8 @@ static int __dma_buf_begin_cpu_access(struct dma_buf *dmabuf,  	long ret;  	/* Wait on any implicit rendering fences */ -	ret = dma_resv_wait_timeout(resv, write, true, MAX_SCHEDULE_TIMEOUT); +	ret = dma_resv_wait_timeout(resv, dma_resv_usage_rw(write), +				    true, MAX_SCHEDULE_TIMEOUT);  	if (ret < 0)  		return ret; diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c index 8c650b96357a..0cce6e4ec946 100644 --- a/drivers/dma-buf/dma-resv.c +++ b/drivers/dma-buf/dma-resv.c @@ -44,12 +44,12 @@  /**   * DOC: Reservation Object Overview   * - * The reservation object provides a mechanism to manage shared and - * exclusive fences associated with a buffer.  A reservation object - * can have attached one exclusive fence (normally associated with - * write operations) or N shared fences (read operations).  The RCU - * mechanism is used to protect read access to fences from locked - * write-side updates. + * The reservation object provides a mechanism to manage a container of + * dma_fence object associated with a resource. A reservation object + * can have any number of fences attaches to it. Each fence carries an usage + * parameter determining how the operation represented by the fence is using the + * resource. The RCU mechanism is used to protect read access to fences from + * locked write-side updates.   *   * See struct dma_resv for more details.   */ @@ -57,39 +57,59 @@  DEFINE_WD_CLASS(reservation_ww_class);  EXPORT_SYMBOL(reservation_ww_class); +/* Mask for the lower fence pointer bits */ +#define DMA_RESV_LIST_MASK	0x3 +  struct dma_resv_list {  	struct rcu_head rcu; -	u32 shared_count, shared_max; -	struct dma_fence __rcu *shared[]; +	u32 num_fences, max_fences; +	struct dma_fence __rcu *table[];  }; -/** - * dma_resv_list_alloc - allocate fence list - * @shared_max: number of fences we need space for - * +/* Extract the fence and usage flags from an RCU protected entry in the list. */ +static void dma_resv_list_entry(struct dma_resv_list *list, unsigned int index, +				struct dma_resv *resv, struct dma_fence **fence, +				enum dma_resv_usage *usage) +{ +	long tmp; + +	tmp = (long)rcu_dereference_check(list->table[index], +					  resv ? dma_resv_held(resv) : true); +	*fence = (struct dma_fence *)(tmp & ~DMA_RESV_LIST_MASK); +	if (usage) +		*usage = tmp & DMA_RESV_LIST_MASK; +} + +/* Set the fence and usage flags at the specific index in the list. */ +static void dma_resv_list_set(struct dma_resv_list *list, +			      unsigned int index, +			      struct dma_fence *fence, +			      enum dma_resv_usage usage) +{ +	long tmp = ((long)fence) | usage; + +	RCU_INIT_POINTER(list->table[index], (struct dma_fence *)tmp); +} + +/*   * Allocate a new dma_resv_list and make sure to correctly initialize - * shared_max. + * max_fences.   */ -static struct dma_resv_list *dma_resv_list_alloc(unsigned int shared_max) +static struct dma_resv_list *dma_resv_list_alloc(unsigned int max_fences)  {  	struct dma_resv_list *list; -	list = kmalloc(struct_size(list, shared, shared_max), GFP_KERNEL); +	list = kmalloc(struct_size(list, table, max_fences), GFP_KERNEL);  	if (!list)  		return NULL; -	list->shared_max = (ksize(list) - offsetof(typeof(*list), shared)) / -		sizeof(*list->shared); +	list->max_fences = (ksize(list) - offsetof(typeof(*list), table)) / +		sizeof(*list->table);  	return list;  } -/** - * dma_resv_list_free - free fence list - * @list: list to free - * - * Free a dma_resv_list and make sure to drop all references. - */ +/* Free a dma_resv_list and make sure to drop all references. */  static void dma_resv_list_free(struct dma_resv_list *list)  {  	unsigned int i; @@ -97,9 +117,12 @@ static void dma_resv_list_free(struct dma_resv_list *list)  	if (!list)  		return; -	for (i = 0; i < list->shared_count; ++i) -		dma_fence_put(rcu_dereference_protected(list->shared[i], true)); +	for (i = 0; i < list->num_fences; ++i) { +		struct dma_fence *fence; +		dma_resv_list_entry(list, i, NULL, &fence, NULL); +		dma_fence_put(fence); +	}  	kfree_rcu(list, rcu);  } @@ -110,10 +133,8 @@ static void dma_resv_list_free(struct dma_resv_list *list)  void dma_resv_init(struct dma_resv *obj)  {  	ww_mutex_init(&obj->lock, &reservation_ww_class); -	seqcount_ww_mutex_init(&obj->seq, &obj->lock); -	RCU_INIT_POINTER(obj->fence, NULL); -	RCU_INIT_POINTER(obj->fence_excl, NULL); +	RCU_INIT_POINTER(obj->fences, NULL);  }  EXPORT_SYMBOL(dma_resv_init); @@ -123,46 +144,32 @@ EXPORT_SYMBOL(dma_resv_init);   */  void dma_resv_fini(struct dma_resv *obj)  { -	struct dma_resv_list *fobj; -	struct dma_fence *excl; -  	/*  	 * This object should be dead and all references must have  	 * been released to it, so no need to be protected with rcu.  	 */ -	excl = rcu_dereference_protected(obj->fence_excl, 1); -	if (excl) -		dma_fence_put(excl); - -	fobj = rcu_dereference_protected(obj->fence, 1); -	dma_resv_list_free(fobj); +	dma_resv_list_free(rcu_dereference_protected(obj->fences, true));  	ww_mutex_destroy(&obj->lock);  }  EXPORT_SYMBOL(dma_resv_fini); -static inline struct dma_fence * -dma_resv_excl_fence(struct dma_resv *obj) +/* Dereference the fences while ensuring RCU rules */ +static inline struct dma_resv_list *dma_resv_fences_list(struct dma_resv *obj)  { -       return rcu_dereference_check(obj->fence_excl, dma_resv_held(obj)); -} - -static inline struct dma_resv_list *dma_resv_shared_list(struct dma_resv *obj) -{ -	return rcu_dereference_check(obj->fence, dma_resv_held(obj)); +	return rcu_dereference_check(obj->fences, dma_resv_held(obj));  }  /** - * dma_resv_reserve_fences - Reserve space to add shared fences to - * a dma_resv. + * dma_resv_reserve_fences - Reserve space to add fences to a dma_resv object.   * @obj: reservation object   * @num_fences: number of fences we want to add   * - * Should be called before dma_resv_add_shared_fence().  Must - * be called with @obj locked through dma_resv_lock(). + * Should be called before dma_resv_add_fence().  Must be called with @obj + * locked through dma_resv_lock().   *   * Note that the preallocated slots need to be re-reserved if @obj is unlocked - * at any time before calling dma_resv_add_shared_fence(). This is validated - * when CONFIG_DEBUG_MUTEXES is enabled. + * at any time before calling dma_resv_add_fence(). This is validated when + * CONFIG_DEBUG_MUTEXES is enabled.   *   * RETURNS   * Zero for success, or -errno @@ -174,11 +181,11 @@ int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)  	dma_resv_assert_held(obj); -	old = dma_resv_shared_list(obj); -	if (old && old->shared_max) { -		if ((old->shared_count + num_fences) <= old->shared_max) +	old = dma_resv_fences_list(obj); +	if (old && old->max_fences) { +		if ((old->num_fences + num_fences) <= old->max_fences)  			return 0; -		max = max(old->shared_count + num_fences, old->shared_max * 2); +		max = max(old->num_fences + num_fences, old->max_fences * 2);  	} else {  		max = max(4ul, roundup_pow_of_two(num_fences));  	} @@ -193,27 +200,27 @@ int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)  	 * references from the old struct are carried over to  	 * the new.  	 */ -	for (i = 0, j = 0, k = max; i < (old ? old->shared_count : 0); ++i) { +	for (i = 0, j = 0, k = max; i < (old ? old->num_fences : 0); ++i) { +		enum dma_resv_usage usage;  		struct dma_fence *fence; -		fence = rcu_dereference_protected(old->shared[i], -						  dma_resv_held(obj)); +		dma_resv_list_entry(old, i, obj, &fence, &usage);  		if (dma_fence_is_signaled(fence)) -			RCU_INIT_POINTER(new->shared[--k], fence); +			RCU_INIT_POINTER(new->table[--k], fence);  		else -			RCU_INIT_POINTER(new->shared[j++], fence); +			dma_resv_list_set(new, j++, fence, usage);  	} -	new->shared_count = j; +	new->num_fences = j;  	/*  	 * We are not changing the effective set of fences here so can  	 * merely update the pointer to the new array; both existing  	 * readers and new readers will see exactly the same set of -	 * active (unsignaled) shared fences. Individual fences and the +	 * active (unsignaled) fences. Individual fences and the  	 * old array are protected by RCU and so will not vanish under  	 * the gaze of the rcu_read_lock() readers.  	 */ -	rcu_assign_pointer(obj->fence, new); +	rcu_assign_pointer(obj->fences, new);  	if (!old)  		return 0; @@ -222,7 +229,7 @@ int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)  	for (i = k; i < max; ++i) {  		struct dma_fence *fence; -		fence = rcu_dereference_protected(new->shared[i], +		fence = rcu_dereference_protected(new->table[i],  						  dma_resv_held(obj));  		dma_fence_put(fence);  	} @@ -234,37 +241,39 @@ EXPORT_SYMBOL(dma_resv_reserve_fences);  #ifdef CONFIG_DEBUG_MUTEXES  /** - * dma_resv_reset_shared_max - reset shared fences for debugging + * dma_resv_reset_max_fences - reset fences for debugging   * @obj: the dma_resv object to reset   * - * Reset the number of pre-reserved shared slots to test that drivers do + * Reset the number of pre-reserved fence slots to test that drivers do   * correct slot allocation using dma_resv_reserve_fences(). See also - * &dma_resv_list.shared_max. + * &dma_resv_list.max_fences.   */ -void dma_resv_reset_shared_max(struct dma_resv *obj) +void dma_resv_reset_max_fences(struct dma_resv *obj)  { -	struct dma_resv_list *fences = dma_resv_shared_list(obj); +	struct dma_resv_list *fences = dma_resv_fences_list(obj);  	dma_resv_assert_held(obj); -	/* Test shared fence slot reservation */ +	/* Test fence slot reservation */  	if (fences) -		fences->shared_max = fences->shared_count; +		fences->max_fences = fences->num_fences;  } -EXPORT_SYMBOL(dma_resv_reset_shared_max); +EXPORT_SYMBOL(dma_resv_reset_max_fences);  #endif  /** - * dma_resv_add_shared_fence - Add a fence to a shared slot + * dma_resv_add_fence - Add a fence to the dma_resv obj   * @obj: the reservation object - * @fence: the shared fence to add + * @fence: the fence to add + * @usage: how the fence is used, see enum dma_resv_usage   * - * Add a fence to a shared slot, @obj must be locked with dma_resv_lock(), and + * Add a fence to a slot, @obj must be locked with dma_resv_lock(), and   * dma_resv_reserve_fences() has been called.   *   * See also &dma_resv.fence for a discussion of the semantics.   */ -void dma_resv_add_shared_fence(struct dma_resv *obj, struct dma_fence *fence) +void dma_resv_add_fence(struct dma_resv *obj, struct dma_fence *fence, +			enum dma_resv_usage usage)  {  	struct dma_resv_list *fobj;  	struct dma_fence *old; @@ -279,39 +288,36 @@ void dma_resv_add_shared_fence(struct dma_resv *obj, struct dma_fence *fence)  	 */  	WARN_ON(dma_fence_is_container(fence)); -	fobj = dma_resv_shared_list(obj); -	count = fobj->shared_count; - -	write_seqcount_begin(&obj->seq); +	fobj = dma_resv_fences_list(obj); +	count = fobj->num_fences;  	for (i = 0; i < count; ++i) { - -		old = rcu_dereference_protected(fobj->shared[i], -						dma_resv_held(obj)); -		if (old->context == fence->context || -		    dma_fence_is_signaled(old)) -			goto replace; +		enum dma_resv_usage old_usage; + +		dma_resv_list_entry(fobj, i, obj, &old, &old_usage); +		if ((old->context == fence->context && old_usage >= usage) || +		    dma_fence_is_signaled(old)) { +			dma_resv_list_set(fobj, i, fence, usage); +			dma_fence_put(old); +			return; +		}  	} -	BUG_ON(fobj->shared_count >= fobj->shared_max); -	old = NULL; +	BUG_ON(fobj->num_fences >= fobj->max_fences);  	count++; -replace: -	RCU_INIT_POINTER(fobj->shared[i], fence); -	/* pointer update must be visible before we extend the shared_count */ -	smp_store_mb(fobj->shared_count, count); - -	write_seqcount_end(&obj->seq); -	dma_fence_put(old); +	dma_resv_list_set(fobj, i, fence, usage); +	/* pointer update must be visible before we extend the num_fences */ +	smp_store_mb(fobj->num_fences, count);  } -EXPORT_SYMBOL(dma_resv_add_shared_fence); +EXPORT_SYMBOL(dma_resv_add_fence);  /**   * dma_resv_replace_fences - replace fences in the dma_resv obj   * @obj: the reservation object   * @context: the context of the fences to replace   * @replacement: the new fence to use instead + * @usage: how the new fence is used, see enum dma_resv_usage   *   * Replace fences with a specified context with a new fence. Only valid if the   * operation represented by the original fence has no longer access to the @@ -321,107 +327,66 @@ EXPORT_SYMBOL(dma_resv_add_shared_fence);   * update fence which makes the resource inaccessible.   */  void dma_resv_replace_fences(struct dma_resv *obj, uint64_t context, -			     struct dma_fence *replacement) +			     struct dma_fence *replacement, +			     enum dma_resv_usage usage)  {  	struct dma_resv_list *list; -	struct dma_fence *old;  	unsigned int i;  	dma_resv_assert_held(obj); -	write_seqcount_begin(&obj->seq); +	list = dma_resv_fences_list(obj); +	for (i = 0; list && i < list->num_fences; ++i) { +		struct dma_fence *old; -	old = dma_resv_excl_fence(obj); -	if (old->context == context) { -		RCU_INIT_POINTER(obj->fence_excl, dma_fence_get(replacement)); -		dma_fence_put(old); -	} - -	list = dma_resv_shared_list(obj); -	for (i = 0; list && i < list->shared_count; ++i) { -		old = rcu_dereference_protected(list->shared[i], -						dma_resv_held(obj)); +		dma_resv_list_entry(list, i, obj, &old, NULL);  		if (old->context != context)  			continue; -		rcu_assign_pointer(list->shared[i], dma_fence_get(replacement)); +		dma_resv_list_set(list, i, replacement, usage);  		dma_fence_put(old);  	} - -	write_seqcount_end(&obj->seq);  }  EXPORT_SYMBOL(dma_resv_replace_fences); -/** - * dma_resv_add_excl_fence - Add an exclusive fence. - * @obj: the reservation object - * @fence: the exclusive fence to add - * - * Add a fence to the exclusive slot. @obj must be locked with dma_resv_lock(). - * See also &dma_resv.fence_excl for a discussion of the semantics. - */ -void dma_resv_add_excl_fence(struct dma_resv *obj, struct dma_fence *fence) -{ -	struct dma_fence *old_fence = dma_resv_excl_fence(obj); - -	dma_resv_assert_held(obj); - -	dma_fence_get(fence); - -	write_seqcount_begin(&obj->seq); -	/* write_seqcount_begin provides the necessary memory barrier */ -	RCU_INIT_POINTER(obj->fence_excl, fence); -	write_seqcount_end(&obj->seq); - -	dma_fence_put(old_fence); -} -EXPORT_SYMBOL(dma_resv_add_excl_fence); - -/* Restart the iterator by initializing all the necessary fields, but not the - * relation to the dma_resv object. */ +/* Restart the unlocked iteration by initializing the cursor object. */  static void dma_resv_iter_restart_unlocked(struct dma_resv_iter *cursor)  { -	cursor->seq = read_seqcount_begin(&cursor->obj->seq); -	cursor->index = -1; -	cursor->shared_count = 0; -	if (cursor->all_fences) { -		cursor->fences = dma_resv_shared_list(cursor->obj); -		if (cursor->fences) -			cursor->shared_count = cursor->fences->shared_count; -	} else { -		cursor->fences = NULL; -	} +	cursor->index = 0; +	cursor->num_fences = 0; +	cursor->fences = dma_resv_fences_list(cursor->obj); +	if (cursor->fences) +		cursor->num_fences = cursor->fences->num_fences;  	cursor->is_restarted = true;  }  /* Walk to the next not signaled fence and grab a reference to it */  static void dma_resv_iter_walk_unlocked(struct dma_resv_iter *cursor)  { -	struct dma_resv *obj = cursor->obj; +	if (!cursor->fences) +		return;  	do {  		/* Drop the reference from the previous round */  		dma_fence_put(cursor->fence); -		if (cursor->index == -1) { -			cursor->fence = dma_resv_excl_fence(obj); -			cursor->index++; -			if (!cursor->fence) -				continue; - -		} else if (!cursor->fences || -			   cursor->index >= cursor->shared_count) { +		if (cursor->index >= cursor->num_fences) {  			cursor->fence = NULL;  			break; -		} else { -			struct dma_resv_list *fences = cursor->fences; -			unsigned int idx = cursor->index++; - -			cursor->fence = rcu_dereference(fences->shared[idx]);  		} + +		dma_resv_list_entry(cursor->fences, cursor->index++, +				    cursor->obj, &cursor->fence, +				    &cursor->fence_usage);  		cursor->fence = dma_fence_get_rcu(cursor->fence); -		if (!cursor->fence || !dma_fence_is_signaled(cursor->fence)) +		if (!cursor->fence) { +			dma_resv_iter_restart_unlocked(cursor); +			continue; +		} + +		if (!dma_fence_is_signaled(cursor->fence) && +		    cursor->usage >= cursor->fence_usage)  			break;  	} while (true);  } @@ -444,7 +409,7 @@ struct dma_fence *dma_resv_iter_first_unlocked(struct dma_resv_iter *cursor)  	do {  		dma_resv_iter_restart_unlocked(cursor);  		dma_resv_iter_walk_unlocked(cursor); -	} while (read_seqcount_retry(&cursor->obj->seq, cursor->seq)); +	} while (dma_resv_fences_list(cursor->obj) != cursor->fences);  	rcu_read_unlock();  	return cursor->fence; @@ -467,13 +432,13 @@ struct dma_fence *dma_resv_iter_next_unlocked(struct dma_resv_iter *cursor)  	rcu_read_lock();  	cursor->is_restarted = false; -	restart = read_seqcount_retry(&cursor->obj->seq, cursor->seq); +	restart = dma_resv_fences_list(cursor->obj) != cursor->fences;  	do {  		if (restart)  			dma_resv_iter_restart_unlocked(cursor);  		dma_resv_iter_walk_unlocked(cursor);  		restart = true; -	} while (read_seqcount_retry(&cursor->obj->seq, cursor->seq)); +	} while (dma_resv_fences_list(cursor->obj) != cursor->fences);  	rcu_read_unlock();  	return cursor->fence; @@ -496,15 +461,9 @@ struct dma_fence *dma_resv_iter_first(struct dma_resv_iter *cursor)  	dma_resv_assert_held(cursor->obj);  	cursor->index = 0; -	if (cursor->all_fences) -		cursor->fences = dma_resv_shared_list(cursor->obj); -	else -		cursor->fences = NULL; - -	fence = dma_resv_excl_fence(cursor->obj); -	if (!fence) -		fence = dma_resv_iter_next(cursor); +	cursor->fences = dma_resv_fences_list(cursor->obj); +	fence = dma_resv_iter_next(cursor);  	cursor->is_restarted = true;  	return fence;  } @@ -519,17 +478,22 @@ EXPORT_SYMBOL_GPL(dma_resv_iter_first);   */  struct dma_fence *dma_resv_iter_next(struct dma_resv_iter *cursor)  { -	unsigned int idx; +	struct dma_fence *fence;  	dma_resv_assert_held(cursor->obj);  	cursor->is_restarted = false; -	if (!cursor->fences || cursor->index >= cursor->fences->shared_count) -		return NULL; -	idx = cursor->index++; -	return rcu_dereference_protected(cursor->fences->shared[idx], -					 dma_resv_held(cursor->obj)); +	do { +		if (!cursor->fences || +		    cursor->index >= cursor->fences->num_fences) +			return NULL; + +		dma_resv_list_entry(cursor->fences, cursor->index++, +				    cursor->obj, &fence, &cursor->fence_usage); +	} while (cursor->fence_usage > cursor->usage); + +	return fence;  }  EXPORT_SYMBOL_GPL(dma_resv_iter_next); @@ -544,60 +508,43 @@ int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src)  {  	struct dma_resv_iter cursor;  	struct dma_resv_list *list; -	struct dma_fence *f, *excl; +	struct dma_fence *f;  	dma_resv_assert_held(dst);  	list = NULL; -	excl = NULL; -	dma_resv_iter_begin(&cursor, src, true); +	dma_resv_iter_begin(&cursor, src, DMA_RESV_USAGE_BOOKKEEP);  	dma_resv_for_each_fence_unlocked(&cursor, f) {  		if (dma_resv_iter_is_restarted(&cursor)) {  			dma_resv_list_free(list); -			dma_fence_put(excl); - -			if (cursor.shared_count) { -				list = dma_resv_list_alloc(cursor.shared_count); -				if (!list) { -					dma_resv_iter_end(&cursor); -					return -ENOMEM; -				} -				list->shared_count = 0; - -			} else { -				list = NULL; +			list = dma_resv_list_alloc(cursor.num_fences); +			if (!list) { +				dma_resv_iter_end(&cursor); +				return -ENOMEM;  			} -			excl = NULL; +			list->num_fences = 0;  		}  		dma_fence_get(f); -		if (dma_resv_iter_is_exclusive(&cursor)) -			excl = f; -		else -			RCU_INIT_POINTER(list->shared[list->shared_count++], f); +		dma_resv_list_set(list, list->num_fences++, f, +				  dma_resv_iter_usage(&cursor));  	}  	dma_resv_iter_end(&cursor); -	write_seqcount_begin(&dst->seq); -	excl = rcu_replace_pointer(dst->fence_excl, excl, dma_resv_held(dst)); -	list = rcu_replace_pointer(dst->fence, list, dma_resv_held(dst)); -	write_seqcount_end(&dst->seq); - +	list = rcu_replace_pointer(dst->fences, list, dma_resv_held(dst));  	dma_resv_list_free(list); -	dma_fence_put(excl); -  	return 0;  }  EXPORT_SYMBOL(dma_resv_copy_fences);  /** - * dma_resv_get_fences - Get an object's shared and exclusive + * dma_resv_get_fences - Get an object's fences   * fences without update side lock held   * @obj: the reservation object - * @write: true if we should return all fences + * @usage: controls which fences to include, see enum dma_resv_usage.   * @num_fences: the number of fences returned   * @fences: the array of fence ptrs returned (array is krealloc'd to the   * required size, and must be freed by caller) @@ -605,7 +552,7 @@ EXPORT_SYMBOL(dma_resv_copy_fences);   * Retrieve all fences from the reservation object.   * Returns either zero or -ENOMEM.   */ -int dma_resv_get_fences(struct dma_resv *obj, bool write, +int dma_resv_get_fences(struct dma_resv *obj, enum dma_resv_usage usage,  			unsigned int *num_fences, struct dma_fence ***fences)  {  	struct dma_resv_iter cursor; @@ -614,7 +561,7 @@ int dma_resv_get_fences(struct dma_resv *obj, bool write,  	*num_fences = 0;  	*fences = NULL; -	dma_resv_iter_begin(&cursor, obj, write); +	dma_resv_iter_begin(&cursor, obj, usage);  	dma_resv_for_each_fence_unlocked(&cursor, fence) {  		if (dma_resv_iter_is_restarted(&cursor)) { @@ -623,7 +570,7 @@ int dma_resv_get_fences(struct dma_resv *obj, bool write,  			while (*num_fences)  				dma_fence_put((*fences)[--(*num_fences)]); -			count = cursor.shared_count + 1; +			count = cursor.num_fences + 1;  			/* Eventually re-allocate the array */  			*fences = krealloc_array(*fences, count, @@ -646,7 +593,7 @@ EXPORT_SYMBOL_GPL(dma_resv_get_fences);  /**   * dma_resv_get_singleton - Get a single fence for all the fences   * @obj: the reservation object - * @write: true if we should return all fences + * @usage: controls which fences to include, see enum dma_resv_usage.   * @fence: the resulting fence   *   * Get a single fence representing all the fences inside the resv object. @@ -658,7 +605,7 @@ EXPORT_SYMBOL_GPL(dma_resv_get_fences);   *   * Returns 0 on success and negative error values on failure.   */ -int dma_resv_get_singleton(struct dma_resv *obj, bool write, +int dma_resv_get_singleton(struct dma_resv *obj, enum dma_resv_usage usage,  			   struct dma_fence **fence)  {  	struct dma_fence_array *array; @@ -666,7 +613,7 @@ int dma_resv_get_singleton(struct dma_resv *obj, bool write,  	unsigned count;  	int r; -	r = dma_resv_get_fences(obj, write, &count, &fences); +	r = dma_resv_get_fences(obj, usage, &count, &fences);          if (r)  		return r; @@ -697,10 +644,9 @@ int dma_resv_get_singleton(struct dma_resv *obj, bool write,  EXPORT_SYMBOL_GPL(dma_resv_get_singleton);  /** - * dma_resv_wait_timeout - Wait on reservation's objects - * shared and/or exclusive fences. + * dma_resv_wait_timeout - Wait on reservation's objects fences   * @obj: the reservation object - * @wait_all: if true, wait on all fences, else wait on just exclusive fence + * @usage: controls which fences to include, see enum dma_resv_usage.   * @intr: if true, do interruptible wait   * @timeout: timeout value in jiffies or zero to return immediately   * @@ -710,14 +656,14 @@ EXPORT_SYMBOL_GPL(dma_resv_get_singleton);   * Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or   * greater than zer on success.   */ -long dma_resv_wait_timeout(struct dma_resv *obj, bool wait_all, bool intr, -			   unsigned long timeout) +long dma_resv_wait_timeout(struct dma_resv *obj, enum dma_resv_usage usage, +			   bool intr, unsigned long timeout)  {  	long ret = timeout ? timeout : 1;  	struct dma_resv_iter cursor;  	struct dma_fence *fence; -	dma_resv_iter_begin(&cursor, obj, wait_all); +	dma_resv_iter_begin(&cursor, obj, usage);  	dma_resv_for_each_fence_unlocked(&cursor, fence) {  		ret = dma_fence_wait_timeout(fence, intr, ret); @@ -737,8 +683,7 @@ EXPORT_SYMBOL_GPL(dma_resv_wait_timeout);   * dma_resv_test_signaled - Test if a reservation object's fences have been   * signaled.   * @obj: the reservation object - * @test_all: if true, test all fences, otherwise only test the exclusive - * fence + * @usage: controls which fences to include, see enum dma_resv_usage.   *   * Callers are not required to hold specific locks, but maybe hold   * dma_resv_lock() already. @@ -747,12 +692,12 @@ EXPORT_SYMBOL_GPL(dma_resv_wait_timeout);   *   * True if all fences signaled, else false.   */ -bool dma_resv_test_signaled(struct dma_resv *obj, bool test_all) +bool dma_resv_test_signaled(struct dma_resv *obj, enum dma_resv_usage usage)  {  	struct dma_resv_iter cursor;  	struct dma_fence *fence; -	dma_resv_iter_begin(&cursor, obj, test_all); +	dma_resv_iter_begin(&cursor, obj, usage);  	dma_resv_for_each_fence_unlocked(&cursor, fence) {  		dma_resv_iter_end(&cursor);  		return false; @@ -772,13 +717,13 @@ EXPORT_SYMBOL_GPL(dma_resv_test_signaled);   */  void dma_resv_describe(struct dma_resv *obj, struct seq_file *seq)  { +	static const char *usage[] = { "kernel", "write", "read", "bookkeep" };  	struct dma_resv_iter cursor;  	struct dma_fence *fence; -	dma_resv_for_each_fence(&cursor, obj, true, fence) { +	dma_resv_for_each_fence(&cursor, obj, DMA_RESV_USAGE_READ, fence) {  		seq_printf(seq, "\t%s fence:", -			   dma_resv_iter_is_exclusive(&cursor) ? -				"Exclusive" : "Shared"); +			   usage[dma_resv_iter_usage(&cursor)]);  		dma_fence_describe(fence, seq);  	}  } diff --git a/drivers/dma-buf/st-dma-resv.c b/drivers/dma-buf/st-dma-resv.c index d2e61f6ae989..813779e3c9be 100644 --- a/drivers/dma-buf/st-dma-resv.c +++ b/drivers/dma-buf/st-dma-resv.c @@ -58,8 +58,9 @@ static int sanitycheck(void *arg)  	return r;  } -static int test_signaling(void *arg, bool shared) +static int test_signaling(void *arg)  { +	enum dma_resv_usage usage = (unsigned long)arg;  	struct dma_resv resv;  	struct dma_fence *f;  	int r; @@ -81,18 +82,14 @@ static int test_signaling(void *arg, bool shared)  		goto err_unlock;  	} -	if (shared) -		dma_resv_add_shared_fence(&resv, f); -	else -		dma_resv_add_excl_fence(&resv, f); - -	if (dma_resv_test_signaled(&resv, shared)) { +	dma_resv_add_fence(&resv, f, usage); +	if (dma_resv_test_signaled(&resv, usage)) {  		pr_err("Resv unexpectedly signaled\n");  		r = -EINVAL;  		goto err_unlock;  	}  	dma_fence_signal(f); -	if (!dma_resv_test_signaled(&resv, shared)) { +	if (!dma_resv_test_signaled(&resv, usage)) {  		pr_err("Resv not reporting signaled\n");  		r = -EINVAL;  		goto err_unlock; @@ -105,18 +102,9 @@ err_free:  	return r;  } -static int test_excl_signaling(void *arg) -{ -	return test_signaling(arg, false); -} - -static int test_shared_signaling(void *arg) -{ -	return test_signaling(arg, true); -} - -static int test_for_each(void *arg, bool shared) +static int test_for_each(void *arg)  { +	enum dma_resv_usage usage = (unsigned long)arg;  	struct dma_resv_iter cursor;  	struct dma_fence *f, *fence;  	struct dma_resv resv; @@ -139,13 +127,10 @@ static int test_for_each(void *arg, bool shared)  		goto err_unlock;  	} -	if (shared) -		dma_resv_add_shared_fence(&resv, f); -	else -		dma_resv_add_excl_fence(&resv, f); +	dma_resv_add_fence(&resv, f, usage);  	r = -ENOENT; -	dma_resv_for_each_fence(&cursor, &resv, shared, fence) { +	dma_resv_for_each_fence(&cursor, &resv, usage, fence) {  		if (!r) {  			pr_err("More than one fence found\n");  			r = -EINVAL; @@ -156,7 +141,7 @@ static int test_for_each(void *arg, bool shared)  			r = -EINVAL;  			goto err_unlock;  		} -		if (dma_resv_iter_is_exclusive(&cursor) != !shared) { +		if (dma_resv_iter_usage(&cursor) != usage) {  			pr_err("Unexpected fence usage\n");  			r = -EINVAL;  			goto err_unlock; @@ -176,18 +161,9 @@ err_free:  	return r;  } -static int test_excl_for_each(void *arg) -{ -	return test_for_each(arg, false); -} - -static int test_shared_for_each(void *arg) -{ -	return test_for_each(arg, true); -} - -static int test_for_each_unlocked(void *arg, bool shared) +static int test_for_each_unlocked(void *arg)  { +	enum dma_resv_usage usage = (unsigned long)arg;  	struct dma_resv_iter cursor;  	struct dma_fence *f, *fence;  	struct dma_resv resv; @@ -211,14 +187,11 @@ static int test_for_each_unlocked(void *arg, bool shared)  		goto err_free;  	} -	if (shared) -		dma_resv_add_shared_fence(&resv, f); -	else -		dma_resv_add_excl_fence(&resv, f); +	dma_resv_add_fence(&resv, f, usage);  	dma_resv_unlock(&resv);  	r = -ENOENT; -	dma_resv_iter_begin(&cursor, &resv, shared); +	dma_resv_iter_begin(&cursor, &resv, usage);  	dma_resv_for_each_fence_unlocked(&cursor, fence) {  		if (!r) {  			pr_err("More than one fence found\n"); @@ -234,7 +207,7 @@ static int test_for_each_unlocked(void *arg, bool shared)  			r = -EINVAL;  			goto err_iter_end;  		} -		if (dma_resv_iter_is_exclusive(&cursor) != !shared) { +		if (dma_resv_iter_usage(&cursor) != usage) {  			pr_err("Unexpected fence usage\n");  			r = -EINVAL;  			goto err_iter_end; @@ -244,7 +217,7 @@ static int test_for_each_unlocked(void *arg, bool shared)  		if (r == -ENOENT) {  			r = -EINVAL;  			/* That should trigger an restart */ -			cursor.seq--; +			cursor.fences = (void*)~0;  		} else if (r == -EINVAL) {  			r = 0;  		} @@ -260,18 +233,9 @@ err_free:  	return r;  } -static int test_excl_for_each_unlocked(void *arg) -{ -	return test_for_each_unlocked(arg, false); -} - -static int test_shared_for_each_unlocked(void *arg) -{ -	return test_for_each_unlocked(arg, true); -} - -static int test_get_fences(void *arg, bool shared) +static int test_get_fences(void *arg)  { +	enum dma_resv_usage usage = (unsigned long)arg;  	struct dma_fence *f, **fences = NULL;  	struct dma_resv resv;  	int r, i; @@ -294,13 +258,10 @@ static int test_get_fences(void *arg, bool shared)  		goto err_resv;  	} -	if (shared) -		dma_resv_add_shared_fence(&resv, f); -	else -		dma_resv_add_excl_fence(&resv, f); +	dma_resv_add_fence(&resv, f, usage);  	dma_resv_unlock(&resv); -	r = dma_resv_get_fences(&resv, shared, &i, &fences); +	r = dma_resv_get_fences(&resv, usage, &i, &fences);  	if (r) {  		pr_err("get_fences failed\n");  		goto err_free; @@ -322,30 +283,24 @@ err_resv:  	return r;  } -static int test_excl_get_fences(void *arg) -{ -	return test_get_fences(arg, false); -} - -static int test_shared_get_fences(void *arg) -{ -	return test_get_fences(arg, true); -} -  int dma_resv(void)  {  	static const struct subtest tests[] = {  		SUBTEST(sanitycheck), -		SUBTEST(test_excl_signaling), -		SUBTEST(test_shared_signaling), -		SUBTEST(test_excl_for_each), -		SUBTEST(test_shared_for_each), -		SUBTEST(test_excl_for_each_unlocked), -		SUBTEST(test_shared_for_each_unlocked), -		SUBTEST(test_excl_get_fences), -		SUBTEST(test_shared_get_fences), +		SUBTEST(test_signaling), +		SUBTEST(test_for_each), +		SUBTEST(test_for_each_unlocked), +		SUBTEST(test_get_fences),  	}; +	enum dma_resv_usage usage; +	int r;  	spin_lock_init(&fence_lock); -	return subtests(tests, NULL); +	for (usage = DMA_RESV_USAGE_KERNEL; usage <= DMA_RESV_USAGE_BOOKKEEP; +	     ++usage) { +		r = subtests(tests, (void *)(unsigned long)usage); +		if (r) +			return r; +	} +	return 0;  } diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index f1422bee3dcc..5133c3f028ab 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -280,6 +280,7 @@ config DRM_AMDGPU  	select HWMON  	select BACKLIGHT_CLASS_DEVICE  	select INTERVAL_TREE +	select DRM_BUDDY  	help  	  Choose this option if you have a recent AMD Radeon graphics card. diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 98b1736bb221..3dc5ab2764ff 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -263,7 +263,7 @@ static int amdgpu_amdkfd_remove_eviction_fence(struct amdgpu_bo *bo,  	 */  	replacement = dma_fence_get_stub();  	dma_resv_replace_fences(bo->tbo.base.resv, ef->base.context, -				replacement); +				replacement, DMA_RESV_USAGE_READ);  	dma_fence_put(replacement);  	return 0;  } @@ -2447,6 +2447,8 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)  		struct amdgpu_bo *bo = mem->bo;  		uint32_t domain = mem->domain;  		struct kfd_mem_attachment *attachment; +		struct dma_resv_iter cursor; +		struct dma_fence *fence;  		total_size += amdgpu_bo_size(bo); @@ -2461,10 +2463,13 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)  				goto validate_map_fail;  			}  		} -		ret = amdgpu_sync_fence(&sync_obj, bo->tbo.moving); -		if (ret) { -			pr_debug("Memory eviction: Sync BO fence failed. Try again\n"); -			goto validate_map_fail; +		dma_resv_for_each_fence(&cursor, bo->tbo.base.resv, +					DMA_RESV_USAGE_KERNEL, fence) { +			ret = amdgpu_sync_fence(&sync_obj, fence); +			if (ret) { +				pr_debug("Memory eviction: Sync BO fence failed. Try again\n"); +				goto validate_map_fail; +			}  		}  		list_for_each_entry(attachment, &mem->attachments, list) {  			if (!attachment->is_mapped) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h index 044b41f0bfd9..529d52a204cf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h @@ -34,7 +34,6 @@ struct amdgpu_fpriv;  struct amdgpu_bo_list_entry {  	struct ttm_validate_buffer	tv;  	struct amdgpu_bo_va		*bo_va; -	struct dma_fence_chain		*chain;  	uint32_t			priority;  	struct page			**user_pages;  	bool				user_invalidated; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index e85e347eb670..8de283997769 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -55,8 +55,8 @@ static int amdgpu_cs_user_fence_chunk(struct amdgpu_cs_parser *p,  	bo = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj));  	p->uf_entry.priority = 0;  	p->uf_entry.tv.bo = &bo->tbo; -	/* One for TTM and one for the CS job */ -	p->uf_entry.tv.num_shared = 2; +	/* One for TTM and two for the CS job */ +	p->uf_entry.tv.num_shared = 3;  	drm_gem_object_put(gobj); @@ -574,14 +574,6 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,  		struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo);  		e->bo_va = amdgpu_vm_bo_find(vm, bo); - -		if (bo->tbo.base.dma_buf && !amdgpu_bo_explicit_sync(bo)) { -			e->chain = dma_fence_chain_alloc(); -			if (!e->chain) { -				r = -ENOMEM; -				goto error_validate; -			} -		}  	}  	/* Move fence waiting after getting reservation lock of @@ -642,13 +634,8 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,  	}  error_validate: -	if (r) { -		amdgpu_bo_list_for_each_entry(e, p->bo_list) { -			dma_fence_chain_free(e->chain); -			e->chain = NULL; -		} +	if (r)  		ttm_eu_backoff_reservation(&p->ticket, &p->validated); -	}  out:  	return r;  } @@ -688,17 +675,9 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error,  {  	unsigned i; -	if (error && backoff) { -		struct amdgpu_bo_list_entry *e; - -		amdgpu_bo_list_for_each_entry(e, parser->bo_list) { -			dma_fence_chain_free(e->chain); -			e->chain = NULL; -		} - +	if (error && backoff)  		ttm_eu_backoff_reservation(&parser->ticket,  					   &parser->validated); -	}  	for (i = 0; i < parser->num_post_deps; i++) {  		drm_syncobj_put(parser->post_deps[i].syncobj); @@ -1272,29 +1251,9 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,  	amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm); -	amdgpu_bo_list_for_each_entry(e, p->bo_list) { -		struct dma_resv *resv = e->tv.bo->base.resv; -		struct dma_fence_chain *chain = e->chain; -		struct dma_resv_iter cursor; -		struct dma_fence *fence; - -		if (!chain) -			continue; - -		/* -		 * Temporary workaround dma_resv shortcommings by wrapping up -		 * the submission in a dma_fence_chain and add it as exclusive -		 * fence. -		 * -		 * TODO: Remove together with dma_resv rework. -		 */ -		dma_resv_for_each_fence(&cursor, resv, false, fence) { -			break; -		} -		dma_fence_chain_init(chain, fence, dma_fence_get(p->fence), 1); -		rcu_assign_pointer(resv->fence_excl, &chain->base); -		e->chain = NULL; -	} +	/* Make sure all BOs are remembered as writers */ +	amdgpu_bo_list_for_each_entry(e, p->bo_list) +		e->tv.num_shared = 0;  	ttm_eu_fence_buffer_objects(&p->ticket, &p->validated, p->fence);  	mutex_unlock(&p->adev->notifier_lock); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index fae5c1debfad..7a6908d71820 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -200,8 +200,7 @@ int amdgpu_display_crtc_page_flip_target(struct drm_crtc *crtc,  		goto unpin;  	} -	/* TODO: Unify this with other drivers */ -	r = dma_resv_get_fences(new_abo->tbo.base.resv, true, +	r = dma_resv_get_fences(new_abo->tbo.base.resv, DMA_RESV_USAGE_WRITE,  				&work->shared_count,  				&work->shared);  	if (unlikely(r != 0)) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c index 579adfafe4d0..782cbca37538 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c @@ -102,21 +102,9 @@ static int amdgpu_dma_buf_pin(struct dma_buf_attachment *attach)  {  	struct drm_gem_object *obj = attach->dmabuf->priv;  	struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); -	int r;  	/* pin buffer into GTT */ -	r = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT); -	if (r) -		return r; - -	if (bo->tbo.moving) { -		r = dma_fence_wait(bo->tbo.moving, true); -		if (r) { -			amdgpu_bo_unpin(bo); -			return r; -		} -	} -	return 0; +	return amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT);  }  /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 57b74d35052f..84a53758e18e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -526,7 +526,8 @@ int amdgpu_gem_wait_idle_ioctl(struct drm_device *dev, void *data,  		return -ENOENT;  	}  	robj = gem_to_amdgpu_bo(gobj); -	ret = dma_resv_wait_timeout(robj->tbo.base.resv, true, true, timeout); +	ret = dma_resv_wait_timeout(robj->tbo.base.resv, DMA_RESV_USAGE_READ, +				    true, timeout);  	/* ret == 0 means not signaled,  	 * ret > 0 means signaled diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c index 81207737c716..4ba4b54092f1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c @@ -111,7 +111,7 @@ void amdgpu_pasid_free_delayed(struct dma_resv *resv,  	struct dma_fence *fence;  	int r; -	r = dma_resv_get_singleton(resv, true, &fence); +	r = dma_resv_get_singleton(resv, DMA_RESV_USAGE_BOOKKEEP, &fence);  	if (r)  		goto fallback; @@ -139,7 +139,8 @@ fallback:  	/* Not enough memory for the delayed delete, as last resort  	 * block for all the fences to complete.  	 */ -	dma_resv_wait_timeout(resv, true, false, MAX_SCHEDULE_TIMEOUT); +	dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP, +			      false, MAX_SCHEDULE_TIMEOUT);  	amdgpu_pasid_free(pasid);  } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c index 4b153daf283d..b86c0b8252a5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c @@ -75,8 +75,8 @@ static bool amdgpu_mn_invalidate_gfx(struct mmu_interval_notifier *mni,  	mmu_interval_set_seq(mni, cur_seq); -	r = dma_resv_wait_timeout(bo->tbo.base.resv, true, false, -				  MAX_SCHEDULE_TIMEOUT); +	r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, +				  false, MAX_SCHEDULE_TIMEOUT);  	mutex_unlock(&adev->notifier_lock);  	if (r <= 0)  		DRM_ERROR("(%ld) failed to wait for user bo\n", r); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index dd6693fff280..e92ecabfa7bd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -612,9 +612,8 @@ int amdgpu_bo_create(struct amdgpu_device *adev,  		if (unlikely(r))  			goto fail_unreserve; -		amdgpu_bo_fence(bo, fence, false); -		dma_fence_put(bo->tbo.moving); -		bo->tbo.moving = dma_fence_get(fence); +		dma_resv_add_fence(bo->tbo.base.resv, fence, +				   DMA_RESV_USAGE_KERNEL);  		dma_fence_put(fence);  	}  	if (!bp->resv) @@ -761,6 +760,11 @@ int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr)  	if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS)  		return -EPERM; +	r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_KERNEL, +				  false, MAX_SCHEDULE_TIMEOUT); +	if (r < 0) +		return r; +  	kptr = amdgpu_bo_kptr(bo);  	if (kptr) {  		if (ptr) @@ -768,11 +772,6 @@ int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr)  		return 0;  	} -	r = dma_resv_wait_timeout(bo->tbo.base.resv, false, false, -				  MAX_SCHEDULE_TIMEOUT); -	if (r < 0) -		return r; -  	r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.resource->num_pages, &bo->kmap);  	if (r)  		return r; @@ -1399,10 +1398,8 @@ void amdgpu_bo_fence(struct amdgpu_bo *bo, struct dma_fence *fence,  		return;  	} -	if (shared) -		dma_resv_add_shared_fence(resv, fence); -	else -		dma_resv_add_excl_fence(resv, fence); +	dma_resv_add_fence(resv, fence, shared ? DMA_RESV_USAGE_READ : +			   DMA_RESV_USAGE_WRITE);  }  /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h index acfa207cf970..6546552e596c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h @@ -30,12 +30,15 @@  #include <drm/ttm/ttm_resource.h>  #include <drm/ttm/ttm_range_manager.h> +#include "amdgpu_vram_mgr.h" +  /* state back for walking over vram_mgr and gtt_mgr allocations */  struct amdgpu_res_cursor {  	uint64_t		start;  	uint64_t		size;  	uint64_t		remaining; -	struct drm_mm_node	*node; +	void			*node; +	uint32_t		mem_type;  };  /** @@ -52,27 +55,63 @@ static inline void amdgpu_res_first(struct ttm_resource *res,  				    uint64_t start, uint64_t size,  				    struct amdgpu_res_cursor *cur)  { +	struct drm_buddy_block *block; +	struct list_head *head, *next;  	struct drm_mm_node *node; -	if (!res || res->mem_type == TTM_PL_SYSTEM) { -		cur->start = start; -		cur->size = size; -		cur->remaining = size; -		cur->node = NULL; -		WARN_ON(res && start + size > res->num_pages << PAGE_SHIFT); -		return; -	} +	if (!res) +		goto fallback;  	BUG_ON(start + size > res->num_pages << PAGE_SHIFT); -	node = to_ttm_range_mgr_node(res)->mm_nodes; -	while (start >= node->size << PAGE_SHIFT) -		start -= node++->size << PAGE_SHIFT; +	cur->mem_type = res->mem_type; + +	switch (cur->mem_type) { +	case TTM_PL_VRAM: +		head = &to_amdgpu_vram_mgr_resource(res)->blocks; + +		block = list_first_entry_or_null(head, +						 struct drm_buddy_block, +						 link); +		if (!block) +			goto fallback; + +		while (start >= amdgpu_vram_mgr_block_size(block)) { +			start -= amdgpu_vram_mgr_block_size(block); + +			next = block->link.next; +			if (next != head) +				block = list_entry(next, struct drm_buddy_block, link); +		} + +		cur->start = amdgpu_vram_mgr_block_start(block) + start; +		cur->size = min(amdgpu_vram_mgr_block_size(block) - start, size); +		cur->remaining = size; +		cur->node = block; +		break; +	case TTM_PL_TT: +		node = to_ttm_range_mgr_node(res)->mm_nodes; +		while (start >= node->size << PAGE_SHIFT) +			start -= node++->size << PAGE_SHIFT; + +		cur->start = (node->start << PAGE_SHIFT) + start; +		cur->size = min((node->size << PAGE_SHIFT) - start, size); +		cur->remaining = size; +		cur->node = node; +		break; +	default: +		goto fallback; +	} -	cur->start = (node->start << PAGE_SHIFT) + start; -	cur->size = min((node->size << PAGE_SHIFT) - start, size); +	return; + +fallback: +	cur->start = start; +	cur->size = size;  	cur->remaining = size; -	cur->node = node; +	cur->node = NULL; +	WARN_ON(res && start + size > res->num_pages << PAGE_SHIFT); +	return;  }  /** @@ -85,7 +124,9 @@ static inline void amdgpu_res_first(struct ttm_resource *res,   */  static inline void amdgpu_res_next(struct amdgpu_res_cursor *cur, uint64_t size)  { -	struct drm_mm_node *node = cur->node; +	struct drm_buddy_block *block; +	struct drm_mm_node *node; +	struct list_head *next;  	BUG_ON(size > cur->remaining); @@ -99,9 +140,27 @@ static inline void amdgpu_res_next(struct amdgpu_res_cursor *cur, uint64_t size)  		return;  	} -	cur->node = ++node; -	cur->start = node->start << PAGE_SHIFT; -	cur->size = min(node->size << PAGE_SHIFT, cur->remaining); +	switch (cur->mem_type) { +	case TTM_PL_VRAM: +		block = cur->node; + +		next = block->link.next; +		block = list_entry(next, struct drm_buddy_block, link); + +		cur->node = block; +		cur->start = amdgpu_vram_mgr_block_start(block); +		cur->size = min(amdgpu_vram_mgr_block_size(block), cur->remaining); +		break; +	case TTM_PL_TT: +		node = cur->node; + +		cur->node = ++node; +		cur->start = node->start << PAGE_SHIFT; +		cur->size = min(node->size << PAGE_SHIFT, cur->remaining); +		break; +	default: +		return; +	}  }  #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c index 40e06745fae9..11c46b3e4c60 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c @@ -259,7 +259,8 @@ int amdgpu_sync_resv(struct amdgpu_device *adev, struct amdgpu_sync *sync,  	if (resv == NULL)  		return -EINVAL; -	dma_resv_for_each_fence(&cursor, resv, true, f) { +	/* TODO: Use DMA_RESV_USAGE_READ here */ +	dma_resv_for_each_fence(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP, f) {  		dma_fence_chain_for_each(f, f) {  			struct dma_fence *tmp = dma_fence_chain_contained(f); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index f7f149588432..ec26edd4f4d8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -1344,7 +1344,8 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,  	 * If true, then return false as any KFD process needs all its BOs to  	 * be resident to run successfully  	 */ -	dma_resv_for_each_fence(&resv_cursor, bo->base.resv, true, f) { +	dma_resv_for_each_fence(&resv_cursor, bo->base.resv, +				DMA_RESV_USAGE_BOOKKEEP, f) {  		if (amdkfd_fence_check_mm(f, current->mm))  			return false;  	} @@ -2160,17 +2161,6 @@ int amdgpu_ttm_evict_resources(struct amdgpu_device *adev, int mem_type)  #if defined(CONFIG_DEBUG_FS) -static int amdgpu_mm_vram_table_show(struct seq_file *m, void *unused) -{ -	struct amdgpu_device *adev = (struct amdgpu_device *)m->private; -	struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, -							    TTM_PL_VRAM); -	struct drm_printer p = drm_seq_file_printer(m); - -	ttm_resource_manager_debug(man, &p); -	return 0; -} -  static int amdgpu_ttm_page_pool_show(struct seq_file *m, void *unused)  {  	struct amdgpu_device *adev = (struct amdgpu_device *)m->private; @@ -2178,55 +2168,6 @@ static int amdgpu_ttm_page_pool_show(struct seq_file *m, void *unused)  	return ttm_pool_debugfs(&adev->mman.bdev.pool, m);  } -static int amdgpu_mm_tt_table_show(struct seq_file *m, void *unused) -{ -	struct amdgpu_device *adev = (struct amdgpu_device *)m->private; -	struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, -							    TTM_PL_TT); -	struct drm_printer p = drm_seq_file_printer(m); - -	ttm_resource_manager_debug(man, &p); -	return 0; -} - -static int amdgpu_mm_gds_table_show(struct seq_file *m, void *unused) -{ -	struct amdgpu_device *adev = (struct amdgpu_device *)m->private; -	struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, -							    AMDGPU_PL_GDS); -	struct drm_printer p = drm_seq_file_printer(m); - -	ttm_resource_manager_debug(man, &p); -	return 0; -} - -static int amdgpu_mm_gws_table_show(struct seq_file *m, void *unused) -{ -	struct amdgpu_device *adev = (struct amdgpu_device *)m->private; -	struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, -							    AMDGPU_PL_GWS); -	struct drm_printer p = drm_seq_file_printer(m); - -	ttm_resource_manager_debug(man, &p); -	return 0; -} - -static int amdgpu_mm_oa_table_show(struct seq_file *m, void *unused) -{ -	struct amdgpu_device *adev = (struct amdgpu_device *)m->private; -	struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, -							    AMDGPU_PL_OA); -	struct drm_printer p = drm_seq_file_printer(m); - -	ttm_resource_manager_debug(man, &p); -	return 0; -} - -DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_vram_table); -DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_tt_table); -DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_gds_table); -DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_gws_table); -DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_oa_table);  DEFINE_SHOW_ATTRIBUTE(amdgpu_ttm_page_pool);  /* @@ -2436,17 +2377,23 @@ void amdgpu_ttm_debugfs_init(struct amdgpu_device *adev)  				 &amdgpu_ttm_vram_fops, adev->gmc.mc_vram_size);  	debugfs_create_file("amdgpu_iomem", 0444, root, adev,  			    &amdgpu_ttm_iomem_fops); -	debugfs_create_file("amdgpu_vram_mm", 0444, root, adev, -			    &amdgpu_mm_vram_table_fops); -	debugfs_create_file("amdgpu_gtt_mm", 0444, root, adev, -			    &amdgpu_mm_tt_table_fops); -	debugfs_create_file("amdgpu_gds_mm", 0444, root, adev, -			    &amdgpu_mm_gds_table_fops); -	debugfs_create_file("amdgpu_gws_mm", 0444, root, adev, -			    &amdgpu_mm_gws_table_fops); -	debugfs_create_file("amdgpu_oa_mm", 0444, root, adev, -			    &amdgpu_mm_oa_table_fops);  	debugfs_create_file("ttm_page_pool", 0444, root, adev,  			    &amdgpu_ttm_page_pool_fops); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev, +							     TTM_PL_VRAM), +					    root, "amdgpu_vram_mm"); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev, +							     TTM_PL_TT), +					    root, "amdgpu_gtt_mm"); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev, +							     AMDGPU_PL_GDS), +					    root, "amdgpu_gds_mm"); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev, +							     AMDGPU_PL_GWS), +					    root, "amdgpu_gws_mm"); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev, +							     AMDGPU_PL_OA), +					    root, "amdgpu_oa_mm"); +  #endif  } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index 9120ae80ef52..6a70818039dd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -26,6 +26,7 @@  #include <linux/dma-direction.h>  #include <drm/gpu_scheduler.h> +#include "amdgpu_vram_mgr.h"  #include "amdgpu.h"  #define AMDGPU_PL_GDS		(TTM_PL_PRIV + 0) @@ -38,15 +39,6 @@  #define AMDGPU_POISON	0xd0bed0be -struct amdgpu_vram_mgr { -	struct ttm_resource_manager manager; -	struct drm_mm mm; -	spinlock_t lock; -	struct list_head reservations_pending; -	struct list_head reserved_pages; -	atomic64_t vis_usage; -}; -  struct amdgpu_gtt_mgr {  	struct ttm_resource_manager manager;  	struct drm_mm mm; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 39c74d9fa7cc..6eac649499d3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -1163,7 +1163,8 @@ static int amdgpu_uvd_send_msg(struct amdgpu_ring *ring, struct amdgpu_bo *bo,  	ib->length_dw = 16;  	if (direct) { -		r = dma_resv_wait_timeout(bo->tbo.base.resv, true, false, +		r = dma_resv_wait_timeout(bo->tbo.base.resv, +					  DMA_RESV_USAGE_KERNEL, false,  					  msecs_to_jiffies(10));  		if (r == 0)  			r = -ETIMEDOUT; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index b13451255e8b..5277c10d901d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2059,7 +2059,7 @@ static void amdgpu_vm_prt_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)  	struct dma_resv_iter cursor;  	struct dma_fence *fence; -	dma_resv_for_each_fence(&cursor, resv, true, fence) { +	dma_resv_for_each_fence(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP, fence) {  		/* Add a callback for each fence in the reservation object */  		amdgpu_vm_prt_get(adev);  		amdgpu_vm_add_prt_cb(adev, fence); @@ -2665,7 +2665,7 @@ bool amdgpu_vm_evictable(struct amdgpu_bo *bo)  		return true;  	/* Don't evict VM page tables while they are busy */ -	if (!dma_resv_test_signaled(bo->tbo.base.resv, true)) +	if (!dma_resv_test_signaled(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP))  		return false;  	/* Try to block ongoing updates */ @@ -2845,7 +2845,8 @@ void amdgpu_vm_adjust_size(struct amdgpu_device *adev, uint32_t min_vm_size,   */  long amdgpu_vm_wait_idle(struct amdgpu_vm *vm, long timeout)  { -	timeout = dma_resv_wait_timeout(vm->root.bo->tbo.base.resv, true, +	timeout = dma_resv_wait_timeout(vm->root.bo->tbo.base.resv, +					DMA_RESV_USAGE_BOOKKEEP,  					true, timeout);  	if (timeout <= 0)  		return timeout; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c index e3fbf0f10add..31913ae86de6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c @@ -74,13 +74,12 @@ static int amdgpu_vm_cpu_update(struct amdgpu_vm_update_params *p,  {  	unsigned int i;  	uint64_t value; -	int r; +	long r; -	if (vmbo->bo.tbo.moving) { -		r = dma_fence_wait(vmbo->bo.tbo.moving, true); -		if (r) -			return r; -	} +	r = dma_resv_wait_timeout(vmbo->bo.tbo.base.resv, DMA_RESV_USAGE_KERNEL, +				  true, MAX_SCHEDULE_TIMEOUT); +	if (r < 0) +		return r;  	pe += (unsigned long)amdgpu_bo_kptr(&vmbo->bo); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c index dbb551762805..bdb44cee19d3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c @@ -204,14 +204,19 @@ static int amdgpu_vm_sdma_update(struct amdgpu_vm_update_params *p,  	struct amdgpu_bo *bo = &vmbo->bo;  	enum amdgpu_ib_pool_type pool = p->immediate ? AMDGPU_IB_POOL_IMMEDIATE  		: AMDGPU_IB_POOL_DELAYED; +	struct dma_resv_iter cursor;  	unsigned int i, ndw, nptes; +	struct dma_fence *fence;  	uint64_t *pte;  	int r;  	/* Wait for PD/PT moves to be completed */ -	r = amdgpu_sync_fence(&p->job->sync, bo->tbo.moving); -	if (r) -		return r; +	dma_resv_for_each_fence(&cursor, bo->tbo.base.resv, +				DMA_RESV_USAGE_KERNEL, fence) { +		r = amdgpu_sync_fence(&p->job->sync, fence); +		if (r) +			return r; +	}  	do {  		ndw = p->num_dw_left; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 0a7611648573..49e4092f447f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -32,8 +32,10 @@  #include "atom.h"  struct amdgpu_vram_reservation { -	struct list_head node; -	struct drm_mm_node mm_node; +	u64 start; +	u64 size; +	struct list_head allocated; +	struct list_head blocks;  };  static inline struct amdgpu_vram_mgr * @@ -186,18 +188,18 @@ const struct attribute_group amdgpu_vram_mgr_attr_group = {  };  /** - * amdgpu_vram_mgr_vis_size - Calculate visible node size + * amdgpu_vram_mgr_vis_size - Calculate visible block size   *   * @adev: amdgpu_device pointer - * @node: MM node structure + * @block: DRM BUDDY block structure   * - * Calculate how many bytes of the MM node are inside visible VRAM + * Calculate how many bytes of the DRM BUDDY block are inside visible VRAM   */  static u64 amdgpu_vram_mgr_vis_size(struct amdgpu_device *adev, -				    struct drm_mm_node *node) +				    struct drm_buddy_block *block)  { -	uint64_t start = node->start << PAGE_SHIFT; -	uint64_t end = (node->size + node->start) << PAGE_SHIFT; +	u64 start = amdgpu_vram_mgr_block_start(block); +	u64 end = start + amdgpu_vram_mgr_block_size(block);  	if (start >= adev->gmc.visible_vram_size)  		return 0; @@ -218,9 +220,9 @@ u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)  {  	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);  	struct ttm_resource *res = bo->tbo.resource; -	unsigned pages = res->num_pages; -	struct drm_mm_node *mm; -	u64 usage; +	struct amdgpu_vram_mgr_resource *vres = to_amdgpu_vram_mgr_resource(res); +	struct drm_buddy_block *block; +	u64 usage = 0;  	if (amdgpu_gmc_vram_full_visible(&adev->gmc))  		return amdgpu_bo_size(bo); @@ -228,9 +230,8 @@ u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)  	if (res->start >= adev->gmc.visible_vram_size >> PAGE_SHIFT)  		return 0; -	mm = &container_of(res, struct ttm_range_mgr_node, base)->mm_nodes[0]; -	for (usage = 0; pages; pages -= mm->size, mm++) -		usage += amdgpu_vram_mgr_vis_size(adev, mm); +	list_for_each_entry(block, &vres->blocks, link) +		usage += amdgpu_vram_mgr_vis_size(adev, block);  	return usage;  } @@ -240,23 +241,30 @@ static void amdgpu_vram_mgr_do_reserve(struct ttm_resource_manager *man)  {  	struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);  	struct amdgpu_device *adev = to_amdgpu_device(mgr); -	struct drm_mm *mm = &mgr->mm; +	struct drm_buddy *mm = &mgr->mm;  	struct amdgpu_vram_reservation *rsv, *temp; +	struct drm_buddy_block *block;  	uint64_t vis_usage; -	list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, node) { -		if (drm_mm_reserve_node(mm, &rsv->mm_node)) +	list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, blocks) { +		if (drm_buddy_alloc_blocks(mm, rsv->start, rsv->start + rsv->size, +					   rsv->size, mm->chunk_size, &rsv->allocated, +					   DRM_BUDDY_RANGE_ALLOCATION)) +			continue; + +		block = amdgpu_vram_mgr_first_block(&rsv->allocated); +		if (!block)  			continue;  		dev_dbg(adev->dev, "Reservation 0x%llx - %lld, Succeeded\n", -			rsv->mm_node.start, rsv->mm_node.size); +			rsv->start, rsv->size); -		vis_usage = amdgpu_vram_mgr_vis_size(adev, &rsv->mm_node); +		vis_usage = amdgpu_vram_mgr_vis_size(adev, block);  		atomic64_add(vis_usage, &mgr->vis_usage);  		spin_lock(&man->bdev->lru_lock); -		man->usage += rsv->mm_node.size << PAGE_SHIFT; +		man->usage += rsv->size;  		spin_unlock(&man->bdev->lru_lock); -		list_move(&rsv->node, &mgr->reserved_pages); +		list_move(&rsv->blocks, &mgr->reserved_pages);  	}  } @@ -278,14 +286,16 @@ int amdgpu_vram_mgr_reserve_range(struct amdgpu_vram_mgr *mgr,  	if (!rsv)  		return -ENOMEM; -	INIT_LIST_HEAD(&rsv->node); -	rsv->mm_node.start = start >> PAGE_SHIFT; -	rsv->mm_node.size = size >> PAGE_SHIFT; +	INIT_LIST_HEAD(&rsv->allocated); +	INIT_LIST_HEAD(&rsv->blocks); -	spin_lock(&mgr->lock); -	list_add_tail(&rsv->node, &mgr->reservations_pending); +	rsv->start = start; +	rsv->size = size; + +	mutex_lock(&mgr->lock); +	list_add_tail(&rsv->blocks, &mgr->reservations_pending);  	amdgpu_vram_mgr_do_reserve(&mgr->manager); -	spin_unlock(&mgr->lock); +	mutex_unlock(&mgr->lock);  	return 0;  } @@ -307,19 +317,19 @@ int amdgpu_vram_mgr_query_page_status(struct amdgpu_vram_mgr *mgr,  	struct amdgpu_vram_reservation *rsv;  	int ret; -	spin_lock(&mgr->lock); +	mutex_lock(&mgr->lock); -	list_for_each_entry(rsv, &mgr->reservations_pending, node) { -		if ((rsv->mm_node.start <= start) && -		    (start < (rsv->mm_node.start + rsv->mm_node.size))) { +	list_for_each_entry(rsv, &mgr->reservations_pending, blocks) { +		if (rsv->start <= start && +		    (start < (rsv->start + rsv->size))) {  			ret = -EBUSY;  			goto out;  		}  	} -	list_for_each_entry(rsv, &mgr->reserved_pages, node) { -		if ((rsv->mm_node.start <= start) && -		    (start < (rsv->mm_node.start + rsv->mm_node.size))) { +	list_for_each_entry(rsv, &mgr->reserved_pages, blocks) { +		if (rsv->start <= start && +		    (start < (rsv->start + rsv->size))) {  			ret = 0;  			goto out;  		} @@ -327,33 +337,11 @@ int amdgpu_vram_mgr_query_page_status(struct amdgpu_vram_mgr *mgr,  	ret = -ENOENT;  out: -	spin_unlock(&mgr->lock); +	mutex_unlock(&mgr->lock);  	return ret;  }  /** - * amdgpu_vram_mgr_virt_start - update virtual start address - * - * @mem: ttm_resource to update - * @node: just allocated node - * - * Calculate a virtual BO start address to easily check if everything is CPU - * accessible. - */ -static void amdgpu_vram_mgr_virt_start(struct ttm_resource *mem, -				       struct drm_mm_node *node) -{ -	unsigned long start; - -	start = node->start + node->size; -	if (start > mem->num_pages) -		start -= mem->num_pages; -	else -		start = 0; -	mem->start = max(mem->start, start); -} - -/**   * amdgpu_vram_mgr_new - allocate new ranges   *   * @man: TTM memory type manager @@ -368,46 +356,44 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,  			       const struct ttm_place *place,  			       struct ttm_resource **res)  { -	unsigned long lpfn, num_nodes, pages_per_node, pages_left, pages; +	u64 vis_usage = 0, max_bytes, cur_size, min_block_size;  	struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);  	struct amdgpu_device *adev = to_amdgpu_device(mgr); -	uint64_t vis_usage = 0, mem_bytes, max_bytes; -	struct ttm_range_mgr_node *node; -	struct drm_mm *mm = &mgr->mm; -	enum drm_mm_insert_mode mode; -	unsigned i; +	struct amdgpu_vram_mgr_resource *vres; +	u64 size, remaining_size, lpfn, fpfn; +	struct drm_buddy *mm = &mgr->mm; +	struct drm_buddy_block *block; +	unsigned long pages_per_block;  	int r; -	lpfn = place->lpfn; +	lpfn = place->lpfn << PAGE_SHIFT;  	if (!lpfn) -		lpfn = man->size >> PAGE_SHIFT; +		lpfn = man->size; + +	fpfn = place->fpfn << PAGE_SHIFT;  	max_bytes = adev->gmc.mc_vram_size;  	if (tbo->type != ttm_bo_type_kernel)  		max_bytes -= AMDGPU_VM_RESERVED_VRAM; -	mem_bytes = tbo->base.size;  	if (place->flags & TTM_PL_FLAG_CONTIGUOUS) { -		pages_per_node = ~0ul; -		num_nodes = 1; +		pages_per_block = ~0ul;  	} else {  #ifdef CONFIG_TRANSPARENT_HUGEPAGE -		pages_per_node = HPAGE_PMD_NR; +		pages_per_block = HPAGE_PMD_NR;  #else  		/* default to 2MB */ -		pages_per_node = 2UL << (20UL - PAGE_SHIFT); +		pages_per_block = 2UL << (20UL - PAGE_SHIFT);  #endif -		pages_per_node = max_t(uint32_t, pages_per_node, -				       tbo->page_alignment); -		num_nodes = DIV_ROUND_UP_ULL(PFN_UP(mem_bytes), pages_per_node); +		pages_per_block = max_t(uint32_t, pages_per_block, +					tbo->page_alignment);  	} -	node = kvmalloc(struct_size(node, mm_nodes, num_nodes), -			GFP_KERNEL | __GFP_ZERO); -	if (!node) +	vres = kzalloc(sizeof(*vres), GFP_KERNEL); +	if (!vres)  		return -ENOMEM; -	ttm_resource_init(tbo, place, &node->base); +	ttm_resource_init(tbo, place, &vres->base);  	/* bail out quickly if there's likely not enough VRAM for this BO */  	if (ttm_resource_manager_usage(man) > max_bytes) { @@ -415,66 +401,130 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,  		goto error_fini;  	} -	mode = DRM_MM_INSERT_BEST; +	INIT_LIST_HEAD(&vres->blocks); +  	if (place->flags & TTM_PL_FLAG_TOPDOWN) -		mode = DRM_MM_INSERT_HIGH; - -	pages_left = node->base.num_pages; - -	/* Limit maximum size to 2GB due to SG table limitations */ -	pages = min(pages_left, 2UL << (30 - PAGE_SHIFT)); - -	i = 0; -	spin_lock(&mgr->lock); -	while (pages_left) { -		uint32_t alignment = tbo->page_alignment; - -		if (pages >= pages_per_node) -			alignment = pages_per_node; - -		r = drm_mm_insert_node_in_range(mm, &node->mm_nodes[i], pages, -						alignment, 0, place->fpfn, -						lpfn, mode); -		if (unlikely(r)) { -			if (pages > pages_per_node) { -				if (is_power_of_2(pages)) -					pages = pages / 2; -				else -					pages = rounddown_pow_of_two(pages); -				continue; +		vres->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION; + +	if (fpfn || lpfn != man->size) +		/* Allocate blocks in desired range */ +		vres->flags |= DRM_BUDDY_RANGE_ALLOCATION; + +	remaining_size = vres->base.num_pages << PAGE_SHIFT; + +	mutex_lock(&mgr->lock); +	while (remaining_size) { +		if (tbo->page_alignment) +			min_block_size = tbo->page_alignment << PAGE_SHIFT; +		else +			min_block_size = mgr->default_page_size; + +		BUG_ON(min_block_size < mm->chunk_size); + +		/* Limit maximum size to 2GiB due to SG table limitations */ +		size = min(remaining_size, 2ULL << 30); + +		if (size >= pages_per_block << PAGE_SHIFT) +			min_block_size = pages_per_block << PAGE_SHIFT; + +		cur_size = size; + +		if (fpfn + size != place->lpfn << PAGE_SHIFT) { +			/* +			 * Except for actual range allocation, modify the size and +			 * min_block_size conforming to continuous flag enablement +			 */ +			if (place->flags & TTM_PL_FLAG_CONTIGUOUS) { +				size = roundup_pow_of_two(size); +				min_block_size = size; +			/* +			 * Modify the size value if size is not +			 * aligned with min_block_size +			 */ +			} else if (!IS_ALIGNED(size, min_block_size)) { +				size = round_up(size, min_block_size);  			} -			goto error_free;  		} -		vis_usage += amdgpu_vram_mgr_vis_size(adev, &node->mm_nodes[i]); -		amdgpu_vram_mgr_virt_start(&node->base, &node->mm_nodes[i]); -		pages_left -= pages; -		++i; +		r = drm_buddy_alloc_blocks(mm, fpfn, +					   lpfn, +					   size, +					   min_block_size, +					   &vres->blocks, +					   vres->flags); +		if (unlikely(r)) +			goto error_free_blocks; + +		if (size > remaining_size) +			remaining_size = 0; +		else +			remaining_size -= size; +	} +	mutex_unlock(&mgr->lock); + +	if (cur_size != size) { +		struct drm_buddy_block *block; +		struct list_head *trim_list; +		u64 original_size; +		LIST_HEAD(temp); + +		trim_list = &vres->blocks; +		original_size = vres->base.num_pages << PAGE_SHIFT; + +		/* +		 * If size value is rounded up to min_block_size, trim the last +		 * block to the required size +		 */ +		if (!list_is_singular(&vres->blocks)) { +			block = list_last_entry(&vres->blocks, typeof(*block), link); +			list_move_tail(&block->link, &temp); +			trim_list = &temp; +			/* +			 * Compute the original_size value by subtracting the +			 * last block size with (aligned size - original size) +			 */ +			original_size = amdgpu_vram_mgr_block_size(block) - (size - cur_size); +		} -		if (pages > pages_left) -			pages = pages_left; +		mutex_lock(&mgr->lock); +		drm_buddy_block_trim(mm, +				     original_size, +				     trim_list); +		mutex_unlock(&mgr->lock); + +		if (!list_empty(&temp)) +			list_splice_tail(trim_list, &vres->blocks); +	} + +	list_for_each_entry(block, &vres->blocks, link) +		vis_usage += amdgpu_vram_mgr_vis_size(adev, block); + +	block = amdgpu_vram_mgr_first_block(&vres->blocks); +	if (!block) { +		r = -EINVAL; +		goto error_fini;  	} -	spin_unlock(&mgr->lock); -	if (i == 1) -		node->base.placement |= TTM_PL_FLAG_CONTIGUOUS; +	vres->base.start = amdgpu_vram_mgr_block_start(block) >> PAGE_SHIFT; + +	if (amdgpu_is_vram_mgr_blocks_contiguous(&vres->blocks)) +		vres->base.placement |= TTM_PL_FLAG_CONTIGUOUS;  	if (adev->gmc.xgmi.connected_to_cpu) -		node->base.bus.caching = ttm_cached; +		vres->base.bus.caching = ttm_cached;  	else -		node->base.bus.caching = ttm_write_combined; +		vres->base.bus.caching = ttm_write_combined;  	atomic64_add(vis_usage, &mgr->vis_usage); -	*res = &node->base; +	*res = &vres->base;  	return 0; -error_free: -	while (i--) -		drm_mm_remove_node(&node->mm_nodes[i]); -	spin_unlock(&mgr->lock); +error_free_blocks: +	drm_buddy_free_list(mm, &vres->blocks); +	mutex_unlock(&mgr->lock);  error_fini: -	ttm_resource_fini(man, &node->base); -	kvfree(node); +	ttm_resource_fini(man, &vres->base); +	kfree(vres);  	return r;  } @@ -490,27 +540,26 @@ error_fini:  static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man,  				struct ttm_resource *res)  { -	struct ttm_range_mgr_node *node = to_ttm_range_mgr_node(res); +	struct amdgpu_vram_mgr_resource *vres = to_amdgpu_vram_mgr_resource(res);  	struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);  	struct amdgpu_device *adev = to_amdgpu_device(mgr); +	struct drm_buddy *mm = &mgr->mm; +	struct drm_buddy_block *block;  	uint64_t vis_usage = 0; -	unsigned i, pages; -	spin_lock(&mgr->lock); -	for (i = 0, pages = res->num_pages; pages; -	     pages -= node->mm_nodes[i].size, ++i) { -		struct drm_mm_node *mm = &node->mm_nodes[i]; +	mutex_lock(&mgr->lock); +	list_for_each_entry(block, &vres->blocks, link) +		vis_usage += amdgpu_vram_mgr_vis_size(adev, block); -		drm_mm_remove_node(mm); -		vis_usage += amdgpu_vram_mgr_vis_size(adev, mm); -	}  	amdgpu_vram_mgr_do_reserve(man); -	spin_unlock(&mgr->lock); + +	drm_buddy_free_list(mm, &vres->blocks); +	mutex_unlock(&mgr->lock);  	atomic64_sub(vis_usage, &mgr->vis_usage);  	ttm_resource_fini(man, res); -	kvfree(node); +	kfree(vres);  }  /** @@ -542,7 +591,7 @@ int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev,  	if (!*sgt)  		return -ENOMEM; -	/* Determine the number of DRM_MM nodes to export */ +	/* Determine the number of DRM_BUDDY blocks to export */  	amdgpu_res_first(res, offset, length, &cursor);  	while (cursor.remaining) {  		num_entries++; @@ -558,10 +607,10 @@ int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev,  		sg->length = 0;  	/* -	 * Walk down DRM_MM nodes to populate scatterlist nodes -	 * @note: Use iterator api to get first the DRM_MM node +	 * Walk down DRM_BUDDY blocks to populate scatterlist nodes +	 * @note: Use iterator api to get first the DRM_BUDDY block  	 * and the number of bytes from it. Access the following -	 * DRM_MM node(s) if more buffer needs to exported +	 * DRM_BUDDY block(s) if more buffer needs to exported  	 */  	amdgpu_res_first(res, offset, length, &cursor);  	for_each_sgtable_sg((*sgt), sg, i) { @@ -648,13 +697,22 @@ static void amdgpu_vram_mgr_debug(struct ttm_resource_manager *man,  				  struct drm_printer *printer)  {  	struct amdgpu_vram_mgr *mgr = to_vram_mgr(man); +	struct drm_buddy *mm = &mgr->mm; +	struct drm_buddy_block *block;  	drm_printf(printer, "  vis usage:%llu\n",  		   amdgpu_vram_mgr_vis_usage(mgr)); -	spin_lock(&mgr->lock); -	drm_mm_print(&mgr->mm, printer); -	spin_unlock(&mgr->lock); +	mutex_lock(&mgr->lock); +	drm_printf(printer, "default_page_size: %lluKiB\n", +		   mgr->default_page_size >> 10); + +	drm_buddy_print(mm, printer); + +	drm_printf(printer, "reserved:\n"); +	list_for_each_entry(block, &mgr->reserved_pages, link) +		drm_buddy_block_print(mm, block, printer); +	mutex_unlock(&mgr->lock);  }  static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = { @@ -674,16 +732,21 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev)  {  	struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;  	struct ttm_resource_manager *man = &mgr->manager; +	int err;  	ttm_resource_manager_init(man, &adev->mman.bdev,  				  adev->gmc.real_vram_size);  	man->func = &amdgpu_vram_mgr_func; -	drm_mm_init(&mgr->mm, 0, man->size >> PAGE_SHIFT); -	spin_lock_init(&mgr->lock); +	err = drm_buddy_init(&mgr->mm, man->size, PAGE_SIZE); +	if (err) +		return err; + +	mutex_init(&mgr->lock);  	INIT_LIST_HEAD(&mgr->reservations_pending);  	INIT_LIST_HEAD(&mgr->reserved_pages); +	mgr->default_page_size = PAGE_SIZE;  	ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager);  	ttm_resource_manager_set_used(man, true); @@ -711,16 +774,16 @@ void amdgpu_vram_mgr_fini(struct amdgpu_device *adev)  	if (ret)  		return; -	spin_lock(&mgr->lock); -	list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, node) +	mutex_lock(&mgr->lock); +	list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, blocks)  		kfree(rsv); -	list_for_each_entry_safe(rsv, temp, &mgr->reserved_pages, node) { -		drm_mm_remove_node(&rsv->mm_node); +	list_for_each_entry_safe(rsv, temp, &mgr->reserved_pages, blocks) { +		drm_buddy_free_list(&mgr->mm, &rsv->blocks);  		kfree(rsv);  	} -	drm_mm_takedown(&mgr->mm); -	spin_unlock(&mgr->lock); +	drm_buddy_fini(&mgr->mm); +	mutex_unlock(&mgr->lock);  	ttm_resource_manager_cleanup(man);  	ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, NULL); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h new file mode 100644 index 000000000000..9a2db87186c7 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: MIT + * Copyright 2021 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __AMDGPU_VRAM_MGR_H__ +#define __AMDGPU_VRAM_MGR_H__ + +#include <drm/drm_buddy.h> + +struct amdgpu_vram_mgr { +	struct ttm_resource_manager manager; +	struct drm_buddy mm; +	/* protects access to buffer objects */ +	struct mutex lock; +	struct list_head reservations_pending; +	struct list_head reserved_pages; +	atomic64_t vis_usage; +	u64 default_page_size; +}; + +struct amdgpu_vram_mgr_resource { +	struct ttm_resource base; +	struct list_head blocks; +	unsigned long flags; +}; + +static inline u64 amdgpu_vram_mgr_block_start(struct drm_buddy_block *block) +{ +	return drm_buddy_block_offset(block); +} + +static inline u64 amdgpu_vram_mgr_block_size(struct drm_buddy_block *block) +{ +	return PAGE_SIZE << drm_buddy_block_order(block); +} + +static inline struct drm_buddy_block * +amdgpu_vram_mgr_first_block(struct list_head *list) +{ +	return list_first_entry_or_null(list, struct drm_buddy_block, link); +} + +static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head) +{ +	struct drm_buddy_block *block; +	u64 start, size; + +	block = amdgpu_vram_mgr_first_block(head); +	if (!block) +		return false; + +	while (head != block->link.next) { +		start = amdgpu_vram_mgr_block_start(block); +		size = amdgpu_vram_mgr_block_size(block); + +		block = list_entry(block->link.next, struct drm_buddy_block, link); +		if (start + size != amdgpu_vram_mgr_block_start(block)) +			return false; +	} + +	return true; +} + +static inline struct amdgpu_vram_mgr_resource * +to_amdgpu_vram_mgr_resource(struct ttm_resource *res) +{ +	return container_of(res, struct amdgpu_vram_mgr_resource, base); +} + +#endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 62139ff35476..73423b805b54 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -9238,7 +9238,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,  		 * deadlock during GPU reset when this fence will not signal  		 * but we hold reservation lock for the BO.  		 */ -		r = dma_resv_wait_timeout(abo->tbo.base.resv, true, false, +		r = dma_resv_wait_timeout(abo->tbo.base.resv, +					  DMA_RESV_USAGE_WRITE, false,  					  msecs_to_jiffies(5000));  		if (unlikely(r <= 0))  			DRM_ERROR("Waiting for fences timed out!"); diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c index 541949f2d44a..e0b9f7063b20 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c @@ -256,6 +256,10 @@ static int komeda_plane_add(struct komeda_kms_dev *kms,  	formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl,  					       layer->layer_type, &n_formats); +	if (!formats) { +		kfree(kplane); +		return -ENOMEM; +	}  	err = drm_universal_plane_init(&kms->base, plane,  			get_possible_crtcs(kms, c->pipeline), @@ -266,8 +270,10 @@ static int komeda_plane_add(struct komeda_kms_dev *kms,  	komeda_put_fourcc_list(formats); -	if (err) -		goto cleanup; +	if (err) { +		kfree(kplane); +		return err; +	}  	drm_plane_helper_add(plane, &komeda_plane_helper_funcs); diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c index 494075ddbef6..b5928b52e279 100644 --- a/drivers/gpu/drm/arm/malidp_crtc.c +++ b/drivers/gpu/drm/arm/malidp_crtc.c @@ -487,7 +487,10 @@ static void malidp_crtc_reset(struct drm_crtc *crtc)  	if (crtc->state)  		malidp_crtc_destroy_state(crtc, crtc->state); -	__drm_atomic_helper_crtc_reset(crtc, &state->base); +	if (state) +		__drm_atomic_helper_crtc_reset(crtc, &state->base); +	else +		__drm_atomic_helper_crtc_reset(crtc, NULL);  }  static int malidp_crtc_enable_vblank(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 0562bdaac00c..81d9f5004025 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -310,17 +310,13 @@ static int malidp_se_check_scaling(struct malidp_plane *mp,  static u32 malidp_get_pgsize_bitmap(struct malidp_plane *mp)  { -	u32 pgsize_bitmap = 0; +	struct iommu_domain *mmu_dom; -	if (iommu_present(&platform_bus_type)) { -		struct iommu_domain *mmu_dom = -			iommu_get_domain_for_dev(mp->base.dev->dev); +	mmu_dom = iommu_get_domain_for_dev(mp->base.dev->dev); +	if (mmu_dom) +		return mmu_dom->pgsize_bitmap; -		if (mmu_dom) -			pgsize_bitmap = mmu_dom->pgsize_bitmap; -	} - -	return pgsize_bitmap; +	return 0;  }  /* diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 658837caaf39..c08ccb4b332b 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -32,6 +32,7 @@ config DRM_CHIPONE_ICN6211  	select DRM_KMS_HELPER  	select DRM_MIPI_DSI  	select DRM_PANEL_BRIDGE +	select REGMAP_I2C  	help  	  ICN6211 is MIPI-DSI/RGB Converter bridge from chipone. @@ -99,6 +100,19 @@ config DRM_LONTIUM_LT8912B  	  Say M here if you want to support this hardware as a module.  	  The module will be named "lontium-lt8912b". +config DRM_LONTIUM_LT9211 +	tristate "Lontium LT9211 DSI/LVDS/DPI bridge" +	depends on OF +	select DRM_PANEL_BRIDGE +	select DRM_KMS_HELPER +	select DRM_MIPI_DSI +	select REGMAP_I2C +	help +	  Driver for Lontium LT9211 Single/Dual-Link DSI/LVDS or Single DPI +	  input to Single-link/Dual-Link DSI/LVDS or Single DPI output bridge +	  chip. +	  Please say Y if you have such hardware. +  config DRM_LONTIUM_LT9611  	tristate "Lontium LT9611 DSI/HDMI bridge"  	select SND_SOC_HDMI_CODEC if SND_SOC diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 425844c30495..b0edf2022fa0 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o  obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o  obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o  obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o +obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o  obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o  obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o  obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 668dcefbae17..b3f10c54e064 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -1292,8 +1292,10 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)  		goto err_unregister_cec;  	adv7511->bridge.funcs = &adv7511_bridge_funcs; -	adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID -			    | DRM_BRIDGE_OP_HPD; +	adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; +	if (adv7511->i2c_main->irq) +		adv7511->bridge.ops |= DRM_BRIDGE_OP_HPD; +  	adv7511->bridge.of_node = dev->of_node;  	adv7511->bridge.type = DRM_MODE_CONNECTOR_HDMIA; diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 6516f9570b86..376da01243a3 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -1486,12 +1486,12 @@ static void anx7625_dp_adjust_swing(struct anx7625_data *ctx)  	for (i = 0; i < ctx->pdata.dp_lane0_swing_reg_cnt; i++)  		anx7625_reg_write(ctx, ctx->i2c.tx_p1_client,  				  DP_TX_LANE0_SWING_REG0 + i, -				  ctx->pdata.lane0_reg_data[i] & 0xFF); +				  ctx->pdata.lane0_reg_data[i]);  	for (i = 0; i < ctx->pdata.dp_lane1_swing_reg_cnt; i++)  		anx7625_reg_write(ctx, ctx->i2c.tx_p1_client,  				  DP_TX_LANE1_SWING_REG0 + i, -				  ctx->pdata.lane1_reg_data[i] & 0xFF); +				  ctx->pdata.lane1_reg_data[i]);  }  static void dp_hpd_change_handler(struct anx7625_data *ctx, bool on) @@ -1598,8 +1598,8 @@ static int anx7625_get_swing_setting(struct device *dev,  			num_regs = DP_TX_SWING_REG_CNT;  		pdata->dp_lane0_swing_reg_cnt = num_regs; -		of_property_read_u32_array(dev->of_node, "analogix,lane0-swing", -					   pdata->lane0_reg_data, num_regs); +		of_property_read_u8_array(dev->of_node, "analogix,lane0-swing", +					  pdata->lane0_reg_data, num_regs);  	}  	if (of_get_property(dev->of_node, @@ -1608,8 +1608,8 @@ static int anx7625_get_swing_setting(struct device *dev,  			num_regs = DP_TX_SWING_REG_CNT;  		pdata->dp_lane1_swing_reg_cnt = num_regs; -		of_property_read_u32_array(dev->of_node, "analogix,lane1-swing", -					   pdata->lane1_reg_data, num_regs); +		of_property_read_u8_array(dev->of_node, "analogix,lane1-swing", +					  pdata->lane1_reg_data, num_regs);  	}  	return 0; @@ -1932,14 +1932,14 @@ static int anx7625_audio_get_eld(struct device *dev, void *data,  	struct anx7625_data *ctx = dev_get_drvdata(dev);  	if (!ctx->connector) { -		dev_err(dev, "connector not initial\n"); -		return -EINVAL; +		/* Pass en empty ELD if connector not available */ +		memset(buf, 0, len); +	} else { +		dev_dbg(dev, "audio copy eld\n"); +		memcpy(buf, ctx->connector->eld, +		       min(sizeof(ctx->connector->eld), len));  	} -	dev_dbg(dev, "audio copy eld\n"); -	memcpy(buf, ctx->connector->eld, -	       min(sizeof(ctx->connector->eld), len)); -  	return 0;  } diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index edbbfe410a56..e257a84db962 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -426,9 +426,9 @@ struct anx7625_platform_data {  	int mipi_lanes;  	int audio_en;  	int dp_lane0_swing_reg_cnt; -	int lane0_reg_data[DP_TX_SWING_REG_CNT]; +	u8 lane0_reg_data[DP_TX_SWING_REG_CNT];  	int dp_lane1_swing_reg_cnt; -	int lane1_reg_data[DP_TX_SWING_REG_CNT]; +	u8 lane1_reg_data[DP_TX_SWING_REG_CNT];  	u32 low_power_mode;  	struct device_node *mipi_host_node;  }; diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index b72aa3f3b5c2..47dea657a752 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -14,6 +14,7 @@  #include <linux/i2c.h>  #include <linux/module.h>  #include <linux/of_device.h> +#include <linux/regmap.h>  #include <linux/regulator/consumer.h>  #define VENDOR_ID		0x00 @@ -134,6 +135,7 @@  struct chipone {  	struct device *dev; +	struct regmap *regmap;  	struct i2c_client *client;  	struct drm_bridge bridge;  	struct drm_display_mode mode; @@ -146,6 +148,77 @@ struct chipone {  	bool interface_i2c;  }; +static const struct regmap_range chipone_dsi_readable_ranges[] = { +	regmap_reg_range(VENDOR_ID, VERSION_ID), +	regmap_reg_range(FIRMWARE_VERSION, PLL_SSC_OFFSET(3)), +	regmap_reg_range(GPIO_OEN, MIPI_ULPS_CTRL), +	regmap_reg_range(MIPI_CLK_CHK_VAR, MIPI_T_TA_SURE_PRE), +	regmap_reg_range(MIPI_T_LPX_SET, MIPI_INIT_TIME_H), +	regmap_reg_range(MIPI_T_CLK_TERM_EN, MIPI_T_CLK_SETTLE), +	regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY_(5)), +	regmap_reg_range(MIPI_PD_RX, MIPI_RST_NUM), +	regmap_reg_range(MIPI_DBG_SET_(0), MIPI_DBG_SET_(9)), +	regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS_(1)), +}; + +static const struct regmap_access_table chipone_dsi_readable_table = { +	.yes_ranges = chipone_dsi_readable_ranges, +	.n_yes_ranges = ARRAY_SIZE(chipone_dsi_readable_ranges), +}; + +static const struct regmap_range chipone_dsi_writeable_ranges[] = { +	regmap_reg_range(CONFIG_FINISH, PLL_SSC_OFFSET(3)), +	regmap_reg_range(GPIO_OEN, MIPI_ULPS_CTRL), +	regmap_reg_range(MIPI_CLK_CHK_VAR, MIPI_T_TA_SURE_PRE), +	regmap_reg_range(MIPI_T_LPX_SET, MIPI_INIT_TIME_H), +	regmap_reg_range(MIPI_T_CLK_TERM_EN, MIPI_T_CLK_SETTLE), +	regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY_(5)), +	regmap_reg_range(MIPI_PD_RX, MIPI_RST_NUM), +	regmap_reg_range(MIPI_DBG_SET_(0), MIPI_DBG_SET_(9)), +	regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS_(1)), +}; + +static const struct regmap_access_table chipone_dsi_writeable_table = { +	.yes_ranges = chipone_dsi_writeable_ranges, +	.n_yes_ranges = ARRAY_SIZE(chipone_dsi_writeable_ranges), +}; + +static const struct regmap_config chipone_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.rd_table = &chipone_dsi_readable_table, +	.wr_table = &chipone_dsi_writeable_table, +	.cache_type = REGCACHE_RBTREE, +	.max_register = MIPI_ATE_STATUS_(1), +}; + +static int chipone_dsi_read(void *context, +			    const void *reg, size_t reg_size, +			    void *val, size_t val_size) +{ +	struct mipi_dsi_device *dsi = context; +	const u16 reg16 = (val_size << 8) | *(u8 *)reg; +	int ret; + +	ret = mipi_dsi_generic_read(dsi, ®16, 2, val, val_size); + +	return ret == val_size ? 0 : -EINVAL; +} + +static int chipone_dsi_write(void *context, const void *data, size_t count) +{ +	struct mipi_dsi_device *dsi = context; + +	return mipi_dsi_generic_write(dsi, data, 2); +} + +static const struct regmap_bus chipone_dsi_regmap_bus = { +	.read				= chipone_dsi_read, +	.write				= chipone_dsi_write, +	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE, +	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE, +}; +  static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge)  {  	return container_of(bridge, struct chipone, bridge); @@ -153,18 +226,16 @@ static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge)  static void chipone_readb(struct chipone *icn, u8 reg, u8 *val)  { -	if (icn->interface_i2c) -		*val = i2c_smbus_read_byte_data(icn->client, reg); -	else -		mipi_dsi_generic_read(icn->dsi, (u8[]){reg, 1}, 2, val, 1); +	int ret, pval; + +	ret = regmap_read(icn->regmap, reg, &pval); + +	*val = ret ? 0 : pval & 0xff;  }  static int chipone_writeb(struct chipone *icn, u8 reg, u8 val)  { -	if (icn->interface_i2c) -		return i2c_smbus_write_byte_data(icn->client, reg, val); -	else -		return mipi_dsi_generic_write(icn->dsi, (u8[]){reg, val}, 2); +	return regmap_write(icn->regmap, reg, val);  }  static void chipone_configure_pll(struct chipone *icn, @@ -324,6 +395,11 @@ static void chipone_atomic_enable(struct drm_bridge *bridge,  	/* dsi specific sequence */  	chipone_writeb(icn, SYNC_EVENT_DLY, 0x80);  	chipone_writeb(icn, HFP_MIN, hfp & 0xff); + +	/* DSI data lane count */ +	chipone_writeb(icn, DSI_CTRL, +		       DSI_CTRL_UNKNOWN | DSI_CTRL_DSI_LANES(icn->dsi->lanes - 1)); +  	chipone_writeb(icn, MIPI_PD_CK_LANE, 0xa0);  	chipone_writeb(icn, PLL_CTRL(12), 0xff);  	chipone_writeb(icn, MIPI_PN_SWAP, 0x00); @@ -409,9 +485,23 @@ static void chipone_mode_set(struct drm_bridge *bridge,  static int chipone_dsi_attach(struct chipone *icn)  {  	struct mipi_dsi_device *dsi = icn->dsi; -	int ret; +	struct device *dev = icn->dev; +	struct device_node *endpoint; +	int dsi_lanes, ret; + +	endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); +	dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); +	of_node_put(endpoint); + +	/* +	 * If the 'data-lanes' property does not exist in DT or is invalid, +	 * default to previously hard-coded behavior, which was 4 data lanes. +	 */ +	if (dsi_lanes >= 1 && dsi_lanes <= 4) +		icn->dsi->lanes = dsi_lanes; +	else +		icn->dsi->lanes = 4; -	dsi->lanes = 4;  	dsi->format = MIPI_DSI_FMT_RGB888;  	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |  			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; @@ -591,6 +681,11 @@ static int chipone_dsi_probe(struct mipi_dsi_device *dsi)  	if (ret)  		return ret; +	icn->regmap = devm_regmap_init(dev, &chipone_dsi_regmap_bus, +				       dsi, &chipone_regmap_config); +	if (IS_ERR(icn->regmap)) +		return PTR_ERR(icn->regmap); +  	icn->interface_i2c = false;  	icn->dsi = dsi; @@ -616,6 +711,10 @@ static int chipone_i2c_probe(struct i2c_client *client,  	if (ret)  		return ret; +	icn->regmap = devm_regmap_init_i2c(client, &chipone_regmap_config); +	if (IS_ERR(icn->regmap)) +		return PTR_ERR(icn->regmap); +  	icn->interface_i2c = true;  	icn->client = client;  	dev_set_drvdata(dev, icn); diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c index d24f5b90feab..e4d52a7e31b7 100644 --- a/drivers/gpu/drm/bridge/display-connector.c +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -24,6 +24,7 @@ struct display_connector {  	int			hpd_irq;  	struct regulator	*dp_pwr; +	struct gpio_desc	*ddc_en;  };  static inline struct display_connector * @@ -345,6 +346,17 @@ static int display_connector_probe(struct platform_device *pdev)  		}  	} +	/* enable DDC */ +	if (type == DRM_MODE_CONNECTOR_HDMIA) { +		conn->ddc_en = devm_gpiod_get_optional(&pdev->dev, "ddc-en", +						       GPIOD_OUT_HIGH); + +		if (IS_ERR(conn->ddc_en)) { +			dev_err(&pdev->dev, "Couldn't get ddc-en gpio\n"); +			return PTR_ERR(conn->ddc_en); +		} +	} +  	conn->bridge.funcs = &display_connector_bridge_funcs;  	conn->bridge.of_node = pdev->dev.of_node; @@ -373,6 +385,9 @@ static int display_connector_remove(struct platform_device *pdev)  {  	struct display_connector *conn = platform_get_drvdata(pdev); +	if (conn->ddc_en) +		gpiod_set_value(conn->ddc_en, 0); +  	if (conn->dp_pwr)  		regulator_disable(conn->dp_pwr); diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c new file mode 100644 index 000000000000..e92821fbc639 --- /dev/null +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -0,0 +1,802 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Lontium LT9211 bridge driver + * + * LT9211 is capable of converting: + *   2xDSI/2xLVDS/1xDPI -> 2xDSI/2xLVDS/1xDPI + * Currently supported is: + *   1xDSI -> 1xLVDS + * + * Copyright (C) 2022 Marek Vasut <marex@denx.de> + */ + +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +#define REG_PAGE_CONTROL			0xff +#define REG_CHIPID0				0x8100 +#define REG_CHIPID0_VALUE			0x18 +#define REG_CHIPID1				0x8101 +#define REG_CHIPID1_VALUE			0x01 +#define REG_CHIPID2				0x8102 +#define REG_CHIPID2_VALUE			0xe3 + +#define REG_DSI_LANE				0xd000 +/* DSI lane count - 0 means 4 lanes ; 1, 2, 3 means 1, 2, 3 lanes. */ +#define REG_DSI_LANE_COUNT(n)			((n) & 3) + +struct lt9211 { +	struct drm_bridge		bridge; +	struct device			*dev; +	struct regmap			*regmap; +	struct mipi_dsi_device		*dsi; +	struct drm_bridge		*panel_bridge; +	struct gpio_desc		*reset_gpio; +	struct regulator		*vccio; +	bool				lvds_dual_link; +	bool				lvds_dual_link_even_odd_swap; +}; + +static const struct regmap_range lt9211_rw_ranges[] = { +	regmap_reg_range(0xff, 0xff), +	regmap_reg_range(0x8100, 0x816b), +	regmap_reg_range(0x8200, 0x82aa), +	regmap_reg_range(0x8500, 0x85ff), +	regmap_reg_range(0x8600, 0x86a0), +	regmap_reg_range(0x8700, 0x8746), +	regmap_reg_range(0xd000, 0xd0a7), +	regmap_reg_range(0xd400, 0xd42c), +	regmap_reg_range(0xd800, 0xd838), +	regmap_reg_range(0xd9c0, 0xd9d5), +}; + +static const struct regmap_access_table lt9211_rw_table = { +	.yes_ranges = lt9211_rw_ranges, +	.n_yes_ranges = ARRAY_SIZE(lt9211_rw_ranges), +}; + +static const struct regmap_range_cfg lt9211_range = { +	.name = "lt9211", +	.range_min = 0x0000, +	.range_max = 0xda00, +	.selector_reg = REG_PAGE_CONTROL, +	.selector_mask = 0xff, +	.selector_shift = 0, +	.window_start = 0, +	.window_len = 0x100, +}; + +static const struct regmap_config lt9211_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.rd_table = <9211_rw_table, +	.wr_table = <9211_rw_table, +	.volatile_table	= <9211_rw_table, +	.ranges = <9211_range, +	.num_ranges = 1, +	.cache_type = REGCACHE_RBTREE, +	.max_register = 0xda00, +}; + +static struct lt9211 *bridge_to_lt9211(struct drm_bridge *bridge) +{ +	return container_of(bridge, struct lt9211, bridge); +} + +static int lt9211_attach(struct drm_bridge *bridge, +			 enum drm_bridge_attach_flags flags) +{ +	struct lt9211 *ctx = bridge_to_lt9211(bridge); + +	return drm_bridge_attach(bridge->encoder, ctx->panel_bridge, +				 &ctx->bridge, flags); +} + +static int lt9211_read_chipid(struct lt9211 *ctx) +{ +	u8 chipid[3]; +	int ret; + +	/* Read Chip ID registers and verify the chip can communicate. */ +	ret = regmap_bulk_read(ctx->regmap, REG_CHIPID0, chipid, 3); +	if (ret < 0) { +		dev_err(ctx->dev, "Failed to read Chip ID: %d\n", ret); +		return ret; +	} + +	/* Test for known Chip ID. */ +	if (chipid[0] != REG_CHIPID0_VALUE || chipid[1] != REG_CHIPID1_VALUE || +	    chipid[2] != REG_CHIPID2_VALUE) { +		dev_err(ctx->dev, "Unknown Chip ID: 0x%02x 0x%02x 0x%02x\n", +			chipid[0], chipid[1], chipid[2]); +		return -EINVAL; +	} + +	return 0; +} + +static int lt9211_system_init(struct lt9211 *ctx) +{ +	const struct reg_sequence lt9211_system_init_seq[] = { +		{ 0x8201, 0x18 }, +		{ 0x8606, 0x61 }, +		{ 0x8607, 0xa8 }, +		{ 0x8714, 0x08 }, +		{ 0x8715, 0x00 }, +		{ 0x8718, 0x0f }, +		{ 0x8722, 0x08 }, +		{ 0x8723, 0x00 }, +		{ 0x8726, 0x0f }, +		{ 0x810b, 0xfe }, +	}; + +	return regmap_multi_reg_write(ctx->regmap, lt9211_system_init_seq, +				      ARRAY_SIZE(lt9211_system_init_seq)); +} + +static int lt9211_configure_rx(struct lt9211 *ctx) +{ +	const struct reg_sequence lt9211_rx_phy_seq[] = { +		{ 0x8202, 0x44 }, +		{ 0x8204, 0xa0 }, +		{ 0x8205, 0x22 }, +		{ 0x8207, 0x9f }, +		{ 0x8208, 0xfc }, +		/* ORR with 0xf8 here to enable DSI DN/DP swap. */ +		{ 0x8209, 0x01 }, +		{ 0x8217, 0x0c }, +		{ 0x8633, 0x1b }, +	}; + +	const struct reg_sequence lt9211_rx_cal_reset_seq[] = { +		{ 0x8120, 0x7f }, +		{ 0x8120, 0xff }, +	}; + +	const struct reg_sequence lt9211_rx_dig_seq[] = { +		{ 0x8630, 0x85 }, +		/* 0x8588: BIT 6 set = MIPI-RX, BIT 4 unset = LVDS-TX */ +		{ 0x8588, 0x40 }, +		{ 0x85ff, 0xd0 }, +		{ REG_DSI_LANE, REG_DSI_LANE_COUNT(ctx->dsi->lanes) }, +		{ 0xd002, 0x05 }, +	}; + +	const struct reg_sequence lt9211_rx_div_reset_seq[] = { +		{ 0x810a, 0xc0 }, +		{ 0x8120, 0xbf }, +	}; + +	const struct reg_sequence lt9211_rx_div_clear_seq[] = { +		{ 0x810a, 0xc1 }, +		{ 0x8120, 0xff }, +	}; + +	int ret; + +	ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_phy_seq, +				     ARRAY_SIZE(lt9211_rx_phy_seq)); +	if (ret) +		return ret; + +	ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_cal_reset_seq, +				     ARRAY_SIZE(lt9211_rx_cal_reset_seq)); +	if (ret) +		return ret; + +	ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_dig_seq, +				     ARRAY_SIZE(lt9211_rx_dig_seq)); +	if (ret) +		return ret; + +	ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_div_reset_seq, +				     ARRAY_SIZE(lt9211_rx_div_reset_seq)); +	if (ret) +		return ret; + +	usleep_range(10000, 15000); + +	return regmap_multi_reg_write(ctx->regmap, lt9211_rx_div_clear_seq, +				      ARRAY_SIZE(lt9211_rx_div_clear_seq)); +} + +static int lt9211_autodetect_rx(struct lt9211 *ctx, +				const struct drm_display_mode *mode) +{ +	u16 width, height; +	u32 byteclk; +	u8 buf[5]; +	u8 format; +	u8 bc[3]; +	int ret; + +	/* Measure ByteClock frequency. */ +	ret = regmap_write(ctx->regmap, 0x8600, 0x01); +	if (ret) +		return ret; + +	/* Give the chip time to lock onto RX stream. */ +	msleep(100); + +	/* Read the ByteClock frequency from the chip. */ +	ret = regmap_bulk_read(ctx->regmap, 0x8608, bc, sizeof(bc)); +	if (ret) +		return ret; + +	/* RX ByteClock in kHz */ +	byteclk = ((bc[0] & 0xf) << 16) | (bc[1] << 8) | bc[2]; + +	/* Width/Height/Format Auto-detection */ +	ret = regmap_bulk_read(ctx->regmap, 0xd082, buf, sizeof(buf)); +	if (ret) +		return ret; + +	width = (buf[0] << 8) | buf[1]; +	height = (buf[3] << 8) | buf[4]; +	format = buf[2] & 0xf; + +	if (format == 0x3) {		/* YUV422 16bit */ +		width /= 2; +	} else if (format == 0xa) {	/* RGB888 24bit */ +		width /= 3; +	} else { +		dev_err(ctx->dev, "Unsupported DSI pixel format 0x%01x\n", +			format); +		return -EINVAL; +	} + +	if (width != mode->hdisplay) { +		dev_err(ctx->dev, +			"RX: Detected DSI width (%d) does not match mode hdisplay (%d)\n", +			width, mode->hdisplay); +		return -EINVAL; +	} + +	if (height != mode->vdisplay) { +		dev_err(ctx->dev, +			"RX: Detected DSI height (%d) does not match mode vdisplay (%d)\n", +			height, mode->vdisplay); +		return -EINVAL; +	} + +	dev_dbg(ctx->dev, "RX: %dx%d format=0x%01x byteclock=%d kHz\n", +		width, height, format, byteclk); + +	return 0; +} + +static int lt9211_configure_timing(struct lt9211 *ctx, +				   const struct drm_display_mode *mode) +{ +	const struct reg_sequence lt9211_timing[] = { +		{ 0xd00d, (mode->vtotal >> 8) & 0xff }, +		{ 0xd00e, mode->vtotal & 0xff }, +		{ 0xd00f, (mode->vdisplay >> 8) & 0xff }, +		{ 0xd010, mode->vdisplay & 0xff }, +		{ 0xd011, (mode->htotal >> 8) & 0xff }, +		{ 0xd012, mode->htotal & 0xff }, +		{ 0xd013, (mode->hdisplay >> 8) & 0xff }, +		{ 0xd014, mode->hdisplay & 0xff }, +		{ 0xd015, (mode->vsync_end - mode->vsync_start) & 0xff }, +		{ 0xd016, (mode->hsync_end - mode->hsync_start) & 0xff }, +		{ 0xd017, ((mode->vsync_start - mode->vdisplay) >> 8) & 0xff }, +		{ 0xd018, (mode->vsync_start - mode->vdisplay) & 0xff }, +		{ 0xd019, ((mode->hsync_start - mode->hdisplay) >> 8) & 0xff }, +		{ 0xd01a, (mode->hsync_start - mode->hdisplay) & 0xff }, +	}; + +	return regmap_multi_reg_write(ctx->regmap, lt9211_timing, +				      ARRAY_SIZE(lt9211_timing)); +} + +static int lt9211_configure_plls(struct lt9211 *ctx, +				 const struct drm_display_mode *mode) +{ +	const struct reg_sequence lt9211_pcr_seq[] = { +		{ 0xd026, 0x17 }, +		{ 0xd027, 0xc3 }, +		{ 0xd02d, 0x30 }, +		{ 0xd031, 0x10 }, +		{ 0xd023, 0x20 }, +		{ 0xd038, 0x02 }, +		{ 0xd039, 0x10 }, +		{ 0xd03a, 0x20 }, +		{ 0xd03b, 0x60 }, +		{ 0xd03f, 0x04 }, +		{ 0xd040, 0x08 }, +		{ 0xd041, 0x10 }, +		{ 0x810b, 0xee }, +		{ 0x810b, 0xfe }, +	}; + +	unsigned int pval; +	int ret; + +	/* DeSSC PLL reference clock is 25 MHz XTal. */ +	ret = regmap_write(ctx->regmap, 0x822d, 0x48); +	if (ret) +		return ret; + +	if (mode->clock < 44000) { +		ret = regmap_write(ctx->regmap, 0x8235, 0x83); +	} else if (mode->clock < 88000) { +		ret = regmap_write(ctx->regmap, 0x8235, 0x82); +	} else if (mode->clock < 176000) { +		ret = regmap_write(ctx->regmap, 0x8235, 0x81); +	} else { +		dev_err(ctx->dev, +			"Unsupported mode clock (%d kHz) above 176 MHz.\n", +			mode->clock); +		return -EINVAL; +	} + +	if (ret) +		return ret; + +	/* Wait for the DeSSC PLL to stabilize. */ +	msleep(100); + +	ret = regmap_multi_reg_write(ctx->regmap, lt9211_pcr_seq, +				     ARRAY_SIZE(lt9211_pcr_seq)); +	if (ret) +		return ret; + +	/* PCR stability test takes seconds. */ +	ret = regmap_read_poll_timeout(ctx->regmap, 0xd087, pval, pval & 0x8, +				       20000, 10000000); +	if (ret) +		dev_err(ctx->dev, "PCR unstable, ret=%i\n", ret); + +	return ret; +} + +static int lt9211_configure_tx(struct lt9211 *ctx, bool jeida, +			       bool bpp24, bool de) +{ +	const struct reg_sequence system_lt9211_tx_phy_seq[] = { +		/* DPI output disable */ +		{ 0x8262, 0x00 }, +		/* BIT(7) is LVDS dual-port */ +		{ 0x823b, 0x38 | (ctx->lvds_dual_link ? BIT(7) : 0) }, +		{ 0x823e, 0x92 }, +		{ 0x823f, 0x48 }, +		{ 0x8240, 0x31 }, +		{ 0x8243, 0x80 }, +		{ 0x8244, 0x00 }, +		{ 0x8245, 0x00 }, +		{ 0x8249, 0x00 }, +		{ 0x824a, 0x01 }, +		{ 0x824e, 0x00 }, +		{ 0x824f, 0x00 }, +		{ 0x8250, 0x00 }, +		{ 0x8253, 0x00 }, +		{ 0x8254, 0x01 }, +		/* LVDS channel order, Odd:Even 0x10..A:B, 0x40..B:A */ +		{ 0x8646, ctx->lvds_dual_link_even_odd_swap ? 0x40 : 0x10 }, +		{ 0x8120, 0x7b }, +		{ 0x816b, 0xff }, +	}; + +	const struct reg_sequence system_lt9211_tx_dig_seq[] = { +		{ 0x8559, 0x40 | (jeida ? BIT(7) : 0) | +			  (de ? BIT(5) : 0) | (bpp24 ? BIT(4) : 0) }, +		{ 0x855a, 0xaa }, +		{ 0x855b, 0xaa }, +		{ 0x855c, ctx->lvds_dual_link ? BIT(0) : 0 }, +		{ 0x85a1, 0x77 }, +		{ 0x8640, 0x40 }, +		{ 0x8641, 0x34 }, +		{ 0x8642, 0x10 }, +		{ 0x8643, 0x23 }, +		{ 0x8644, 0x41 }, +		{ 0x8645, 0x02 }, +	}; + +	const struct reg_sequence system_lt9211_tx_pll_seq[] = { +		/* TX PLL power down */ +		{ 0x8236, 0x01 }, +		{ 0x8237, ctx->lvds_dual_link ? 0x2a : 0x29 }, +		{ 0x8238, 0x06 }, +		{ 0x8239, 0x30 }, +		{ 0x823a, 0x8e }, +		{ 0x8737, 0x14 }, +		{ 0x8713, 0x00 }, +		{ 0x8713, 0x80 }, +	}; + +	unsigned int pval; +	int ret; + +	ret = regmap_multi_reg_write(ctx->regmap, system_lt9211_tx_phy_seq, +				     ARRAY_SIZE(system_lt9211_tx_phy_seq)); +	if (ret) +		return ret; + +	ret = regmap_multi_reg_write(ctx->regmap, system_lt9211_tx_dig_seq, +				     ARRAY_SIZE(system_lt9211_tx_dig_seq)); +	if (ret) +		return ret; + +	ret = regmap_multi_reg_write(ctx->regmap, system_lt9211_tx_pll_seq, +				     ARRAY_SIZE(system_lt9211_tx_pll_seq)); +	if (ret) +		return ret; + +	ret = regmap_read_poll_timeout(ctx->regmap, 0x871f, pval, pval & 0x80, +				       10000, 1000000); +	if (ret) { +		dev_err(ctx->dev, "TX PLL unstable, ret=%i\n", ret); +		return ret; +	} + +	ret = regmap_read_poll_timeout(ctx->regmap, 0x8720, pval, pval & 0x80, +				       10000, 1000000); +	if (ret) { +		dev_err(ctx->dev, "TX PLL unstable, ret=%i\n", ret); +		return ret; +	} + +	return 0; +} + +static void lt9211_atomic_enable(struct drm_bridge *bridge, +				 struct drm_bridge_state *old_bridge_state) +{ +	struct lt9211 *ctx = bridge_to_lt9211(bridge); +	struct drm_atomic_state *state = old_bridge_state->base.state; +	const struct drm_bridge_state *bridge_state; +	const struct drm_crtc_state *crtc_state; +	const struct drm_display_mode *mode; +	struct drm_connector *connector; +	struct drm_crtc *crtc; +	bool lvds_format_24bpp; +	bool lvds_format_jeida; +	u32 bus_flags; +	int ret; + +	ret = regulator_enable(ctx->vccio); +	if (ret) { +		dev_err(ctx->dev, "Failed to enable vccio: %d\n", ret); +		return; +	} + +	/* Deassert reset */ +	gpiod_set_value(ctx->reset_gpio, 1); +	usleep_range(20000, 21000);	/* Very long post-reset delay. */ + +	/* Get the LVDS format from the bridge state. */ +	bridge_state = drm_atomic_get_new_bridge_state(state, bridge); +	bus_flags = bridge_state->output_bus_cfg.flags; + +	switch (bridge_state->output_bus_cfg.format) { +	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: +		lvds_format_24bpp = false; +		lvds_format_jeida = true; +		break; +	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: +		lvds_format_24bpp = true; +		lvds_format_jeida = true; +		break; +	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: +		lvds_format_24bpp = true; +		lvds_format_jeida = false; +		break; +	default: +		/* +		 * Some bridges still don't set the correct +		 * LVDS bus pixel format, use SPWG24 default +		 * format until those are fixed. +		 */ +		lvds_format_24bpp = true; +		lvds_format_jeida = false; +		dev_warn(ctx->dev, +			 "Unsupported LVDS bus format 0x%04x, please check output bridge driver. Falling back to SPWG24.\n", +			 bridge_state->output_bus_cfg.format); +		break; +	} + +	/* +	 * Retrieve the CRTC adjusted mode. This requires a little dance to go +	 * from the bridge to the encoder, to the connector and to the CRTC. +	 */ +	connector = drm_atomic_get_new_connector_for_encoder(state, +							     bridge->encoder); +	crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; +	crtc_state = drm_atomic_get_new_crtc_state(state, crtc); +	mode = &crtc_state->adjusted_mode; + +	ret = lt9211_read_chipid(ctx); +	if (ret) +		return; + +	ret = lt9211_system_init(ctx); +	if (ret) +		return; + +	ret = lt9211_configure_rx(ctx); +	if (ret) +		return; + +	ret = lt9211_autodetect_rx(ctx, mode); +	if (ret) +		return; + +	ret = lt9211_configure_timing(ctx, mode); +	if (ret) +		return; + +	ret = lt9211_configure_plls(ctx, mode); +	if (ret) +		return; + +	ret = lt9211_configure_tx(ctx, lvds_format_jeida, lvds_format_24bpp, +				  bus_flags & DRM_BUS_FLAG_DE_HIGH); +	if (ret) +		return; + +	dev_dbg(ctx->dev, "LT9211 enabled.\n"); +} + +static void lt9211_atomic_disable(struct drm_bridge *bridge, +				  struct drm_bridge_state *old_bridge_state) +{ +	struct lt9211 *ctx = bridge_to_lt9211(bridge); +	int ret; + +	/* +	 * Put the chip in reset, pull nRST line low, +	 * and assure lengthy 10ms reset low timing. +	 */ +	gpiod_set_value(ctx->reset_gpio, 0); +	usleep_range(10000, 11000);	/* Very long reset duration. */ + +	ret = regulator_disable(ctx->vccio); +	if (ret) +		dev_err(ctx->dev, "Failed to disable vccio: %d\n", ret); + +	regcache_mark_dirty(ctx->regmap); +} + +static enum drm_mode_status +lt9211_mode_valid(struct drm_bridge *bridge, +		  const struct drm_display_info *info, +		  const struct drm_display_mode *mode) +{ +	/* LVDS output clock range 25..176 MHz */ +	if (mode->clock < 25000) +		return MODE_CLOCK_LOW; +	if (mode->clock > 176000) +		return MODE_CLOCK_HIGH; + +	return MODE_OK; +} + +#define MAX_INPUT_SEL_FORMATS	1 + +static u32 * +lt9211_atomic_get_input_bus_fmts(struct drm_bridge *bridge, +				 struct drm_bridge_state *bridge_state, +				 struct drm_crtc_state *crtc_state, +				 struct drm_connector_state *conn_state, +				 u32 output_fmt, +				 unsigned int *num_input_fmts) +{ +	u32 *input_fmts; + +	*num_input_fmts = 0; + +	input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), +			     GFP_KERNEL); +	if (!input_fmts) +		return NULL; + +	/* This is the DSI-end bus format */ +	input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; +	*num_input_fmts = 1; + +	return input_fmts; +} + +static const struct drm_bridge_funcs lt9211_funcs = { +	.attach			= lt9211_attach, +	.mode_valid		= lt9211_mode_valid, +	.atomic_enable		= lt9211_atomic_enable, +	.atomic_disable		= lt9211_atomic_disable, +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, +	.atomic_destroy_state	= drm_atomic_helper_bridge_destroy_state, +	.atomic_get_input_bus_fmts = lt9211_atomic_get_input_bus_fmts, +	.atomic_reset		= drm_atomic_helper_bridge_reset, +}; + +static int lt9211_parse_dt(struct lt9211 *ctx) +{ +	struct device_node *port2, *port3; +	struct drm_bridge *panel_bridge; +	struct device *dev = ctx->dev; +	struct drm_panel *panel; +	int dual_link; +	int ret; + +	ctx->vccio = devm_regulator_get(dev, "vccio"); +	if (IS_ERR(ctx->vccio)) +		return dev_err_probe(dev, PTR_ERR(ctx->vccio), +				     "Failed to get supply 'vccio'\n"); + +	ctx->lvds_dual_link = false; +	ctx->lvds_dual_link_even_odd_swap = false; + +	port2 = of_graph_get_port_by_id(dev->of_node, 2); +	port3 = of_graph_get_port_by_id(dev->of_node, 3); +	dual_link = drm_of_lvds_get_dual_link_pixel_order(port2, port3); +	of_node_put(port2); +	of_node_put(port3); + +	if (dual_link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS) { +		ctx->lvds_dual_link = true; +		/* Odd pixels to LVDS Channel A, even pixels to B */ +		ctx->lvds_dual_link_even_odd_swap = false; +	} else if (dual_link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS) { +		ctx->lvds_dual_link = true; +		/* Even pixels to LVDS Channel A, odd pixels to B */ +		ctx->lvds_dual_link_even_odd_swap = true; +	} + +	ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, &panel_bridge); +	if (ret < 0) +		return ret; +	if (panel) { +		panel_bridge = devm_drm_panel_bridge_add(dev, panel); +		if (IS_ERR(panel_bridge)) +			return PTR_ERR(panel_bridge); +	} + +	ctx->panel_bridge = panel_bridge; + +	return 0; +} + +static int lt9211_host_attach(struct lt9211 *ctx) +{ +	const struct mipi_dsi_device_info info = { +		.type = "lt9211", +		.channel = 0, +		.node = NULL, +	}; +	struct device *dev = ctx->dev; +	struct device_node *host_node; +	struct device_node *endpoint; +	struct mipi_dsi_device *dsi; +	struct mipi_dsi_host *host; +	int dsi_lanes; +	int ret; + +	endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); +	dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); +	host_node = of_graph_get_remote_port_parent(endpoint); +	host = of_find_mipi_dsi_host_by_node(host_node); +	of_node_put(host_node); +	of_node_put(endpoint); + +	if (!host) +		return -EPROBE_DEFER; + +	if (dsi_lanes < 0 || dsi_lanes > 4) +		return -EINVAL; + +	dsi = devm_mipi_dsi_device_register_full(dev, host, &info); +	if (IS_ERR(dsi)) +		return dev_err_probe(dev, PTR_ERR(dsi), +				     "failed to create dsi device\n"); + +	ctx->dsi = dsi; + +	dsi->lanes = dsi_lanes; +	dsi->format = MIPI_DSI_FMT_RGB888; +	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +			  MIPI_DSI_MODE_VIDEO_HSE; + +	ret = devm_mipi_dsi_attach(dev, dsi); +	if (ret < 0) { +		dev_err(dev, "failed to attach dsi to host: %d\n", ret); +		return ret; +	} + +	return 0; +} + +static int lt9211_probe(struct i2c_client *client, +			const struct i2c_device_id *id) +{ +	struct device *dev = &client->dev; +	struct lt9211 *ctx; +	int ret; + +	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); +	if (!ctx) +		return -ENOMEM; + +	ctx->dev = dev; + +	/* +	 * Put the chip in reset, pull nRST line low, +	 * and assure lengthy 10ms reset low timing. +	 */ +	ctx->reset_gpio = devm_gpiod_get_optional(ctx->dev, "reset", +						  GPIOD_OUT_LOW); +	if (IS_ERR(ctx->reset_gpio)) +		return PTR_ERR(ctx->reset_gpio); + +	usleep_range(10000, 11000);	/* Very long reset duration. */ + +	ret = lt9211_parse_dt(ctx); +	if (ret) +		return ret; + +	ctx->regmap = devm_regmap_init_i2c(client, <9211_regmap_config); +	if (IS_ERR(ctx->regmap)) +		return PTR_ERR(ctx->regmap); + +	dev_set_drvdata(dev, ctx); +	i2c_set_clientdata(client, ctx); + +	ctx->bridge.funcs = <9211_funcs; +	ctx->bridge.of_node = dev->of_node; +	drm_bridge_add(&ctx->bridge); + +	ret = lt9211_host_attach(ctx); +	if (ret) +		drm_bridge_remove(&ctx->bridge); + +	return ret; +} + +static int lt9211_remove(struct i2c_client *client) +{ +	struct lt9211 *ctx = i2c_get_clientdata(client); + +	drm_bridge_remove(&ctx->bridge); + +	return 0; +} + +static struct i2c_device_id lt9211_id[] = { +	{ "lontium,lt9211" }, +	{}, +}; +MODULE_DEVICE_TABLE(i2c, lt9211_id); + +static const struct of_device_id lt9211_match_table[] = { +	{ .compatible = "lontium,lt9211" }, +	{}, +}; +MODULE_DEVICE_TABLE(of, lt9211_match_table); + +static struct i2c_driver lt9211_driver = { +	.probe = lt9211_probe, +	.remove = lt9211_remove, +	.id_table = lt9211_id, +	.driver = { +		.name = "lt9211", +		.of_match_table = lt9211_match_table, +	}, +}; +module_i2c_driver(lt9211_driver); + +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); +MODULE_DESCRIPTION("Lontium LT9211 DSI/LVDS/DPI bridge driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index ff1c37b2e6e5..0ee563eb2b6f 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -83,8 +83,11 @@ static int panel_bridge_attach(struct drm_bridge *bridge,  	drm_connector_attach_encoder(&panel_bridge->connector,  					  bridge->encoder); -	if (connector->funcs->reset) -		connector->funcs->reset(connector); +	if (bridge->dev->registered) { +		if (connector->funcs->reset) +			connector->funcs->reset(connector); +		drm_connector_register(connector); +	}  	return 0;  } diff --git a/drivers/gpu/drm/bridge/synopsys/Kconfig b/drivers/gpu/drm/bridge/synopsys/Kconfig index 21a1be3ced0f..a4a31b669b65 100644 --- a/drivers/gpu/drm/bridge/synopsys/Kconfig +++ b/drivers/gpu/drm/bridge/synopsys/Kconfig @@ -25,6 +25,16 @@ config DRM_DW_HDMI_I2S_AUDIO  	  Support the I2S Audio interface which is part of the Synopsys  	  Designware HDMI block. +config DRM_DW_HDMI_GP_AUDIO +	tristate "Synopsys Designware GP Audio interface" +	depends on DRM_DW_HDMI && SND +	select SND_PCM +	select SND_PCM_ELD +	select SND_PCM_IEC958 +	help +	  Support the GP Audio interface which is part of the Synopsys +	  Designware HDMI block. +  config DRM_DW_HDMI_CEC  	tristate "Synopsis Designware CEC interface"  	depends on DRM_DW_HDMI diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile index 91d746ad5de1..ce715562e9e5 100644 --- a/drivers/gpu/drm/bridge/synopsys/Makefile +++ b/drivers/gpu/drm/bridge/synopsys/Makefile @@ -1,6 +1,7 @@  # SPDX-License-Identifier: GPL-2.0-only  obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o  obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o +obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o  obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o  obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c new file mode 100644 index 000000000000..11ea1c84eb35 --- /dev/null +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * dw-hdmi-gp-audio.c + * + * Copyright 2020-2022 NXP + */ +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <drm/bridge/dw_hdmi.h> +#include <drm/drm_edid.h> +#include <drm/drm_connector.h> + +#include <sound/hdmi-codec.h> +#include <sound/asoundef.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_drm_eld.h> +#include <sound/pcm_iec958.h> +#include <sound/dmaengine_pcm.h> + +#include "dw-hdmi-audio.h" + +#define DRIVER_NAME "dw-hdmi-gp-audio" +#define DRV_NAME    "hdmi-gp-audio" + +struct snd_dw_hdmi { +	struct dw_hdmi_audio_data data; +	struct platform_device  *audio_pdev; +	unsigned int pos; +}; + +struct dw_hdmi_channel_conf { +	u8 conf1; +	u8 ca; +}; + +/* + * The default mapping of ALSA channels to HDMI channels and speaker + * allocation bits.  Note that we can't do channel remapping here - + * channels must be in the same order. + * + * Mappings for alsa-lib pcm/surround*.conf files: + * + *		Front	Sur4.0	Sur4.1	Sur5.0	Sur5.1	Sur7.1 + * Channels	2	4	6	6	6	8 + * + * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel: + * + *				Number of ALSA channels + * ALSA Channel	2	3	4	5	6	7	8 + * 0		FL:0	=	=	=	=	=	= + * 1		FR:1	=	=	=	=	=	= + * 2			FC:3	RL:4	LFE:2	=	=	= + * 3				RR:5	RL:4	FC:3	=	= + * 4					RR:5	RL:4	=	= + * 5						RR:5	=	= + * 6							RC:6	= + * 7							RLC/FRC	RLC/FRC + */ +static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = { +	{ 0x03, 0x00 },	/* FL,FR */ +	{ 0x0b, 0x02 },	/* FL,FR,FC */ +	{ 0x33, 0x08 },	/* FL,FR,RL,RR */ +	{ 0x37, 0x09 },	/* FL,FR,LFE,RL,RR */ +	{ 0x3f, 0x0b },	/* FL,FR,LFE,FC,RL,RR */ +	{ 0x7f, 0x0f },	/* FL,FR,LFE,FC,RL,RR,RC */ +	{ 0xff, 0x13 },	/* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */ +}; + +static int audio_hw_params(struct device *dev,  void *data, +			   struct hdmi_codec_daifmt *daifmt, +			   struct hdmi_codec_params *params) +{ +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev); +	int ret = 0; +	u8 ca; + +	dw_hdmi_set_sample_rate(dw->data.hdmi, params->sample_rate); + +	ca = default_hdmi_channel_config[params->channels - 2].ca; + +	dw_hdmi_set_channel_count(dw->data.hdmi, params->channels); +	dw_hdmi_set_channel_allocation(dw->data.hdmi, ca); + +	dw_hdmi_set_sample_non_pcm(dw->data.hdmi, +				   params->iec.status[0] & IEC958_AES0_NONAUDIO); +	dw_hdmi_set_sample_width(dw->data.hdmi, params->sample_width); + +	return ret; +} + +static void audio_shutdown(struct device *dev, void *data) +{ +} + +static int audio_mute_stream(struct device *dev, void *data, +			     bool enable, int direction) +{ +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev); +	int ret = 0; + +	if (!enable) +		dw_hdmi_audio_enable(dw->data.hdmi); +	else +		dw_hdmi_audio_disable(dw->data.hdmi); + +	return ret; +} + +static int audio_get_eld(struct device *dev, void *data, +			 u8 *buf, size_t len) +{ +	struct dw_hdmi_audio_data *audio = data; +	u8 *eld; + +	eld = audio->get_eld(audio->hdmi); +	if (eld) +		memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len)); +	else +		/* Pass en empty ELD if connector not available */ +		memset(buf, 0, len); + +	return 0; +} + +static int audio_hook_plugged_cb(struct device *dev, void *data, +				 hdmi_codec_plugged_cb fn, +				 struct device *codec_dev) +{ +	struct snd_dw_hdmi *dw = dev_get_drvdata(dev); + +	return dw_hdmi_set_plugged_cb(dw->data.hdmi, fn, codec_dev); +} + +static const struct hdmi_codec_ops audio_codec_ops = { +	.hw_params = audio_hw_params, +	.audio_shutdown = audio_shutdown, +	.mute_stream = audio_mute_stream, +	.get_eld = audio_get_eld, +	.hook_plugged_cb = audio_hook_plugged_cb, +}; + +static int snd_dw_hdmi_probe(struct platform_device *pdev) +{ +	struct dw_hdmi_audio_data *data = pdev->dev.platform_data; +	struct snd_dw_hdmi *dw; + +	const struct hdmi_codec_pdata codec_data = { +		.i2s = 1, +		.spdif = 0, +		.ops = &audio_codec_ops, +		.max_i2s_channels = 8, +		.data = data, +	}; + +	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL); +	if (!dw) +		return -ENOMEM; + +	dw->data = *data; + +	platform_set_drvdata(pdev, dw); + +	dw->audio_pdev = platform_device_register_data(&pdev->dev, +						       HDMI_CODEC_DRV_NAME, 1, +						       &codec_data, +						       sizeof(codec_data)); + +	return PTR_ERR_OR_ZERO(dw->audio_pdev); +} + +static int snd_dw_hdmi_remove(struct platform_device *pdev) +{ +	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev); + +	platform_device_unregister(dw->audio_pdev); + +	return 0; +} + +static struct platform_driver snd_dw_hdmi_driver = { +	.probe	= snd_dw_hdmi_probe, +	.remove	= snd_dw_hdmi_remove, +	.driver	= { +		.name = DRIVER_NAME, +	}, +}; + +module_platform_driver(snd_dw_hdmi_driver); + +MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>"); +MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index a563460f8d20..f3f82a04c252 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -191,7 +191,10 @@ struct dw_hdmi {  	spinlock_t audio_lock;  	struct mutex audio_mutex; +	unsigned int sample_non_pcm; +	unsigned int sample_width;  	unsigned int sample_rate; +	unsigned int channels;  	unsigned int audio_cts;  	unsigned int audio_n;  	bool audio_enable; @@ -589,6 +592,8 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)  			n = 4096;  		else if (pixel_clk == 74176000 || pixel_clk == 148352000)  			n = 11648; +		else if (pixel_clk == 297000000) +			n = 3072;  		else  			n = 4096;  		n *= mult; @@ -601,6 +606,8 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)  			n = 17836;  		else if (pixel_clk == 148352000)  			n = 8918; +		else if (pixel_clk == 297000000) +			n = 4704;  		else  			n = 6272;  		n *= mult; @@ -615,6 +622,8 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)  			n = 11648;  		else if (pixel_clk == 148352000)  			n = 5824; +		else if (pixel_clk == 297000000) +			n = 5120;  		else  			n = 6144;  		n *= mult; @@ -659,8 +668,8 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,  	config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID); -	/* Only compute CTS when using internal AHB audio */ -	if (config3 & HDMI_CONFIG3_AHBAUDDMA) { +	/* Compute CTS when using internal AHB audio or General Parallel audio*/ +	if ((config3 & HDMI_CONFIG3_AHBAUDDMA) || (config3 & HDMI_CONFIG3_GPAUD)) {  		/*  		 * Compute the CTS value from the N value.  Note that CTS and N  		 * can be up to 20 bits in total, so we need 64-bit math.  Also @@ -702,6 +711,22 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)  	mutex_unlock(&hdmi->audio_mutex);  } +void dw_hdmi_set_sample_width(struct dw_hdmi *hdmi, unsigned int width) +{ +	mutex_lock(&hdmi->audio_mutex); +	hdmi->sample_width = width; +	mutex_unlock(&hdmi->audio_mutex); +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_width); + +void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm) +{ +	mutex_lock(&hdmi->audio_mutex); +	hdmi->sample_non_pcm = non_pcm; +	mutex_unlock(&hdmi->audio_mutex); +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_non_pcm); +  void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)  {  	mutex_lock(&hdmi->audio_mutex); @@ -717,6 +742,7 @@ void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt)  	u8 layout;  	mutex_lock(&hdmi->audio_mutex); +	hdmi->channels = cnt;  	/*  	 * For >2 channel PCM audio, we need to select layout 1 @@ -765,6 +791,89 @@ static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi)  	return hdmi->curr_conn->eld;  } +static void dw_hdmi_gp_audio_enable(struct dw_hdmi *hdmi) +{ +	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; +	int sample_freq = 0x2, org_sample_freq = 0xD; +	int ch_mask = BIT(hdmi->channels) - 1; + +	switch (hdmi->sample_rate) { +	case 32000: +		sample_freq = 0x03; +		org_sample_freq = 0x0C; +		break; +	case 44100: +		sample_freq = 0x00; +		org_sample_freq = 0x0F; +		break; +	case 48000: +		sample_freq = 0x02; +		org_sample_freq = 0x0D; +		break; +	case 88200: +		sample_freq = 0x08; +		org_sample_freq = 0x07; +		break; +	case 96000: +		sample_freq = 0x0A; +		org_sample_freq = 0x05; +		break; +	case 176400: +		sample_freq = 0x0C; +		org_sample_freq = 0x03; +		break; +	case 192000: +		sample_freq = 0x0E; +		org_sample_freq = 0x01; +		break; +	default: +		break; +	} + +	hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); +	hdmi_enable_audio_clk(hdmi, true); + +	hdmi_writeb(hdmi, 0x1, HDMI_FC_AUDSCHNLS0); +	hdmi_writeb(hdmi, hdmi->channels, HDMI_FC_AUDSCHNLS2); +	hdmi_writeb(hdmi, 0x22, HDMI_FC_AUDSCHNLS3); +	hdmi_writeb(hdmi, 0x22, HDMI_FC_AUDSCHNLS4); +	hdmi_writeb(hdmi, 0x11, HDMI_FC_AUDSCHNLS5); +	hdmi_writeb(hdmi, 0x11, HDMI_FC_AUDSCHNLS6); +	hdmi_writeb(hdmi, (0x3 << 4) | sample_freq, HDMI_FC_AUDSCHNLS7); +	hdmi_writeb(hdmi, (org_sample_freq << 4) | 0xb, HDMI_FC_AUDSCHNLS8); + +	hdmi_writeb(hdmi, ch_mask, HDMI_GP_CONF1); +	hdmi_writeb(hdmi, 0x02, HDMI_GP_CONF2); +	hdmi_writeb(hdmi, 0x01, HDMI_GP_CONF0); + +	hdmi_modb(hdmi,  0x3, 0x3, HDMI_FC_DATAUTO3); + +	/* hbr */ +	if (hdmi->sample_rate == 192000 && hdmi->channels == 8 && +	    hdmi->sample_width == 32 && hdmi->sample_non_pcm) +		hdmi_modb(hdmi, 0x01, 0x01, HDMI_GP_CONF2); + +	if (pdata->enable_audio) +		pdata->enable_audio(hdmi, +					    hdmi->channels, +					    hdmi->sample_width, +					    hdmi->sample_rate, +					    hdmi->sample_non_pcm); +} + +static void dw_hdmi_gp_audio_disable(struct dw_hdmi *hdmi) +{ +	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; + +	hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0); + +	hdmi_modb(hdmi,  0, 0x3, HDMI_FC_DATAUTO3); +	if (pdata->disable_audio) +		pdata->disable_audio(hdmi); + +	hdmi_enable_audio_clk(hdmi, false); +} +  static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)  {  	hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); @@ -1108,6 +1217,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)  	unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;  	struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;  	u8 val, vp_conf; +	u8 clear_gcp_auto = 0; +  	if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||  	    hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) || @@ -1117,6 +1228,7 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)  		case 8:  			color_depth = 4;  			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; +			clear_gcp_auto = 1;  			break;  		case 10:  			color_depth = 5; @@ -1136,6 +1248,7 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)  		case 0:  		case 8:  			remap_size = HDMI_VP_REMAP_YCC422_16bit; +			clear_gcp_auto = 1;  			break;  		case 10:  			remap_size = HDMI_VP_REMAP_YCC422_20bit; @@ -1160,6 +1273,19 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)  		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);  	hdmi_writeb(hdmi, val, HDMI_VP_PR_CD); +	/* HDMI1.4b specification section 6.5.3: +	 * Source shall only send GCPs with non-zero CD to sinks +	 * that indicate support for Deep Color. +	 * GCP only transmit CD and do not handle AVMUTE, PP norDefault_Phase (yet). +	 * Disable Auto GCP when 24-bit color for sinks that not support Deep Color. +	 */ +	val = hdmi_readb(hdmi, HDMI_FC_DATAUTO3); +	if (clear_gcp_auto == 1) +		val &= ~HDMI_FC_DATAUTO3_GCP_AUTO; +	else +		val |= HDMI_FC_DATAUTO3_GCP_AUTO; +	hdmi_writeb(hdmi, val, HDMI_FC_DATAUTO3); +  	hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,  		  HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF); @@ -1357,13 +1483,21 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)  			 HDMI_PHY_CONF0_SELDIPIF_MASK);  } -void dw_hdmi_phy_reset(struct dw_hdmi *hdmi) +void dw_hdmi_phy_gen1_reset(struct dw_hdmi *hdmi) +{ +	/* PHY reset. The reset signal is active low on Gen1 PHYs. */ +	hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ); +	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ); +} +EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen1_reset); + +void dw_hdmi_phy_gen2_reset(struct dw_hdmi *hdmi)  {  	/* PHY reset. The reset signal is active high on Gen2 PHYs. */  	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);  	hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);  } -EXPORT_SYMBOL_GPL(dw_hdmi_phy_reset); +EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_reset);  void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address)  { @@ -1517,7 +1651,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi,  	if (phy->has_svsret)  		dw_hdmi_phy_enable_svsret(hdmi, 1); -	dw_hdmi_phy_reset(hdmi); +	dw_hdmi_phy_gen2_reset(hdmi);  	hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST); @@ -2086,30 +2220,21 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)  	 * then write one of the FC registers several times.  	 *  	 * The number of iterations matters and depends on the HDMI TX revision -	 * (and possibly on the platform). So far i.MX6Q (v1.30a), i.MX6DL -	 * (v1.31a) and multiple Allwinner SoCs (v1.32a) have been identified -	 * as needing the workaround, with 4 iterations for v1.30a and 1 -	 * iteration for others. -	 * The Amlogic Meson GX SoCs (v2.01a) have been identified as needing -	 * the workaround with a single iteration. -	 * The Rockchip RK3288 SoC (v2.00a) and RK3328/RK3399 SoCs (v2.11a) have -	 * been identified as needing the workaround with a single iteration. +	 * (and possibly on the platform). +	 * 4 iterations for i.MX6Q(v1.30a) and 1 iteration for others. +	 * i.MX6DL (v1.31a), Allwinner SoCs (v1.32a), Rockchip RK3288 SoC (v2.00a), +	 * Amlogic Meson GX SoCs (v2.01a), RK3328/RK3399 SoCs (v2.11a) +	 * and i.MX8MPlus (v2.13a) have been identified as needing the workaround +	 * with a single iteration.  	 */  	switch (hdmi->version) {  	case 0x130a:  		count = 4;  		break; -	case 0x131a: -	case 0x132a: -	case 0x200a: -	case 0x201a: -	case 0x211a: -	case 0x212a: +	default:  		count = 1;  		break; -	default: -		return;  	}  	/* TMDS software reset */ @@ -3242,6 +3367,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,  	hdmi->plat_data = plat_data;  	hdmi->dev = dev;  	hdmi->sample_rate = 48000; +	hdmi->channels = 2;  	hdmi->disabled = true;  	hdmi->rxsense = true;  	hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE); @@ -3465,6 +3591,24 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,  		pdevinfo.size_data = sizeof(audio);  		pdevinfo.dma_mask = DMA_BIT_MASK(32);  		hdmi->audio = platform_device_register_full(&pdevinfo); +	} else if (iores && config3 & HDMI_CONFIG3_GPAUD) { +		struct dw_hdmi_audio_data audio; + +		audio.phys = iores->start; +		audio.base = hdmi->regs; +		audio.irq = irq; +		audio.hdmi = hdmi; +		audio.get_eld = hdmi_audio_get_eld; + +		hdmi->enable_audio = dw_hdmi_gp_audio_enable; +		hdmi->disable_audio = dw_hdmi_gp_audio_disable; + +		pdevinfo.name = "dw-hdmi-gp-audio"; +		pdevinfo.id = PLATFORM_DEVID_NONE; +		pdevinfo.data = &audio; +		pdevinfo.size_data = sizeof(audio); +		pdevinfo.dma_mask = DMA_BIT_MASK(32); +		hdmi->audio = platform_device_register_full(&pdevinfo);  	}  	if (!plat_data->disable_cec && (config0 & HDMI_CONFIG0_CEC)) { diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index 1999db05bc3b..af43a0414b78 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h @@ -158,8 +158,17 @@  #define HDMI_FC_SPDDEVICEINF                    0x1062  #define HDMI_FC_AUDSCONF                        0x1063  #define HDMI_FC_AUDSSTAT                        0x1064 -#define HDMI_FC_AUDSCHNLS7                      0x106e -#define HDMI_FC_AUDSCHNLS8                      0x106f +#define HDMI_FC_AUDSV                           0x1065 +#define HDMI_FC_AUDSU                           0x1066 +#define HDMI_FC_AUDSCHNLS0                       0x1067 +#define HDMI_FC_AUDSCHNLS1                       0x1068 +#define HDMI_FC_AUDSCHNLS2                       0x1069 +#define HDMI_FC_AUDSCHNLS3                       0x106A +#define HDMI_FC_AUDSCHNLS4                       0x106B +#define HDMI_FC_AUDSCHNLS5                       0x106C +#define HDMI_FC_AUDSCHNLS6                       0x106D +#define HDMI_FC_AUDSCHNLS7                       0x106E +#define HDMI_FC_AUDSCHNLS8                       0x106F  #define HDMI_FC_DATACH0FILL                     0x1070  #define HDMI_FC_DATACH1FILL                     0x1071  #define HDMI_FC_DATACH2FILL                     0x1072 @@ -850,6 +859,9 @@ enum {  	HDMI_FC_DATAUTO0_VSD_MASK = 0x08,  	HDMI_FC_DATAUTO0_VSD_OFFSET = 3, +/* FC_DATAUTO3 field values */ +	HDMI_FC_DATAUTO3_GCP_AUTO = 0x04, +  /* PHY_CONF0 field values */  	HDMI_PHY_CONF0_PDZ_MASK = 0x80,  	HDMI_PHY_CONF0_PDZ_OFFSET = 7, diff --git a/drivers/gpu/drm/dp/drm_dp.c b/drivers/gpu/drm/dp/drm_dp.c index 580016a1b9eb..2a1f5ff6633c 100644 --- a/drivers/gpu/drm/dp/drm_dp.c +++ b/drivers/gpu/drm/dp/drm_dp.c @@ -528,6 +528,31 @@ unlock:  }  /** + * drm_dp_dpcd_probe() - probe a given DPCD address with a 1-byte read access + * @aux: DisplayPort AUX channel (SST) + * @offset: address of the register to probe + * + * Probe the provided DPCD address by reading 1 byte from it. The function can + * be used to trigger some side-effect the read access has, like waking up the + * sink, without the need for the read-out value. + * + * Returns 0 if the read access suceeded, or a negative error code on failure. + */ +int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset) +{ +	u8 buffer; +	int ret; + +	ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, &buffer, 1); +	WARN_ON(ret == 0); + +	drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, &buffer, ret); + +	return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL(drm_dp_dpcd_probe); + +/**   * drm_dp_dpcd_read() - read a series of bytes from the DPCD   * @aux: DisplayPort AUX channel (SST or MST)   * @offset: address of the (first) register to read @@ -559,10 +584,9 @@ ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,  	 * monitor doesn't power down exactly after the throw away read.  	 */  	if (!aux->is_remote) { -		ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV, -					 buffer, 1); -		if (ret != 1) -			goto out; +		ret = drm_dp_dpcd_probe(aux, DP_DPCD_REV); +		if (ret < 0) +			return ret;  	}  	if (aux->is_remote) @@ -571,7 +595,6 @@ ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,  		ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset,  					 buffer, size); -out:  	drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, buffer, ret);  	return ret;  } diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index 72f52f293249..11bb59399471 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -665,6 +665,9 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,  	if (start + size == end)  		return __drm_buddy_alloc_range(mm, start, size, blocks); +	if (!IS_ALIGNED(size, min_page_size)) +		return -EINVAL; +  	pages = size >> ilog2(mm->chunk_size);  	order = fls(pages) - 1;  	min_order = ilog2(min_page_size) - ilog2(mm->chunk_size); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index bff917531f33..b632825654a9 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -297,15 +297,15 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,  		return false;  	} -	saved_mode = crtc->mode; -	saved_hwmode = crtc->hwmode; +	drm_mode_init(&saved_mode, &crtc->mode); +	drm_mode_init(&saved_hwmode, &crtc->hwmode);  	saved_x = crtc->x;  	saved_y = crtc->y;  	/* Update crtc values up front so the driver can rely on them for mode  	 * setting.  	 */ -	crtc->mode = *mode; +	drm_mode_copy(&crtc->mode, mode);  	crtc->x = x;  	crtc->y = y; @@ -341,7 +341,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,  	}  	DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); -	crtc->hwmode = *adjusted_mode; +	drm_mode_copy(&crtc->hwmode, adjusted_mode);  	/* Prepare the encoders and CRTCs before setting the mode. */  	drm_for_each_encoder(encoder, dev) { @@ -411,8 +411,8 @@ done:  	drm_mode_destroy(dev, adjusted_mode);  	if (!ret) {  		crtc->enabled = saved_enabled; -		crtc->mode = saved_mode; -		crtc->hwmode = saved_hwmode; +		drm_mode_copy(&crtc->mode, &saved_mode); +		drm_mode_copy(&crtc->hwmode, &saved_hwmode);  		crtc->x = saved_x;  		crtc->y = saved_y;  	} diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 04e818ecd662..324ce8467915 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1568,6 +1568,38 @@ static const struct drm_display_mode edid_4k_modes[] = {  /*** DDC fetch and block validation ***/ +static int edid_extension_block_count(const struct edid *edid) +{ +	return edid->extensions; +} + +static int edid_block_count(const struct edid *edid) +{ +	return edid_extension_block_count(edid) + 1; +} + +static int edid_size_by_blocks(int num_blocks) +{ +	return num_blocks * EDID_LENGTH; +} + +static int edid_size(const struct edid *edid) +{ +	return edid_size_by_blocks(edid_block_count(edid)); +} + +static const void *edid_block_data(const struct edid *edid, int index) +{ +	BUILD_BUG_ON(sizeof(*edid) != EDID_LENGTH); + +	return edid + index; +} + +static const void *edid_extension_block_data(const struct edid *edid, int index) +{ +	return edid_block_data(edid, index + 1); +} +  static const u8 edid_header[] = {  	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00  }; @@ -1632,9 +1664,9 @@ static int edid_block_tag(const void *_block)  	return block[0];  } -static bool edid_is_zero(const void *edid, int length) +static bool edid_block_is_zero(const void *edid)  { -	return !memchr_inv(edid, 0, length); +	return !memchr_inv(edid, 0, EDID_LENGTH);  }  /** @@ -1654,8 +1686,8 @@ bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2)  		return false;  	if (edid1) { -		edid1_len = EDID_LENGTH * (1 + edid1->extensions); -		edid2_len = EDID_LENGTH * (1 + edid2->extensions); +		edid1_len = edid_size(edid1); +		edid2_len = edid_size(edid2);  		if (edid1_len != edid2_len)  			return false; @@ -1670,7 +1702,9 @@ EXPORT_SYMBOL(drm_edid_are_equal);  enum edid_block_status {  	EDID_BLOCK_OK = 0, +	EDID_BLOCK_READ_FAIL,  	EDID_BLOCK_NULL, +	EDID_BLOCK_ZERO,  	EDID_BLOCK_HEADER_CORRUPT,  	EDID_BLOCK_HEADER_REPAIR,  	EDID_BLOCK_HEADER_FIXED, @@ -1689,15 +1723,23 @@ static enum edid_block_status edid_block_check(const void *_block,  	if (is_base_block) {  		int score = drm_edid_header_is_valid(block); -		if (score < clamp(edid_fixup, 0, 8)) -			return EDID_BLOCK_HEADER_CORRUPT; +		if (score < clamp(edid_fixup, 0, 8)) { +			if (edid_block_is_zero(block)) +				return EDID_BLOCK_ZERO; +			else +				return EDID_BLOCK_HEADER_CORRUPT; +		}  		if (score < 8)  			return EDID_BLOCK_HEADER_REPAIR;  	} -	if (edid_block_compute_checksum(block) != edid_block_get_checksum(block)) -		return EDID_BLOCK_CHECKSUM; +	if (edid_block_compute_checksum(block) != edid_block_get_checksum(block)) { +		if (edid_block_is_zero(block)) +			return EDID_BLOCK_ZERO; +		else +			return EDID_BLOCK_CHECKSUM; +	}  	if (is_base_block) {  		if (block->version != 1) @@ -1720,6 +1762,70 @@ static bool edid_block_valid(const void *block, bool base)  				       edid_block_tag(block));  } +static void edid_block_status_print(enum edid_block_status status, +				    const struct edid *block, +				    int block_num) +{ +	switch (status) { +	case EDID_BLOCK_OK: +		break; +	case EDID_BLOCK_READ_FAIL: +		pr_debug("EDID block %d read failed\n", block_num); +		break; +	case EDID_BLOCK_NULL: +		pr_debug("EDID block %d pointer is NULL\n", block_num); +		break; +	case EDID_BLOCK_ZERO: +		pr_notice("EDID block %d is all zeroes\n", block_num); +		break; +	case EDID_BLOCK_HEADER_CORRUPT: +		pr_notice("EDID has corrupt header\n"); +		break; +	case EDID_BLOCK_HEADER_REPAIR: +		pr_debug("EDID corrupt header needs repair\n"); +		break; +	case EDID_BLOCK_HEADER_FIXED: +		pr_debug("EDID corrupt header fixed\n"); +		break; +	case EDID_BLOCK_CHECKSUM: +		if (edid_block_status_valid(status, edid_block_tag(block))) { +			pr_debug("EDID block %d (tag 0x%02x) checksum is invalid, remainder is %d, ignoring\n", +				 block_num, edid_block_tag(block), +				 edid_block_compute_checksum(block)); +		} else { +			pr_notice("EDID block %d (tag 0x%02x) checksum is invalid, remainder is %d\n", +				  block_num, edid_block_tag(block), +				  edid_block_compute_checksum(block)); +		} +		break; +	case EDID_BLOCK_VERSION: +		pr_notice("EDID has major version %d, instead of 1\n", +			  block->version); +		break; +	default: +		WARN(1, "EDID block %d unknown edid block status code %d\n", +		     block_num, status); +		break; +	} +} + +static void edid_block_dump(const char *level, const void *block, int block_num) +{ +	enum edid_block_status status; +	char prefix[20]; + +	status = edid_block_check(block, block_num == 0); +	if (status == EDID_BLOCK_ZERO) +		sprintf(prefix, "\t[%02x] ZERO ", block_num); +	else if (!edid_block_status_valid(status, edid_block_tag(block))) +		sprintf(prefix, "\t[%02x] BAD  ", block_num); +	else +		sprintf(prefix, "\t[%02x] GOOD ", block_num); + +	print_hex_dump(level, prefix, DUMP_PREFIX_NONE, 16, 1, +		       block, EDID_LENGTH, false); +} +  /**   * drm_edid_block_valid - Sanity check the EDID block (base or extension)   * @raw_edid: pointer to raw EDID block @@ -1766,33 +1872,14 @@ bool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid,  			*edid_corrupt = true;  	} +	edid_block_status_print(status, block, block_num); +  	/* Determine whether we can use this block with this status. */  	valid = edid_block_status_valid(status, edid_block_tag(block)); -	/* Some fairly random status printouts. */ -	if (status == EDID_BLOCK_CHECKSUM) { -		if (valid) { -			DRM_DEBUG("EDID block checksum is invalid, remainder is %d\n", -				  edid_block_compute_checksum(block)); -			DRM_DEBUG("Assuming a KVM switch modified the block but left the original checksum\n"); -		} else if (print_bad_edid) { -			DRM_NOTE("EDID block checksum is invalid, remainder is %d\n", -				 edid_block_compute_checksum(block)); -		} -	} else if (status == EDID_BLOCK_VERSION) { -		DRM_NOTE("EDID has major version %d, instead of 1\n", -			 block->version); -	} - -	if (!valid && print_bad_edid) { -		if (edid_is_zero(block, EDID_LENGTH)) { -			pr_notice("EDID block is all zeroes\n"); -		} else { -			pr_notice("Raw EDID:\n"); -			print_hex_dump(KERN_NOTICE, -				       " \t", DUMP_PREFIX_NONE, 16, 1, -				       block, EDID_LENGTH, false); -		} +	if (!valid && print_bad_edid && status != EDID_BLOCK_ZERO) { +		pr_notice("Raw EDID:\n"); +		edid_block_dump(KERN_NOTICE, block, block_num);  	}  	return valid; @@ -1810,14 +1897,16 @@ EXPORT_SYMBOL(drm_edid_block_valid);  bool drm_edid_is_valid(struct edid *edid)  {  	int i; -	u8 *raw = (u8 *)edid;  	if (!edid)  		return false; -	for (i = 0; i <= edid->extensions; i++) -		if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true, NULL)) +	for (i = 0; i < edid_block_count(edid); i++) { +		void *block = (void *)edid_block_data(edid, i); + +		if (!drm_edid_block_valid(block, i, true, NULL))  			return false; +	}  	return true;  } @@ -1830,13 +1919,13 @@ static struct edid *edid_filter_invalid_blocks(const struct edid *edid,  	int valid_extensions = edid->extensions - invalid_blocks;  	int i; -	new = kmalloc_array(valid_extensions + 1, EDID_LENGTH, GFP_KERNEL); +	new = kmalloc(edid_size_by_blocks(valid_extensions + 1), GFP_KERNEL);  	if (!new)  		goto out;  	dest_block = new; -	for (i = 0; i <= edid->extensions; i++) { -		const void *block = edid + i; +	for (i = 0; i < edid_block_count(edid); i++) { +		const void *block = edid_block_data(edid, i);  		if (edid_block_valid(block, i == 0))  			memcpy(dest_block++, block, EDID_LENGTH); @@ -1916,7 +2005,7 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)  }  static void connector_bad_edid(struct drm_connector *connector, -			       u8 *edid, int num_blocks) +			       const struct edid *edid, int num_blocks)  {  	int i;  	u8 last_block; @@ -1927,32 +2016,19 @@ static void connector_bad_edid(struct drm_connector *connector,  	 * of 0x7e in the EDID of the _index_ of the last block in the  	 * combined chunk of memory.  	 */ -	last_block = edid[0x7e]; +	last_block = edid->extensions;  	/* Calculate real checksum for the last edid extension block data */  	if (last_block < num_blocks)  		connector->real_edid_checksum = -			edid_block_compute_checksum(edid + last_block * EDID_LENGTH); +			edid_block_compute_checksum(edid + last_block);  	if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS))  		return;  	drm_dbg_kms(connector->dev, "%s: EDID is invalid:\n", connector->name); -	for (i = 0; i < num_blocks; i++) { -		u8 *block = edid + i * EDID_LENGTH; -		char prefix[20]; - -		if (edid_is_zero(block, EDID_LENGTH)) -			sprintf(prefix, "\t[%02x] ZERO ", i); -		else if (!drm_edid_block_valid(block, i, false, NULL)) -			sprintf(prefix, "\t[%02x] BAD  ", i); -		else -			sprintf(prefix, "\t[%02x] GOOD ", i); - -		print_hex_dump(KERN_DEBUG, -			       prefix, DUMP_PREFIX_NONE, 16, 1, -			       block, EDID_LENGTH, false); -	} +	for (i = 0; i < num_blocks; i++) +		edid_block_dump(KERN_DEBUG, edid + i, i);  }  /* Get override or firmware EDID */ @@ -1999,43 +2075,39 @@ int drm_add_override_edid_modes(struct drm_connector *connector)  }  EXPORT_SYMBOL(drm_add_override_edid_modes); -static struct edid *drm_do_get_edid_base_block(struct drm_connector *connector, -	int (*get_edid_block)(void *data, u8 *buf, unsigned int block, -			      size_t len), -	void *data) +typedef int read_block_fn(void *context, u8 *buf, unsigned int block, size_t len); + +static enum edid_block_status edid_block_read(void *block, unsigned int block_num, +					      read_block_fn read_block, +					      void *context)  { -	int *null_edid_counter = connector ? &connector->null_edid_counter : NULL; -	bool *edid_corrupt = connector ? &connector->edid_corrupt : NULL; -	void *edid; +	enum edid_block_status status; +	bool is_base_block = block_num == 0;  	int try; -	edid = kmalloc(EDID_LENGTH, GFP_KERNEL); -	if (edid == NULL) -		return NULL; - -	/* base block fetch */  	for (try = 0; try < 4; try++) { -		if (get_edid_block(data, edid, 0, EDID_LENGTH)) -			goto out; -		if (drm_edid_block_valid(edid, 0, false, edid_corrupt)) -			break; -		if (try == 0 && edid_is_zero(edid, EDID_LENGTH)) { -			if (null_edid_counter) -				(*null_edid_counter)++; -			goto carp; +		if (read_block(context, block, block_num, EDID_LENGTH)) +			return EDID_BLOCK_READ_FAIL; + +		status = edid_block_check(block, is_base_block); +		if (status == EDID_BLOCK_HEADER_REPAIR) { +			edid_header_fix(block); + +			/* Retry with fixed header, update status if that worked. */ +			status = edid_block_check(block, is_base_block); +			if (status == EDID_BLOCK_OK) +				status = EDID_BLOCK_HEADER_FIXED;  		} -	} -	if (try == 4) -		goto carp; -	return edid; +		if (edid_block_status_valid(status, edid_block_tag(block))) +			break; -carp: -	if (connector) -		connector_bad_edid(connector, edid, 1); -out: -	kfree(edid); -	return NULL; +		/* Fail early for unrepairable base block all zeros. */ +		if (try == 0 && is_base_block && status == EDID_BLOCK_ZERO) +			break; +	} + +	return status;  }  /** @@ -2059,53 +2131,74 @@ out:   * Return: Pointer to valid EDID or NULL if we couldn't find any.   */  struct edid *drm_do_get_edid(struct drm_connector *connector, -	int (*get_edid_block)(void *data, u8 *buf, unsigned int block, -			      size_t len), -	void *data) +			     read_block_fn read_block, +			     void *context)  { -	int j, invalid_blocks = 0; -	struct edid *edid, *new, *override; +	enum edid_block_status status; +	int i, invalid_blocks = 0; +	struct edid *edid, *new; -	override = drm_get_override_edid(connector); -	if (override) -		return override; +	edid = drm_get_override_edid(connector); +	if (edid) +		goto ok; -	edid = drm_do_get_edid_base_block(connector, get_edid_block, data); +	edid = kmalloc(EDID_LENGTH, GFP_KERNEL);  	if (!edid)  		return NULL; -	if (edid->extensions == 0) -		return edid; +	status = edid_block_read(edid, 0, read_block, context); + +	edid_block_status_print(status, edid, 0); + +	if (status == EDID_BLOCK_READ_FAIL) +		goto fail; + +	/* FIXME: Clarify what a corrupt EDID actually means. */ +	if (status == EDID_BLOCK_OK || status == EDID_BLOCK_VERSION) +		connector->edid_corrupt = false; +	else +		connector->edid_corrupt = true; + +	if (!edid_block_status_valid(status, edid_block_tag(edid))) { +		if (status == EDID_BLOCK_ZERO) +			connector->null_edid_counter++; + +		connector_bad_edid(connector, edid, 1); +		goto fail; +	} -	new = krealloc(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL); +	if (!edid_extension_block_count(edid)) +		goto ok; + +	new = krealloc(edid, edid_size(edid), GFP_KERNEL);  	if (!new) -		goto out; +		goto fail;  	edid = new; -	for (j = 1; j <= edid->extensions; j++) { -		void *block = edid + j; -		int try; +	for (i = 1; i < edid_block_count(edid); i++) { +		void *block = (void *)edid_block_data(edid, i); -		for (try = 0; try < 4; try++) { -			if (get_edid_block(data, block, j, EDID_LENGTH)) -				goto out; -			if (drm_edid_block_valid(block, j, false, NULL)) -				break; -		} +		status = edid_block_read(block, i, read_block, context); + +		edid_block_status_print(status, block, i); -		if (try == 4) +		if (!edid_block_status_valid(status, edid_block_tag(block))) { +			if (status == EDID_BLOCK_READ_FAIL) +				goto fail;  			invalid_blocks++; +		}  	}  	if (invalid_blocks) { -		connector_bad_edid(connector, (u8 *)edid, edid->extensions + 1); +		connector_bad_edid(connector, edid, edid_block_count(edid));  		edid = edid_filter_invalid_blocks(edid, invalid_blocks);  	} +ok:  	return edid; -out: +fail:  	kfree(edid);  	return NULL;  } @@ -2199,20 +2292,27 @@ static u32 edid_extract_panel_id(const struct edid *edid)  u32 drm_edid_get_panel_id(struct i2c_adapter *adapter)  { -	const struct edid *edid; -	u32 panel_id; - -	edid = drm_do_get_edid_base_block(NULL, drm_do_probe_ddc_edid, adapter); +	enum edid_block_status status; +	void *base_block; +	u32 panel_id = 0;  	/*  	 * There are no manufacturer IDs of 0, so if there is a problem reading  	 * the EDID then we'll just return 0.  	 */ -	if (!edid) + +	base_block = kmalloc(EDID_LENGTH, GFP_KERNEL); +	if (!base_block)  		return 0; -	panel_id = edid_extract_panel_id(edid); -	kfree(edid); +	status = edid_block_read(base_block, 0, drm_do_probe_ddc_edid, adapter); + +	edid_block_status_print(status, base_block, 0); + +	if (edid_block_status_valid(status, edid_block_tag(base_block))) +		panel_id = edid_extract_panel_id(base_block); + +	kfree(base_block);  	return panel_id;  } @@ -2255,7 +2355,7 @@ EXPORT_SYMBOL(drm_get_edid_switcheroo);   */  struct edid *drm_edid_duplicate(const struct edid *edid)  { -	return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL); +	return kmemdup(edid, edid_size(edid), GFP_KERNEL);  }  EXPORT_SYMBOL(drm_edid_duplicate); @@ -2439,8 +2539,8 @@ drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *clos  	for (i = 0; i < EDID_DETAILED_TIMINGS; i++)  		cb(&(edid->detailed_timings[i]), closure); -	for (i = 1; i <= edid->extensions; i++) { -		const u8 *ext = (const u8 *)edid + (i * EDID_LENGTH); +	for (i = 0; i < edid_extension_block_count(edid); i++) { +		const u8 *ext = edid_extension_block_data(edid, i);  		switch (*ext) {  		case CEA_EXT: @@ -3410,17 +3510,17 @@ const u8 *drm_find_edid_extension(const struct edid *edid,  	int i;  	/* No EDID or EDID extensions */ -	if (edid == NULL || edid->extensions == 0) +	if (!edid || !edid_extension_block_count(edid))  		return NULL;  	/* Find CEA extension */ -	for (i = *ext_index; i < edid->extensions; i++) { -		edid_ext = (const u8 *)edid + EDID_LENGTH * (i + 1); +	for (i = *ext_index; i < edid_extension_block_count(edid); i++) { +		edid_ext = edid_extension_block_data(edid, i);  		if (edid_block_tag(edid_ext) == ext_id)  			break;  	} -	if (i >= edid->extensions) +	if (i >= edid_extension_block_count(edid))  		return NULL;  	*ext_index = i + 1; @@ -3551,9 +3651,11 @@ static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_m  		match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;  	for (vic = 1; vic < cea_num_vics(); vic = cea_next_vic(vic)) { -		struct drm_display_mode cea_mode = *cea_mode_for_vic(vic); +		struct drm_display_mode cea_mode;  		unsigned int clock1, clock2; +		drm_mode_init(&cea_mode, cea_mode_for_vic(vic)); +  		/* Check both 60Hz and 59.94Hz */  		clock1 = cea_mode.clock;  		clock2 = cea_mode_alternate_clock(&cea_mode); @@ -3590,9 +3692,11 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)  		match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;  	for (vic = 1; vic < cea_num_vics(); vic = cea_next_vic(vic)) { -		struct drm_display_mode cea_mode = *cea_mode_for_vic(vic); +		struct drm_display_mode cea_mode;  		unsigned int clock1, clock2; +		drm_mode_init(&cea_mode, cea_mode_for_vic(vic)); +  		/* Check both 60Hz and 59.94Hz */  		clock1 = cea_mode.clock;  		clock2 = cea_mode_alternate_clock(&cea_mode); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 133dfae06fab..eb0c2d041f13 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -771,7 +771,8 @@ long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle,  		return -EINVAL;  	} -	ret = dma_resv_wait_timeout(obj->resv, wait_all, true, timeout); +	ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(wait_all), +				    true, timeout);  	if (ret == 0)  		ret = -ETIME;  	else if (ret > 0) diff --git a/drivers/gpu/drm/drm_gem_atomic_helper.c b/drivers/gpu/drm/drm_gem_atomic_helper.c index 9338ddb7edff..a6d89aed0bda 100644 --- a/drivers/gpu/drm/drm_gem_atomic_helper.c +++ b/drivers/gpu/drm/drm_gem_atomic_helper.c @@ -151,7 +151,7 @@ int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_st  		return 0;  	obj = drm_gem_fb_get_obj(state->fb, 0); -	ret = dma_resv_get_singleton(obj->resv, false, &fence); +	ret = dma_resv_get_singleton(obj->resv, DMA_RESV_USAGE_WRITE, &fence);  	if (ret)  		return ret; diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 6e7e10f16ec0..e699950cc34d 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -837,7 +837,9 @@ EXPORT_SYMBOL(drm_mode_vrefresh);  void drm_mode_get_hv_timing(const struct drm_display_mode *mode,  			    int *hdisplay, int *vdisplay)  { -	struct drm_display_mode adjusted = *mode; +	struct drm_display_mode adjusted; + +	drm_mode_init(&adjusted, mode);  	drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY);  	*hdisplay = adjusted.crtc_hdisplay; diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index b701cda86d0c..2ff31717a3de 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -644,7 +644,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,  	vblank->linedur_ns  = linedur_ns;  	vblank->framedur_ns = framedur_ns; -	vblank->hwmode = *mode; +	drm_mode_copy(&vblank->hwmode, mode);  	drm_dbg_core(dev,  		     "crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n", diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index d5314aa28ff7..507172e2780b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -380,12 +380,14 @@ int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,  	}  	if (op & ETNA_PREP_NOSYNC) { -		if (!dma_resv_test_signaled(obj->resv, write)) +		if (!dma_resv_test_signaled(obj->resv, +					    dma_resv_usage_rw(write)))  			return -EBUSY;  	} else {  		unsigned long remain = etnaviv_timeout_to_jiffies(timeout); -		ret = dma_resv_wait_timeout(obj->resv, write, true, remain); +		ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(write), +					    true, remain);  		if (ret <= 0)  			return ret == 0 ? -ETIMEDOUT : ret;  	} diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 53f7c78628a4..98bb5c9239de 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -202,14 +202,10 @@ static void submit_attach_object_fences(struct etnaviv_gem_submit *submit)  	for (i = 0; i < submit->nr_bos; i++) {  		struct drm_gem_object *obj = &submit->bos[i].obj->base; +		bool write = submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE; -		if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE) -			dma_resv_add_excl_fence(obj->resv, -							  submit->out_fence); -		else -			dma_resv_add_shared_fence(obj->resv, -							    submit->out_fence); - +		dma_resv_add_fence(obj->resv, submit->out_fence, write ? +				   DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);  		submit_unlock_object(submit, i);  	}  } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 2aff54d505e2..1d8744f3e702 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -396,9 +396,8 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags)  	drm_for_each_connector_iter(connector, &conn_iter) {  		gma_encoder = gma_attached_encoder(connector); -		switch (gma_encoder->type) { -		case INTEL_OUTPUT_LVDS: -		case INTEL_OUTPUT_MIPI: +		if (gma_encoder->type == INTEL_OUTPUT_LVDS || +		    gma_encoder->type == INTEL_OUTPUT_MIPI) {  			ret = gma_backlight_init(dev);  			break;  		} diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c index 3d87da283cfa..efe8591619e3 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c @@ -1047,7 +1047,8 @@ intel_prepare_plane_fb(struct drm_plane *_plane,  		if (ret < 0)  			goto unpin_fb; -		dma_resv_iter_begin(&cursor, obj->base.resv, false); +		dma_resv_iter_begin(&cursor, obj->base.resv, +				    DMA_RESV_USAGE_WRITE);  		dma_resv_for_each_fence_unlocked(&cursor, fence) {  			add_rps_boost_after_vblank(new_plane_state->hw.crtc,  						   fence); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_busy.c b/drivers/gpu/drm/i915/gem/i915_gem_busy.c index 470fdfd61a0f..ddda468241ef 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_busy.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_busy.c @@ -138,21 +138,21 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,  	 * Alternatively, we can trade that extra information on read/write  	 * activity with  	 *	args->busy = -	 *		!dma_resv_test_signaled(obj->resv, true); +	 *		!dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ);  	 * to report the overall busyness. This is what the wait-ioctl does.  	 *  	 */  	args->busy = 0; -	dma_resv_iter_begin(&cursor, obj->base.resv, true); +	dma_resv_iter_begin(&cursor, obj->base.resv, DMA_RESV_USAGE_READ);  	dma_resv_for_each_fence_unlocked(&cursor, fence) {  		if (dma_resv_iter_is_restarted(&cursor))  			args->busy = 0; -		if (dma_resv_iter_is_exclusive(&cursor)) -			/* Translate the exclusive fence to the READ *and* WRITE engine */ +		if (dma_resv_iter_usage(&cursor) <= DMA_RESV_USAGE_WRITE) +			/* Translate the write fences to the READ *and* WRITE engine */  			args->busy |= busy_check_writer(fence);  		else -			/* Translate shared fences to READ set of engines */ +			/* Translate read fences to READ set of engines */  			args->busy |= busy_check_reader(fence);  	}  	dma_resv_iter_end(&cursor); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_clflush.c b/drivers/gpu/drm/i915/gem/i915_gem_clflush.c index 1fd0cc9ca213..0512afdd20d8 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_clflush.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_clflush.c @@ -116,7 +116,8 @@ bool i915_gem_clflush_object(struct drm_i915_gem_object *obj,  						obj->base.resv, NULL, true,  						i915_fence_timeout(i915),  						I915_FENCE_GFP); -		dma_resv_add_excl_fence(obj->base.resv, &clflush->base.dma); +		dma_resv_add_fence(obj->base.resv, &clflush->base.dma, +				   DMA_RESV_USAGE_KERNEL);  		dma_fence_work_commit(&clflush->base);  		/*  		 * We must have successfully populated the pages(since we are diff --git a/drivers/gpu/drm/i915/gem/i915_gem_lmem.c b/drivers/gpu/drm/i915/gem/i915_gem_lmem.c index ede084f36ca9..02ed3b269326 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_lmem.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_lmem.c @@ -68,7 +68,7 @@ bool __i915_gem_object_is_lmem(struct drm_i915_gem_object *obj)  	struct intel_memory_region *mr = READ_ONCE(obj->mm.region);  #ifdef CONFIG_LOCKDEP -	GEM_WARN_ON(dma_resv_test_signaled(obj->base.resv, true) && +	GEM_WARN_ON(dma_resv_test_signaled(obj->base.resv, DMA_RESV_USAGE_BOOKKEEP) &&  		    i915_gem_object_evictable(obj));  #endif  	return mr && (mr->type == INTEL_MEMORY_LOCAL || diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c index c1c3b510b9e2..747ac65e060f 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c @@ -742,30 +742,19 @@ static const struct drm_gem_object_funcs i915_gem_object_funcs = {  /**   * i915_gem_object_get_moving_fence - Get the object's moving fence if any   * @obj: The object whose moving fence to get. + * @fence: The resulting fence   *   * A non-signaled moving fence means that there is an async operation   * pending on the object that needs to be waited on before setting up   * any GPU- or CPU PTEs to the object's pages.   * - * Return: A refcounted pointer to the object's moving fence if any, - * NULL otherwise. + * Return: Negative error code or 0 for success.   */ -struct dma_fence * -i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj) +int i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj, +				     struct dma_fence **fence)  { -	return dma_fence_get(i915_gem_to_ttm(obj)->moving); -} - -void i915_gem_object_set_moving_fence(struct drm_i915_gem_object *obj, -				      struct dma_fence *fence) -{ -	struct dma_fence **moving = &i915_gem_to_ttm(obj)->moving; - -	if (*moving == fence) -		return; - -	dma_fence_put(*moving); -	*moving = dma_fence_get(fence); +	return dma_resv_get_singleton(obj->base.resv, DMA_RESV_USAGE_KERNEL, +				      fence);  }  /** @@ -783,23 +772,16 @@ void i915_gem_object_set_moving_fence(struct drm_i915_gem_object *obj,  int i915_gem_object_wait_moving_fence(struct drm_i915_gem_object *obj,  				      bool intr)  { -	struct dma_fence *fence = i915_gem_to_ttm(obj)->moving; -	int ret; +	long ret;  	assert_object_held(obj); -	if (!fence) -		return 0; -	ret = dma_fence_wait(fence, intr); -	if (ret) -		return ret; +	ret = dma_resv_wait_timeout(obj->base. resv, DMA_RESV_USAGE_KERNEL, +				    intr, MAX_SCHEDULE_TIMEOUT); +	if (!ret) +		ret = -ETIME; -	if (fence->error) -		return fence->error; - -	i915_gem_to_ttm(obj)->moving = NULL; -	dma_fence_put(fence); -	return 0; +	return ret < 0 ? ret : 0;  }  #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h index 02c37fe4a535..e11d82a9f7c3 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h @@ -520,12 +520,8 @@ i915_gem_object_finish_access(struct drm_i915_gem_object *obj)  	i915_gem_object_unpin_pages(obj);  } -struct dma_fence * -i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj); - -void i915_gem_object_set_moving_fence(struct drm_i915_gem_object *obj, -				      struct dma_fence *fence); - +int i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj, +				     struct dma_fence **fence);  int i915_gem_object_wait_moving_fence(struct drm_i915_gem_object *obj,  				      bool intr); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c index 432ac74ff225..a10716f4e717 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c @@ -467,19 +467,6 @@ out:  	return fence;  } -static int -prev_deps(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, -	  struct i915_deps *deps) -{ -	int ret; - -	ret = i915_deps_add_dependency(deps, bo->moving, ctx); -	if (!ret) -		ret = i915_deps_add_resv(deps, bo->base.resv, ctx); - -	return ret; -} -  /**   * i915_ttm_move - The TTM move callback used by i915.   * @bo: The buffer object. @@ -534,7 +521,7 @@ int i915_ttm_move(struct ttm_buffer_object *bo, bool evict,  		struct i915_deps deps;  		i915_deps_init(&deps, GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN); -		ret = prev_deps(bo, ctx, &deps); +		ret = i915_deps_add_resv(&deps, bo->base.resv, ctx);  		if (ret) {  			i915_refct_sgt_put(dst_rsgt);  			return ret; @@ -637,9 +624,8 @@ int i915_gem_obj_copy_ttm(struct drm_i915_gem_object *dst,  	if (IS_ERR_OR_NULL(copy_fence))  		return PTR_ERR_OR_ZERO(copy_fence); -	dma_resv_add_excl_fence(dst_bo->base.resv, copy_fence); -	dma_resv_add_shared_fence(src_bo->base.resv, copy_fence); - +	dma_resv_add_fence(dst_bo->base.resv, copy_fence, DMA_RESV_USAGE_WRITE); +	dma_resv_add_fence(src_bo->base.resv, copy_fence, DMA_RESV_USAGE_READ);  	dma_fence_put(copy_fence);  	return 0; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c index 6d1a71d6404c..094f06b4ce33 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c @@ -86,7 +86,7 @@ static bool i915_gem_userptr_invalidate(struct mmu_interval_notifier *mni,  		return true;  	/* we will unbind on next submission, still have userptr pins */ -	r = dma_resv_wait_timeout(obj->base.resv, true, false, +	r = dma_resv_wait_timeout(obj->base.resv, DMA_RESV_USAGE_BOOKKEEP, false,  				  MAX_SCHEDULE_TIMEOUT);  	if (r <= 0)  		drm_err(&i915->drm, "(%ld) failed to wait for idle\n", r); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_wait.c b/drivers/gpu/drm/i915/gem/i915_gem_wait.c index dab3d30c09a0..319936f91ac5 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_wait.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_wait.c @@ -40,7 +40,8 @@ i915_gem_object_wait_reservation(struct dma_resv *resv,  	struct dma_fence *fence;  	long ret = timeout ?: 1; -	dma_resv_iter_begin(&cursor, resv, flags & I915_WAIT_ALL); +	dma_resv_iter_begin(&cursor, resv, +			    dma_resv_usage_rw(flags & I915_WAIT_ALL));  	dma_resv_for_each_fence_unlocked(&cursor, fence) {  		ret = i915_gem_object_wait_fence(fence, flags, timeout);  		if (ret <= 0) @@ -117,7 +118,8 @@ i915_gem_object_wait_priority(struct drm_i915_gem_object *obj,  	struct dma_resv_iter cursor;  	struct dma_fence *fence; -	dma_resv_iter_begin(&cursor, obj->base.resv, flags & I915_WAIT_ALL); +	dma_resv_iter_begin(&cursor, obj->base.resv, +			    dma_resv_usage_rw(flags & I915_WAIT_ALL));  	dma_resv_for_each_fence_unlocked(&cursor, fence)  		i915_gem_fence_wait_priority(fence, attr);  	dma_resv_iter_end(&cursor); diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index b071a58dd6da..b4275b55e5b8 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -219,7 +219,8 @@ static int igt_dmabuf_import_same_driver(struct drm_i915_private *i915,  		goto out_detach;  	} -	timeout = dma_resv_wait_timeout(dmabuf->resv, false, true, 5 * HZ); +	timeout = dma_resv_wait_timeout(dmabuf->resv, DMA_RESV_USAGE_WRITE, +					true, 5 * HZ);  	if (!timeout) {  		pr_err("dmabuf wait for exclusive fence timed out.\n");  		timeout = -ETIME; diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_migrate.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_migrate.c index 0e52eb87cd55..0ad443a90c8b 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_migrate.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_migrate.c @@ -218,9 +218,8 @@ static int __igt_lmem_pages_migrate(struct intel_gt *gt,  		if (rq) {  			err = dma_resv_reserve_fences(obj->base.resv, 1);  			if (!err) -				dma_resv_add_excl_fence(obj->base.resv, -							&rq->fence); -			i915_gem_object_set_moving_fence(obj, &rq->fence); +				dma_resv_add_fence(obj->base.resv, &rq->fence, +						   DMA_RESV_USAGE_KERNEL);  			i915_request_put(rq);  		}  		if (err) diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c index c4c2c91a2ee7..5bc93a1ce3e3 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c @@ -1221,8 +1221,8 @@ static int __igt_mmap_migrate(struct intel_memory_region **placements,  					  expand32(POISON_INUSE), &rq);  	i915_gem_object_unpin_pages(obj);  	if (rq) { -		dma_resv_add_excl_fence(obj->base.resv, &rq->fence); -		i915_gem_object_set_moving_fence(obj, &rq->fence); +		dma_resv_add_fence(obj->base.resv, &rq->fence, +				   DMA_RESV_USAGE_KERNEL);  		i915_request_put(rq);  	}  	i915_gem_object_unlock(obj); diff --git a/drivers/gpu/drm/i915/i915_deps.c b/drivers/gpu/drm/i915/i915_deps.c index 999210b37325..297b8e4e42ee 100644 --- a/drivers/gpu/drm/i915/i915_deps.c +++ b/drivers/gpu/drm/i915/i915_deps.c @@ -226,7 +226,7 @@ int i915_deps_add_resv(struct i915_deps *deps, struct dma_resv *resv,  	struct dma_fence *fence;  	dma_resv_assert_held(resv); -	dma_resv_for_each_fence(&iter, resv, true, fence) { +	dma_resv_for_each_fence(&iter, resv, dma_resv_usage_rw(true), fence) {  		int ret = i915_deps_add_dependency(deps, fence, ctx);  		if (ret) diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index 582770360ad1..73d5195146b0 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -1598,7 +1598,8 @@ i915_request_await_object(struct i915_request *to,  	struct dma_fence *fence;  	int ret = 0; -	dma_resv_for_each_fence(&cursor, obj->base.resv, write, fence) { +	dma_resv_for_each_fence(&cursor, obj->base.resv, +				dma_resv_usage_rw(write), fence) {  		ret = i915_request_await_dma_fence(to, fence);  		if (ret)  			break; diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c index 2a74a9a1cafe..ae984c66c48a 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence.c +++ b/drivers/gpu/drm/i915/i915_sw_fence.c @@ -585,7 +585,7 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence,  	debug_fence_assert(fence);  	might_sleep_if(gfpflags_allow_blocking(gfp)); -	dma_resv_iter_begin(&cursor, resv, write); +	dma_resv_iter_begin(&cursor, resv, dma_resv_usage_rw(write));  	dma_resv_for_each_fence_unlocked(&cursor, f) {  		pending = i915_sw_fence_await_dma_fence(fence, f, timeout,  							gfp); diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c index bae3423f58e8..d077f7b9eaad 100644 --- a/drivers/gpu/drm/i915/i915_vma.c +++ b/drivers/gpu/drm/i915/i915_vma.c @@ -1357,10 +1357,17 @@ int i915_vma_pin_ww(struct i915_vma *vma, struct i915_gem_ww_ctx *ww,  	if (err)  		return err; +	if (vma->obj) { +		err = i915_gem_object_get_moving_fence(vma->obj, &moving); +		if (err) +			return err; +	} else { +		moving = NULL; +	} +  	if (flags & PIN_GLOBAL)  		wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm); -	moving = vma->obj ? i915_gem_object_get_moving_fence(vma->obj) : NULL;  	if (flags & vma->vm->bind_async_flags || moving) {  		/* lock VM */  		err = i915_vm_lock_objects(vma->vm, ww); @@ -1826,7 +1833,8 @@ int _i915_vma_move_to_active(struct i915_vma *vma,  		}  		if (fence) { -			dma_resv_add_excl_fence(vma->obj->base.resv, fence); +			dma_resv_add_fence(vma->obj->base.resv, fence, +					   DMA_RESV_USAGE_WRITE);  			obj->write_domain = I915_GEM_DOMAIN_RENDER;  			obj->read_domains = 0;  		} @@ -1838,7 +1846,8 @@ int _i915_vma_move_to_active(struct i915_vma *vma,  		}  		if (fence) { -			dma_resv_add_shared_fence(vma->obj->base.resv, fence); +			dma_resv_add_fence(vma->obj->base.resv, fence, +					   DMA_RESV_USAGE_READ);  			obj->write_domain = 0;  		}  	} @@ -2078,7 +2087,7 @@ int i915_vma_unbind_async(struct i915_vma *vma, bool trylock_vm)  		goto out_rpm;  	} -	dma_resv_add_shared_fence(obj->base.resv, fence); +	dma_resv_add_fence(obj->base.resv, fence, DMA_RESV_USAGE_READ);  	dma_fence_put(fence);  out_rpm: diff --git a/drivers/gpu/drm/i915/selftests/intel_memory_region.c b/drivers/gpu/drm/i915/selftests/intel_memory_region.c index 6114e013092b..73eb53edb8de 100644 --- a/drivers/gpu/drm/i915/selftests/intel_memory_region.c +++ b/drivers/gpu/drm/i915/selftests/intel_memory_region.c @@ -1056,7 +1056,8 @@ static int igt_lmem_write_cpu(void *arg)  					  obj->mm.pages->sgl, I915_CACHE_NONE,  					  true, 0xdeadbeaf, &rq);  	if (rq) { -		dma_resv_add_excl_fence(obj->base.resv, &rq->fence); +		dma_resv_add_fence(obj->base.resv, &rq->fence, +				   DMA_RESV_USAGE_WRITE);  		i915_request_put(rq);  	} diff --git a/drivers/gpu/drm/ingenic/Kconfig b/drivers/gpu/drm/ingenic/Kconfig index 001f59fb06d5..090830bcbde7 100644 --- a/drivers/gpu/drm/ingenic/Kconfig +++ b/drivers/gpu/drm/ingenic/Kconfig @@ -24,4 +24,13 @@ config DRM_INGENIC_IPU  	  The Image Processing Unit (IPU) will appear as a second primary plane. +config DRM_INGENIC_DW_HDMI +	tristate "Ingenic specific support for Synopsys DW HDMI" +	depends on MACH_JZ4780 +	select DRM_DW_HDMI +	help +	  Choose this option to enable Synopsys DesignWare HDMI based driver. +	  If you want to enable HDMI on Ingenic JZ4780 based SoC, you should +	  select this option. +  endif diff --git a/drivers/gpu/drm/ingenic/Makefile b/drivers/gpu/drm/ingenic/Makefile index d313326bdddb..f10cc1c5a5f2 100644 --- a/drivers/gpu/drm/ingenic/Makefile +++ b/drivers/gpu/drm/ingenic/Makefile @@ -1,3 +1,4 @@  obj-$(CONFIG_DRM_INGENIC) += ingenic-drm.o  ingenic-drm-y = ingenic-drm-drv.o  ingenic-drm-$(CONFIG_DRM_INGENIC_IPU) += ingenic-ipu.o +obj-$(CONFIG_DRM_INGENIC_DW_HDMI) += ingenic-dw-hdmi.o diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index a4f5a323f490..8eb0ad501a7b 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -833,6 +833,32 @@ static int ingenic_drm_bridge_atomic_check(struct drm_bridge *bridge,  	}  } +static u32 * +ingenic_drm_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, +					     struct drm_bridge_state *bridge_state, +					     struct drm_crtc_state *crtc_state, +					     struct drm_connector_state *conn_state, +					     u32 output_fmt, +					     unsigned int *num_input_fmts) +{ +	switch (output_fmt) { +	case MEDIA_BUS_FMT_RGB888_1X24: +	case MEDIA_BUS_FMT_RGB666_1X18: +	case MEDIA_BUS_FMT_RGB565_1X16: +	case MEDIA_BUS_FMT_RGB888_3X8: +	case MEDIA_BUS_FMT_RGB888_3X8_DELTA: +		break; +	default: +		*num_input_fmts = 0; +		return NULL; +	} + +	return drm_atomic_helper_bridge_propagate_bus_fmt(bridge, bridge_state, +							  crtc_state, conn_state, +							  output_fmt, +							  num_input_fmts); +} +  static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)  {  	struct ingenic_drm *priv = drm_device_get_priv(arg); @@ -984,7 +1010,7 @@ static const struct drm_bridge_funcs ingenic_drm_bridge_funcs = {  	.atomic_reset		= drm_atomic_helper_bridge_reset,  	.atomic_duplicate_state	= drm_atomic_helper_bridge_duplicate_state,  	.atomic_destroy_state	= drm_atomic_helper_bridge_destroy_state, -	.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, +	.atomic_get_input_bus_fmts = ingenic_drm_bridge_atomic_get_input_bus_fmts,  };  static const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = { diff --git a/drivers/gpu/drm/ingenic/ingenic-dw-hdmi.c b/drivers/gpu/drm/ingenic/ingenic-dw-hdmi.c new file mode 100644 index 000000000000..72f8b44998a5 --- /dev/null +++ b/drivers/gpu/drm/ingenic/ingenic-dw-hdmi.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2011-2013 Freescale Semiconductor, Inc. + * Copyright (C) 2019, 2020 Paul Boddie <paul@boddie.org.uk> + * + * Derived from dw_hdmi-imx.c with i.MX portions removed. + */ + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> + +#include <drm/bridge/dw_hdmi.h> +#include <drm/drm_of.h> +#include <drm/drm_print.h> + +static const struct dw_hdmi_mpll_config ingenic_mpll_cfg[] = { +	{ 45250000,  { { 0x01e0, 0x0000 }, { 0x21e1, 0x0000 }, { 0x41e2, 0x0000 } } }, +	{ 92500000,  { { 0x0140, 0x0005 }, { 0x2141, 0x0005 }, { 0x4142, 0x0005 } } }, +	{ 148500000, { { 0x00a0, 0x000a }, { 0x20a1, 0x000a }, { 0x40a2, 0x000a } } }, +	{ 216000000, { { 0x00a0, 0x000a }, { 0x2001, 0x000f }, { 0x4002, 0x000f } } }, +	{ ~0UL,      { { 0x0000, 0x0000 }, { 0x0000, 0x0000 }, { 0x0000, 0x0000 } } } +}; + +static const struct dw_hdmi_curr_ctrl ingenic_cur_ctr[] = { +	/*pixelclk     bpp8    bpp10   bpp12 */ +	{ 54000000,  { 0x091c, 0x091c, 0x06dc } }, +	{ 58400000,  { 0x091c, 0x06dc, 0x06dc } }, +	{ 72000000,  { 0x06dc, 0x06dc, 0x091c } }, +	{ 74250000,  { 0x06dc, 0x0b5c, 0x091c } }, +	{ 118800000, { 0x091c, 0x091c, 0x06dc } }, +	{ 216000000, { 0x06dc, 0x0b5c, 0x091c } }, +	{ ~0UL,      { 0x0000, 0x0000, 0x0000 } }, +}; + +/* + * Resistance term 133Ohm Cfg + * PREEMP config 0.00 + * TX/CK level 10 + */ +static const struct dw_hdmi_phy_config ingenic_phy_config[] = { +	/*pixelclk   symbol   term   vlev */ +	{ 216000000, 0x800d, 0x0005, 0x01ad}, +	{ ~0UL,      0x0000, 0x0000, 0x0000} +}; + +static enum drm_mode_status +ingenic_dw_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data, +			   const struct drm_display_info *info, +			   const struct drm_display_mode *mode) +{ +	if (mode->clock < 13500) +		return MODE_CLOCK_LOW; +	/* FIXME: Hardware is capable of 270MHz, but setup data is missing. */ +	if (mode->clock > 216000) +		return MODE_CLOCK_HIGH; + +	return MODE_OK; +} + +static struct dw_hdmi_plat_data ingenic_dw_hdmi_plat_data = { +	.mpll_cfg   = ingenic_mpll_cfg, +	.cur_ctr    = ingenic_cur_ctr, +	.phy_config = ingenic_phy_config, +	.mode_valid = ingenic_dw_hdmi_mode_valid, +	.output_port	= 1, +}; + +static const struct of_device_id ingenic_dw_hdmi_dt_ids[] = { +	{ .compatible = "ingenic,jz4780-dw-hdmi" }, +	{ /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ingenic_dw_hdmi_dt_ids); + +static void ingenic_dw_hdmi_cleanup(void *data) +{ +	struct dw_hdmi *hdmi = (struct dw_hdmi *)data; + +	dw_hdmi_remove(hdmi); +} + +static int ingenic_dw_hdmi_probe(struct platform_device *pdev) +{ +	struct dw_hdmi *hdmi; + +	hdmi = dw_hdmi_probe(pdev, &ingenic_dw_hdmi_plat_data); +	if (IS_ERR(hdmi)) +		return PTR_ERR(hdmi); + +	return devm_add_action_or_reset(&pdev->dev, ingenic_dw_hdmi_cleanup, hdmi); +} + +static struct platform_driver ingenic_dw_hdmi_driver = { +	.probe  = ingenic_dw_hdmi_probe, +	.driver = { +		.name = "dw-hdmi-ingenic", +		.of_match_table = ingenic_dw_hdmi_dt_ids, +	}, +}; +module_platform_driver(ingenic_dw_hdmi_driver); + +MODULE_DESCRIPTION("JZ4780 Specific DW-HDMI Driver Extension"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dw-hdmi-ingenic"); diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c index e0a11ee0e86d..0f1ca0b0db49 100644 --- a/drivers/gpu/drm/lima/lima_gem.c +++ b/drivers/gpu/drm/lima/lima_gem.c @@ -364,10 +364,9 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit)  	fence = lima_sched_context_queue_task(submit->task);  	for (i = 0; i < submit->nr_bos; i++) { -		if (submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE) -			dma_resv_add_excl_fence(lima_bo_resv(bos[i]), fence); -		else -			dma_resv_add_shared_fence(lima_bo_resv(bos[i]), fence); +		dma_resv_add_fence(lima_bo_resv(bos[i]), fence, +				   submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE ? +				   DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);  	}  	drm_gem_unlock_reservations((struct drm_gem_object **)bos, diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 02b9ae65a96a..01bbb5f2d462 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -848,7 +848,8 @@ int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout)  		op & MSM_PREP_NOSYNC ? 0 : timeout_to_jiffies(timeout);  	long ret; -	ret = dma_resv_wait_timeout(obj->resv, write, true,  remain); +	ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(write), +				    true,  remain);  	if (ret == 0)  		return remain == 0 ? -EBUSY : -ETIMEDOUT;  	else if (ret < 0) diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 3164db8be893..8d1eef914ba8 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -395,9 +395,11 @@ static void submit_attach_object_fences(struct msm_gem_submit *submit)  		struct drm_gem_object *obj = &submit->bos[i].obj->base;  		if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE) -			dma_resv_add_excl_fence(obj->resv, submit->user_fence); +			dma_resv_add_fence(obj->resv, submit->user_fence, +					   DMA_RESV_USAGE_WRITE);  		else if (submit->bos[i].flags & MSM_SUBMIT_BO_READ) -			dma_resv_add_shared_fence(obj->resv, submit->user_fence); +			dma_resv_add_fence(obj->resv, submit->user_fence, +					   DMA_RESV_USAGE_READ);  	}  } diff --git a/drivers/gpu/drm/nouveau/dispnv50/base917c.c b/drivers/gpu/drm/nouveau/dispnv50/base917c.c index a1baed4fe0e9..ca260509a4f1 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/base917c.c +++ b/drivers/gpu/drm/nouveau/dispnv50/base917c.c @@ -22,7 +22,7 @@  #include "base.h"  #include "atom.h" -const u32 +static const u32  base917c_format[] = {  	DRM_FORMAT_C8,  	DRM_FORMAT_XRGB8888, diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c index e2faf92e4831..8642b84ea20c 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c @@ -558,7 +558,8 @@ nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state)  			asyw->image.handle[0] = ctxdma->object.handle;  	} -	ret = dma_resv_get_singleton(nvbo->bo.base.resv, false, +	ret = dma_resv_get_singleton(nvbo->bo.base.resv, +				     DMA_RESV_USAGE_WRITE,  				     &asyw->state.fence);  	if (ret)  		return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 74f8652d2bd3..05076e530e7d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -962,11 +962,11 @@ nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo,  	struct dma_fence *fence;  	int ret; -	/* TODO: This is actually a memory management dependency */ -	ret = dma_resv_get_singleton(bo->base.resv, false, &fence); +	ret = dma_resv_get_singleton(bo->base.resv, DMA_RESV_USAGE_WRITE, +				     &fence);  	if (ret) -		dma_resv_wait_timeout(bo->base.resv, false, false, -				      MAX_SCHEDULE_TIMEOUT); +		dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_WRITE, +				      false, MAX_SCHEDULE_TIMEOUT);  	nv10_bo_put_tile_region(dev, *old_tile, fence);  	*old_tile = new_tile; @@ -1308,10 +1308,11 @@ nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence, bool excl  {  	struct dma_resv *resv = nvbo->bo.base.resv; -	if (exclusive) -		dma_resv_add_excl_fence(resv, &fence->base); -	else if (fence) -		dma_resv_add_shared_fence(resv, &fence->base); +	if (!fence) +		return; + +	dma_resv_add_fence(resv, &fence->base, exclusive ? +			   DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);  }  static void diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 0268259e97eb..7f01dcf81fab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -350,17 +350,21 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan,  	if (ret)  		return ret; -	/* Waiting for the exclusive fence first causes performance regressions -	 * under some circumstances. So manually wait for the shared ones first. +	/* Waiting for the writes first causes performance regressions +	 * under some circumstances. So manually wait for the reads first.  	 */  	for (i = 0; i < 2; ++i) {  		struct dma_resv_iter cursor;  		struct dma_fence *fence; -		dma_resv_for_each_fence(&cursor, resv, exclusive, fence) { +		dma_resv_for_each_fence(&cursor, resv, +					dma_resv_usage_rw(exclusive), +					fence) { +			enum dma_resv_usage usage;  			struct nouveau_fence *f; -			if (i == 0 && dma_resv_iter_is_exclusive(&cursor)) +			usage = dma_resv_iter_usage(&cursor); +			if (i == 0 && usage == DMA_RESV_USAGE_WRITE)  				continue;  			f = nouveau_local_fence(fence, chan->drm); diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 9416bee92141..fab542a758ff 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -962,7 +962,8 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,  		return -ENOENT;  	nvbo = nouveau_gem_object(gem); -	lret = dma_resv_wait_timeout(nvbo->bo.base.resv, write, true, +	lret = dma_resv_wait_timeout(nvbo->bo.base.resv, +				     dma_resv_usage_rw(write), true,  				     no_wait ? 0 : 30 * HZ);  	if (!lret)  		ret = -EBUSY; diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index 60019d0532fc..347488685f74 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -93,22 +93,7 @@ int nouveau_gem_prime_pin(struct drm_gem_object *obj)  	if (ret)  		return -EINVAL; -	ret = ttm_bo_reserve(&nvbo->bo, false, false, NULL); -	if (ret) -		goto error; - -	if (nvbo->bo.moving) -		ret = dma_fence_wait(nvbo->bo.moving, true); - -	ttm_bo_unreserve(&nvbo->bo); -	if (ret) -		goto error; - -	return ret; - -error: -	nouveau_bo_unpin(nvbo); -	return ret; +	return 0;  }  void nouveau_gem_prime_unpin(struct drm_gem_object *obj) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c index 030640bb3dca..ab3760e804b8 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c @@ -143,7 +143,7 @@ gf108_gr = {  	}  }; -const struct gf100_gr_fwif +static const struct gf100_gr_fwif  gf108_gr_fwif[] = {  	{ -1, gf100_gr_load, &gf108_gr },  	{ -1, gf100_gr_nofw, &gf108_gr }, diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c index 27a1c9923b09..eca067e78579 100644 --- a/drivers/gpu/drm/panel/panel-lvds.c +++ b/drivers/gpu/drm/panel/panel-lvds.c @@ -30,9 +30,9 @@ struct panel_lvds {  	const char *label;  	unsigned int width;  	unsigned int height; -	struct videomode video_mode; +	struct drm_display_mode dmode; +	u32 bus_flags;  	unsigned int bus_format; -	bool data_mirror;  	struct regulator *supply; @@ -87,21 +87,18 @@ static int panel_lvds_get_modes(struct drm_panel *panel,  	struct panel_lvds *lvds = to_panel_lvds(panel);  	struct drm_display_mode *mode; -	mode = drm_mode_create(connector->dev); +	mode = drm_mode_duplicate(connector->dev, &lvds->dmode);  	if (!mode)  		return 0; -	drm_display_mode_from_videomode(&lvds->video_mode, mode);  	mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;  	drm_mode_probed_add(connector, mode); -	connector->display_info.width_mm = lvds->width; -	connector->display_info.height_mm = lvds->height; +	connector->display_info.width_mm = lvds->dmode.width_mm; +	connector->display_info.height_mm = lvds->dmode.height_mm;  	drm_display_info_set_bus_formats(&connector->display_info,  					 &lvds->bus_format, 1); -	connector->display_info.bus_flags = lvds->data_mirror -					  ? DRM_BUS_FLAG_DATA_LSB_TO_MSB -					  : DRM_BUS_FLAG_DATA_MSB_TO_LSB; +	connector->display_info.bus_flags = lvds->bus_flags;  	drm_connector_set_panel_orientation(connector, lvds->orientation);  	return 1; @@ -116,7 +113,6 @@ static const struct drm_panel_funcs panel_lvds_funcs = {  static int panel_lvds_parse_dt(struct panel_lvds *lvds)  {  	struct device_node *np = lvds->dev->of_node; -	struct display_timing timing;  	int ret;  	ret = of_drm_get_panel_orientation(np, &lvds->orientation); @@ -125,23 +121,20 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)  		return ret;  	} -	ret = of_get_display_timing(np, "panel-timing", &timing); +	ret = of_get_drm_panel_display_mode(np, &lvds->dmode, &lvds->bus_flags);  	if (ret < 0) {  		dev_err(lvds->dev, "%pOF: problems parsing panel-timing (%d)\n",  			np, ret);  		return ret;  	} -	videomode_from_timing(&timing, &lvds->video_mode); - -	ret = of_property_read_u32(np, "width-mm", &lvds->width); -	if (ret < 0) { +	if (lvds->dmode.width_mm == 0) {  		dev_err(lvds->dev, "%pOF: invalid or missing %s DT property\n",  			np, "width-mm");  		return -ENODEV;  	} -	ret = of_property_read_u32(np, "height-mm", &lvds->height); -	if (ret < 0) { + +	if (lvds->dmode.height_mm == 0) {  		dev_err(lvds->dev, "%pOF: invalid or missing %s DT property\n",  			np, "height-mm");  		return -ENODEV; @@ -158,7 +151,9 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)  	lvds->bus_format = ret; -	lvds->data_mirror = of_property_read_bool(np, "data-mirror"); +	lvds->bus_flags |= of_property_read_bool(np, "data-mirror") ? +			   DRM_BUS_FLAG_DATA_LSB_TO_MSB : +			   DRM_BUS_FLAG_DATA_MSB_TO_LSB;  	return 0;  } diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 94b6f0a19c83..7fcbc2a5b6cd 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -316,7 +316,8 @@ panfrost_ioctl_wait_bo(struct drm_device *dev, void *data,  	if (!gem_obj)  		return -ENOENT; -	ret = dma_resv_wait_timeout(gem_obj->resv, true, true, timeout); +	ret = dma_resv_wait_timeout(gem_obj->resv, DMA_RESV_USAGE_READ, +				    true, timeout);  	if (!ret)  		ret = timeout ? -ETIMEDOUT : -EBUSY; diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index c34114560e49..fda5871aebe3 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -268,7 +268,7 @@ static void panfrost_attach_object_fences(struct drm_gem_object **bos,  	int i;  	for (i = 0; i < bo_count; i++) -		dma_resv_add_excl_fence(bos[i]->resv, fence); +		dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);  }  int panfrost_job_push(struct panfrost_job *job) diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c index 6a36b0fd845c..2d9ed3b94574 100644 --- a/drivers/gpu/drm/qxl/qxl_debugfs.c +++ b/drivers/gpu/drm/qxl/qxl_debugfs.c @@ -61,7 +61,8 @@ qxl_debugfs_buffers_info(struct seq_file *m, void *data)  		struct dma_fence *fence;  		int rel = 0; -		dma_resv_iter_begin(&cursor, bo->tbo.base.resv, true); +		dma_resv_iter_begin(&cursor, bo->tbo.base.resv, +				    DMA_RESV_USAGE_BOOKKEEP);  		dma_resv_for_each_fence_unlocked(&cursor, fence) {  			if (dma_resv_iter_is_restarted(&cursor))  				rel = 0; diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c index cde1e8ddaeaa..368d26da0d6a 100644 --- a/drivers/gpu/drm/qxl/qxl_release.c +++ b/drivers/gpu/drm/qxl/qxl_release.c @@ -429,7 +429,8 @@ void qxl_release_fence_buffer_objects(struct qxl_release *release)  	list_for_each_entry(entry, &release->bos, head) {  		bo = entry->bo; -		dma_resv_add_shared_fence(bo->base.resv, &release->base); +		dma_resv_add_fence(bo->base.resv, &release->base, +				   DMA_RESV_USAGE_READ);  		ttm_bo_move_to_lru_tail_unlocked(bo);  		dma_resv_unlock(bo->base.resv);  	} diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 9ba871bd19b1..ee95001e6b5e 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -222,41 +222,14 @@ void qxl_ttm_fini(struct qxl_device *qdev)  	DRM_INFO("qxl: ttm finalized\n");  } -#define QXL_DEBUGFS_MEM_TYPES 2 - -#if defined(CONFIG_DEBUG_FS) -static int qxl_mm_dump_table(struct seq_file *m, void *data) -{ -	struct drm_info_node *node = (struct drm_info_node *)m->private; -	struct ttm_resource_manager *man = (struct ttm_resource_manager *)node->info_ent->data; -	struct drm_printer p = drm_seq_file_printer(m); - -	ttm_resource_manager_debug(man, &p); -	return 0; -} -#endif -  void qxl_ttm_debugfs_init(struct qxl_device *qdev)  {  #if defined(CONFIG_DEBUG_FS) -	static struct drm_info_list qxl_mem_types_list[QXL_DEBUGFS_MEM_TYPES]; -	static char qxl_mem_types_names[QXL_DEBUGFS_MEM_TYPES][32]; -	unsigned int i; - -	for (i = 0; i < QXL_DEBUGFS_MEM_TYPES; i++) { -		if (i == 0) -			sprintf(qxl_mem_types_names[i], "qxl_mem_mm"); -		else -			sprintf(qxl_mem_types_names[i], "qxl_surf_mm"); -		qxl_mem_types_list[i].name = qxl_mem_types_names[i]; -		qxl_mem_types_list[i].show = &qxl_mm_dump_table; -		qxl_mem_types_list[i].driver_features = 0; -		if (i == 0) -			qxl_mem_types_list[i].data = ttm_manager_type(&qdev->mman.bdev, TTM_PL_VRAM); -		else -			qxl_mem_types_list[i].data = ttm_manager_type(&qdev->mman.bdev, TTM_PL_PRIV); - -	} -	qxl_debugfs_add_files(qdev, qxl_mem_types_list, i); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&qdev->mman.bdev, +							     TTM_PL_VRAM), +					    qdev->ddev.primary->debugfs_root, "qxl_mem_mm"); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&qdev->mman.bdev, +							     TTM_PL_PRIV), +					    qdev->ddev.primary->debugfs_root, "qxl_surf_mm");  #endif  } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index f60e826cd292..57ff2b723c87 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -533,7 +533,8 @@ static int radeon_crtc_page_flip_target(struct drm_crtc *crtc,  		DRM_ERROR("failed to pin new rbo buffer before flip\n");  		goto cleanup;  	} -	r = dma_resv_get_singleton(new_rbo->tbo.base.resv, false, &work->fence); +	r = dma_resv_get_singleton(new_rbo->tbo.base.resv, DMA_RESV_USAGE_WRITE, +				   &work->fence);  	if (r) {  		radeon_bo_unreserve(new_rbo);  		DRM_ERROR("failed to get new rbo buffer fences\n"); diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index f563284a7fac..8c01a7f0e027 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -162,7 +162,9 @@ static int radeon_gem_set_domain(struct drm_gem_object *gobj,  	}  	if (domain == RADEON_GEM_DOMAIN_CPU) {  		/* Asking for cpu access wait for object idle */ -		r = dma_resv_wait_timeout(robj->tbo.base.resv, true, true, 30 * HZ); +		r = dma_resv_wait_timeout(robj->tbo.base.resv, +					  DMA_RESV_USAGE_BOOKKEEP, +					  true, 30 * HZ);  		if (!r)  			r = -EBUSY; @@ -524,7 +526,7 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,  	}  	robj = gem_to_radeon_bo(gobj); -	r = dma_resv_test_signaled(robj->tbo.base.resv, true); +	r = dma_resv_test_signaled(robj->tbo.base.resv, DMA_RESV_USAGE_READ);  	if (r == 0)  		r = -EBUSY;  	else @@ -553,7 +555,8 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,  	}  	robj = gem_to_radeon_bo(gobj); -	ret = dma_resv_wait_timeout(robj->tbo.base.resv, true, true, 30 * HZ); +	ret = dma_resv_wait_timeout(robj->tbo.base.resv, DMA_RESV_USAGE_READ, +				    true, 30 * HZ);  	if (ret == 0)  		r = -EBUSY;  	else if (ret < 0) diff --git a/drivers/gpu/drm/radeon/radeon_mn.c b/drivers/gpu/drm/radeon/radeon_mn.c index 9fa88549c89e..29fe8423bd90 100644 --- a/drivers/gpu/drm/radeon/radeon_mn.c +++ b/drivers/gpu/drm/radeon/radeon_mn.c @@ -66,8 +66,8 @@ static bool radeon_mn_invalidate(struct mmu_interval_notifier *mn,  		return true;  	} -	r = dma_resv_wait_timeout(bo->tbo.base.resv, true, false, -				  MAX_SCHEDULE_TIMEOUT); +	r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, +				  false, MAX_SCHEDULE_TIMEOUT);  	if (r <= 0)  		DRM_ERROR("(%ld) failed to wait for user bo\n", r); diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 7ffd2e90f325..6c4a6802ca96 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -219,7 +219,12 @@ int radeon_bo_create(struct radeon_device *rdev,  int radeon_bo_kmap(struct radeon_bo *bo, void **ptr)  {  	bool is_iomem; -	int r; +	long r; + +	r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_KERNEL, +				  false, MAX_SCHEDULE_TIMEOUT); +	if (r < 0) +		return r;  	if (bo->kptr) {  		if (ptr) { @@ -791,8 +796,6 @@ void radeon_bo_fence(struct radeon_bo *bo, struct radeon_fence *fence,  		return;  	} -	if (shared) -		dma_resv_add_shared_fence(resv, &fence->base); -	else -		dma_resv_add_excl_fence(resv, &fence->base); +	dma_resv_add_fence(resv, &fence->base, shared ? +			   DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE);  } diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c index 4a90807351e7..42a87948e28c 100644 --- a/drivers/gpu/drm/radeon/radeon_prime.c +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -77,19 +77,9 @@ int radeon_gem_prime_pin(struct drm_gem_object *obj)  	/* pin buffer into GTT */  	ret = radeon_bo_pin(bo, RADEON_GEM_DOMAIN_GTT, NULL); -	if (unlikely(ret)) -		goto error; - -	if (bo->tbo.moving) { -		ret = dma_fence_wait(bo->tbo.moving, false); -		if (unlikely(ret)) { -			radeon_bo_unpin(bo); -			goto error; -		} -	} - -	bo->prime_shared_count++; -error: +	if (likely(ret == 0)) +		bo->prime_shared_count++; +  	radeon_bo_unreserve(bo);  	return ret;  } diff --git a/drivers/gpu/drm/radeon/radeon_sync.c b/drivers/gpu/drm/radeon/radeon_sync.c index b991ba1bcd51..49bbb2266c0f 100644 --- a/drivers/gpu/drm/radeon/radeon_sync.c +++ b/drivers/gpu/drm/radeon/radeon_sync.c @@ -96,7 +96,7 @@ int radeon_sync_resv(struct radeon_device *rdev,  	struct dma_fence *f;  	int r = 0; -	dma_resv_for_each_fence(&cursor, resv, shared, f) { +	dma_resv_for_each_fence(&cursor, resv, dma_resv_usage_rw(shared), f) {  		fence = to_radeon_fence(f);  		if (fence && fence->rdev == rdev)  			radeon_sync_fence(sync, fence); diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 44594d16611f..d33fec488713 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -781,17 +781,6 @@ void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size)  #if defined(CONFIG_DEBUG_FS) -static int radeon_mm_vram_dump_table_show(struct seq_file *m, void *unused) -{ -	struct radeon_device *rdev = (struct radeon_device *)m->private; -	struct ttm_resource_manager *man = ttm_manager_type(&rdev->mman.bdev, -							    TTM_PL_VRAM); -	struct drm_printer p = drm_seq_file_printer(m); - -	ttm_resource_manager_debug(man, &p); -	return 0; -} -  static int radeon_ttm_page_pool_show(struct seq_file *m, void *data)  {  	struct radeon_device *rdev = (struct radeon_device *)m->private; @@ -799,19 +788,6 @@ static int radeon_ttm_page_pool_show(struct seq_file *m, void *data)  	return ttm_pool_debugfs(&rdev->mman.bdev.pool, m);  } -static int radeon_mm_gtt_dump_table_show(struct seq_file *m, void *unused) -{ -	struct radeon_device *rdev = (struct radeon_device *)m->private; -	struct ttm_resource_manager *man = ttm_manager_type(&rdev->mman.bdev, -							    TTM_PL_TT); -	struct drm_printer p = drm_seq_file_printer(m); - -	ttm_resource_manager_debug(man, &p); -	return 0; -} - -DEFINE_SHOW_ATTRIBUTE(radeon_mm_vram_dump_table); -DEFINE_SHOW_ATTRIBUTE(radeon_mm_gtt_dump_table);  DEFINE_SHOW_ATTRIBUTE(radeon_ttm_page_pool);  static int radeon_ttm_vram_open(struct inode *inode, struct file *filep) @@ -930,15 +906,15 @@ static void radeon_ttm_debugfs_init(struct radeon_device *rdev)  	debugfs_create_file("radeon_vram", 0444, root, rdev,  			    &radeon_ttm_vram_fops); -  	debugfs_create_file("radeon_gtt", 0444, root, rdev,  			    &radeon_ttm_gtt_fops); - -	debugfs_create_file("radeon_vram_mm", 0444, root, rdev, -			    &radeon_mm_vram_dump_table_fops); -	debugfs_create_file("radeon_gtt_mm", 0444, root, rdev, -			    &radeon_mm_gtt_dump_table_fops);  	debugfs_create_file("ttm_page_pool", 0444, root, rdev,  			    &radeon_ttm_page_pool_fops); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&rdev->mman.bdev, +							     TTM_PL_VRAM), +					    root, "radeon_vram_mm"); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&rdev->mman.bdev, +							     TTM_PL_TT), +					    root, "radeon_gtt_mm");  #endif  } diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index bc0f44299bb9..a2cda184b2b2 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -470,24 +470,16 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo,  	int32_t *msg, msg_type, handle;  	unsigned img_size = 0;  	void *ptr; -	long r; -	int i; +	int i, r;  	if (offset & 0x3F) {  		DRM_ERROR("UVD messages must be 64 byte aligned!\n");  		return -EINVAL;  	} -	r = dma_resv_wait_timeout(bo->tbo.base.resv, false, false, -				  MAX_SCHEDULE_TIMEOUT); -	if (r <= 0) { -		DRM_ERROR("Failed waiting for UVD message (%ld)!\n", r); -		return r ? r : -ETIME; -	} -  	r = radeon_bo_kmap(bo, &ptr);  	if (r) { -		DRM_ERROR("Failed mapping the UVD message (%ld)!\n", r); +		DRM_ERROR("Failed mapping the UVD message (%d)!\n", r);  		return r;  	} diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index c5660b066554..76fd2904c7c6 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -705,7 +705,8 @@ int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job,  	dma_resv_assert_held(obj->resv); -	dma_resv_for_each_fence(&cursor, obj->resv, write, fence) { +	dma_resv_for_each_fence(&cursor, obj->resv, dma_resv_usage_rw(write), +				fence) {  		/* Make sure to grab an additional ref on the added fence */  		dma_fence_get(fence);  		ret = drm_sched_job_add_dependency(job, fence); diff --git a/drivers/gpu/drm/solomon/Kconfig b/drivers/gpu/drm/solomon/Kconfig index 6230369505c9..e170716d976b 100644 --- a/drivers/gpu/drm/solomon/Kconfig +++ b/drivers/gpu/drm/solomon/Kconfig @@ -5,9 +5,9 @@ config DRM_SSD130X  	select DRM_GEM_SHMEM_HELPER  	select DRM_KMS_HELPER  	help -	  DRM driver for the SSD1305, SSD1306, SSD1307 and SSD1309 Solomon -	  OLED controllers. This is only for the core driver, a driver for -	  the appropriate bus transport in your chip also must be selected. +	  DRM driver for the SSD130x Solomon and SINO WEALTH SH110x OLED +	  controllers. This is only for the core driver, a driver for the +	  appropriate bus transport in your chip also must be selected.  	  If M is selected the module will be called ssd130x. @@ -16,6 +16,16 @@ config DRM_SSD130X_I2C  	depends on DRM_SSD130X && I2C  	select REGMAP_I2C  	help -	  Say Y here if the SSD130x OLED display is connected via I2C bus. +	  Say Y here if the SSD130x or SH110x OLED display is connected via +	  I2C bus.  	  If M is selected the module will be called ssd130x-i2c. + +config DRM_SSD130X_SPI +	tristate "DRM support for Solomon SSD130X OLED displays (SPI bus)" +	depends on DRM_SSD130X && SPI +	select REGMAP +	help +	  Say Y here if the SSD130x OLED display is connected via SPI bus. + +	  If M is selected the module will be called ssd130x-spi. diff --git a/drivers/gpu/drm/solomon/Makefile b/drivers/gpu/drm/solomon/Makefile index 4bfc5acb0447..b5fc792257d7 100644 --- a/drivers/gpu/drm/solomon/Makefile +++ b/drivers/gpu/drm/solomon/Makefile @@ -1,2 +1,3 @@  obj-$(CONFIG_DRM_SSD130X)	+= ssd130x.o  obj-$(CONFIG_DRM_SSD130X_I2C)	+= ssd130x-i2c.o +obj-$(CONFIG_DRM_SSD130X_SPI)	+= ssd130x-spi.o diff --git a/drivers/gpu/drm/solomon/ssd130x-i2c.c b/drivers/gpu/drm/solomon/ssd130x-i2c.c index 3126aeda4ced..d6835ec71c39 100644 --- a/drivers/gpu/drm/solomon/ssd130x-i2c.c +++ b/drivers/gpu/drm/solomon/ssd130x-i2c.c @@ -53,48 +53,43 @@ static void ssd130x_i2c_shutdown(struct i2c_client *client)  	ssd130x_shutdown(ssd130x);  } -static struct ssd130x_deviceinfo ssd130x_ssd1305_deviceinfo = { -	.default_vcomh = 0x34, -	.default_dclk_div = 1, -	.default_dclk_frq = 7, -}; - -static struct ssd130x_deviceinfo ssd130x_ssd1306_deviceinfo = { -	.default_vcomh = 0x20, -	.default_dclk_div = 1, -	.default_dclk_frq = 8, -	.need_chargepump = 1, -}; - -static struct ssd130x_deviceinfo ssd130x_ssd1307_deviceinfo = { -	.default_vcomh = 0x20, -	.default_dclk_div = 2, -	.default_dclk_frq = 12, -	.need_pwm = 1, -}; - -static struct ssd130x_deviceinfo ssd130x_ssd1309_deviceinfo = { -	.default_vcomh = 0x34, -	.default_dclk_div = 1, -	.default_dclk_frq = 10, -}; -  static const struct of_device_id ssd130x_of_match[] = {  	{ +		.compatible = "sinowealth,sh1106", +		.data = &ssd130x_variants[SH1106_ID], +	}, +	{ +		.compatible = "solomon,ssd1305", +		.data = &ssd130x_variants[SSD1305_ID], +	}, +	{ +		.compatible = "solomon,ssd1306", +		.data = &ssd130x_variants[SSD1306_ID], +	}, +	{ +		.compatible = "solomon,ssd1307", +		.data = &ssd130x_variants[SSD1307_ID], +	}, +	{ +		.compatible = "solomon,ssd1309", +		.data = &ssd130x_variants[SSD1309_ID], +	}, +	/* Deprecated but kept for backward compatibility */ +	{  		.compatible = "solomon,ssd1305fb-i2c", -		.data = &ssd130x_ssd1305_deviceinfo, +		.data = &ssd130x_variants[SSD1305_ID],  	},  	{  		.compatible = "solomon,ssd1306fb-i2c", -		.data = &ssd130x_ssd1306_deviceinfo, +		.data = &ssd130x_variants[SSD1306_ID],  	},  	{  		.compatible = "solomon,ssd1307fb-i2c", -		.data = &ssd130x_ssd1307_deviceinfo, +		.data = &ssd130x_variants[SSD1307_ID],  	},  	{  		.compatible = "solomon,ssd1309fb-i2c", -		.data = &ssd130x_ssd1309_deviceinfo, +		.data = &ssd130x_variants[SSD1309_ID],  	},  	{ /* sentinel */ }  }; @@ -114,3 +109,4 @@ module_i2c_driver(ssd130x_i2c_driver);  MODULE_DESCRIPTION(DRIVER_DESC);  MODULE_AUTHOR("Javier Martinez Canillas <javierm@redhat.com>");  MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(DRM_SSD130X); diff --git a/drivers/gpu/drm/solomon/ssd130x-spi.c b/drivers/gpu/drm/solomon/ssd130x-spi.c new file mode 100644 index 000000000000..43722adab1f8 --- /dev/null +++ b/drivers/gpu/drm/solomon/ssd130x-spi.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DRM driver for Solomon SSD130X OLED displays (SPI bus) + * + * Copyright 2022 Red Hat Inc. + * Authors: Javier Martinez Canillas <javierm@redhat.com> + */ +#include <linux/spi/spi.h> +#include <linux/module.h> + +#include "ssd130x.h" + +#define DRIVER_NAME	"ssd130x-spi" +#define DRIVER_DESC	"DRM driver for Solomon SSD130X OLED displays (SPI)" + +struct ssd130x_spi_transport { +	struct spi_device *spi; +	struct gpio_desc *dc; +}; + +static const struct regmap_config ssd130x_spi_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +}; + +/* + * The regmap bus .write handler, it is just a wrapper around spi_write() + * but toggling the Data/Command control pin (D/C#). Since for 4-wire SPI + * a D/C# pin is used, in contrast with I2C where a control byte is sent, + * prior to every data byte, that contains a bit with the D/C# value. + * + * These control bytes are considered registers by the ssd130x core driver + * and can be used by the ssd130x SPI driver to determine if the data sent + * is for a command register or for the Graphic Display Data RAM (GDDRAM). + */ +static int ssd130x_spi_write(void *context, const void *data, size_t count) +{ +	struct ssd130x_spi_transport *t = context; +	struct spi_device *spi = t->spi; +	const u8 *reg = data; + +	if (*reg == SSD130X_COMMAND) +		gpiod_set_value_cansleep(t->dc, 0); + +	if (*reg == SSD130X_DATA) +		gpiod_set_value_cansleep(t->dc, 1); + +	/* Remove control byte since is not used in a 4-wire SPI interface */ +	return spi_write(spi, reg + 1, count - 1); +} + +/* The ssd130x driver does not read registers but regmap expects a .read */ +static int ssd130x_spi_read(void *context, const void *reg, size_t reg_size, +			    void *val, size_t val_size) +{ +	return -EOPNOTSUPP; +} + +/* + * A custom bus is needed due the special write that toggles a D/C# pin, + * another option could be to just have a .reg_write() callback but that + * will prevent to do data writes in bulk. + * + * Once the regmap API is extended to support defining a bulk write handler + * in the struct regmap_config, this can be simplified and the bus dropped. + */ +static struct regmap_bus regmap_ssd130x_spi_bus = { +	.write = ssd130x_spi_write, +	.read = ssd130x_spi_read, +}; + +static int ssd130x_spi_probe(struct spi_device *spi) +{ +	struct ssd130x_spi_transport *t; +	struct ssd130x_device *ssd130x; +	struct regmap *regmap; +	struct gpio_desc *dc; +	struct device *dev = &spi->dev; + +	dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW); +	if (IS_ERR(dc)) +		return dev_err_probe(dev, PTR_ERR(dc), +				     "Failed to get dc gpio\n"); + +	t = devm_kzalloc(dev, sizeof(*t), GFP_KERNEL); +	if (!t) +		return dev_err_probe(dev, -ENOMEM, +				     "Failed to allocate SPI transport data\n"); + +	t->spi = spi; +	t->dc = dc; + +	regmap = devm_regmap_init(dev, ®map_ssd130x_spi_bus, t, +				  &ssd130x_spi_regmap_config); +	if (IS_ERR(regmap)) +		return PTR_ERR(regmap); + +	ssd130x = ssd130x_probe(dev, regmap); +	if (IS_ERR(ssd130x)) +		return PTR_ERR(ssd130x); + +	spi_set_drvdata(spi, ssd130x); + +	return 0; +} + +static void ssd130x_spi_remove(struct spi_device *spi) +{ +	struct ssd130x_device *ssd130x = spi_get_drvdata(spi); + +	ssd130x_remove(ssd130x); +} + +static void ssd130x_spi_shutdown(struct spi_device *spi) +{ +	struct ssd130x_device *ssd130x = spi_get_drvdata(spi); + +	ssd130x_shutdown(ssd130x); +} + +static const struct of_device_id ssd130x_of_match[] = { +	{ +		.compatible = "sinowealth,sh1106", +		.data = &ssd130x_variants[SH1106_ID], +	}, +	{ +		.compatible = "solomon,ssd1305", +		.data = &ssd130x_variants[SSD1305_ID], +	}, +	{ +		.compatible = "solomon,ssd1306", +		.data = &ssd130x_variants[SSD1306_ID], +	}, +	{ +		.compatible = "solomon,ssd1307", +		.data = &ssd130x_variants[SSD1307_ID], +	}, +	{ +		.compatible = "solomon,ssd1309", +		.data = &ssd130x_variants[SSD1309_ID], +	}, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ssd130x_of_match); + +/* + * The SPI core always reports a MODALIAS uevent of the form "spi:<dev>", even + * if the device was registered via OF. This means that the module will not be + * auto loaded, unless it contains an alias that matches the MODALIAS reported. + * + * To workaround this issue, add a SPI device ID table. Even when this should + * not be needed for this driver to match the registered SPI devices. + */ +static const struct spi_device_id ssd130x_spi_table[] = { +	{ "sh1106",  SH1106_ID }, +	{ "ssd1305", SSD1305_ID }, +	{ "ssd1306", SSD1306_ID }, +	{ "ssd1307", SSD1307_ID }, +	{ "ssd1309", SSD1309_ID }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, ssd130x_spi_table); + +static struct spi_driver ssd130x_spi_driver = { +	.driver = { +		.name = DRIVER_NAME, +		.of_match_table = ssd130x_of_match, +	}, +	.probe = ssd130x_spi_probe, +	.remove = ssd130x_spi_remove, +	.shutdown = ssd130x_spi_shutdown, +}; +module_spi_driver(ssd130x_spi_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Javier Martinez Canillas <javierm@redhat.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(DRM_SSD130X); diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 38b6c2c14f53..ba2de93d00f0 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -39,9 +39,8 @@  #define DRIVER_MAJOR	1  #define DRIVER_MINOR	0 -#define SSD130X_DATA				0x40 -#define SSD130X_COMMAND				0x80 - +#define SSD130X_PAGE_COL_START_LOW		0x00 +#define SSD130X_PAGE_COL_START_HIGH		0x10  #define SSD130X_SET_ADDRESS_MODE		0x20  #define SSD130X_SET_COL_RANGE			0x21  #define SSD130X_SET_PAGE_RANGE			0x22 @@ -61,6 +60,11 @@  #define SSD130X_SET_COM_PINS_CONFIG		0xda  #define SSD130X_SET_VCOMH			0xdb +#define SSD130X_PAGE_COL_START_MASK		GENMASK(3, 0) +#define SSD130X_PAGE_COL_START_HIGH_SET(val)	FIELD_PREP(SSD130X_PAGE_COL_START_MASK, (val) >> 4) +#define SSD130X_PAGE_COL_START_LOW_SET(val)	FIELD_PREP(SSD130X_PAGE_COL_START_MASK, (val)) +#define SSD130X_START_PAGE_ADDRESS_MASK		GENMASK(2, 0) +#define SSD130X_START_PAGE_ADDRESS_SET(val)	FIELD_PREP(SSD130X_START_PAGE_ADDRESS_MASK, (val))  #define SSD130X_SET_SEG_REMAP_MASK		GENMASK(0, 0)  #define SSD130X_SET_SEG_REMAP_SET(val)		FIELD_PREP(SSD130X_SET_SEG_REMAP_MASK, (val))  #define SSD130X_SET_COM_SCAN_DIR_MASK		GENMASK(3, 3) @@ -87,6 +91,38 @@  #define MAX_CONTRAST 255 +const struct ssd130x_deviceinfo ssd130x_variants[] = { +	[SH1106_ID] = { +		.default_vcomh = 0x40, +		.default_dclk_div = 1, +		.default_dclk_frq = 5, +		.page_mode_only = 1, +	}, +	[SSD1305_ID] = { +		.default_vcomh = 0x34, +		.default_dclk_div = 1, +		.default_dclk_frq = 7, +	}, +	[SSD1306_ID] = { +		.default_vcomh = 0x20, +		.default_dclk_div = 1, +		.default_dclk_frq = 8, +		.need_chargepump = 1, +	}, +	[SSD1307_ID] = { +		.default_vcomh = 0x20, +		.default_dclk_div = 2, +		.default_dclk_frq = 12, +		.need_pwm = 1, +	}, +	[SSD1309_ID] = { +		.default_vcomh = 0x34, +		.default_dclk_div = 1, +		.default_dclk_frq = 10, +	} +}; +EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X); +  static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm)  {  	return container_of(drm, struct ssd130x_device, drm); @@ -130,6 +166,7 @@ out_end:  	return ret;  } +/* Set address range for horizontal/vertical addressing modes */  static int ssd130x_set_col_range(struct ssd130x_device *ssd130x,  				 u8 col_start, u8 cols)  { @@ -166,6 +203,26 @@ static int ssd130x_set_page_range(struct ssd130x_device *ssd130x,  	return 0;  } +/* Set page and column start address for page addressing mode */ +static int ssd130x_set_page_pos(struct ssd130x_device *ssd130x, +				u8 page_start, u8 col_start) +{ +	int ret; +	u32 page, col_low, col_high; + +	page = SSD130X_START_PAGE_ADDRESS | +	       SSD130X_START_PAGE_ADDRESS_SET(page_start); +	col_low = SSD130X_PAGE_COL_START_LOW | +		  SSD130X_PAGE_COL_START_LOW_SET(col_start); +	col_high = SSD130X_PAGE_COL_START_HIGH | +		   SSD130X_PAGE_COL_START_HIGH_SET(col_start); +	ret = ssd130x_write_cmd(ssd130x, 3, page, col_low, col_high); +	if (ret < 0) +		return ret; + +	return 0; +} +  static int ssd130x_pwm_enable(struct ssd130x_device *ssd130x)  {  	struct device *dev = ssd130x->dev; @@ -342,6 +399,11 @@ static int ssd130x_init(struct ssd130x_device *ssd130x)  		}  	} +	/* Switch to page addressing mode */ +	if (ssd130x->page_address_mode) +		return ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_ADDRESS_MODE, +					 SSD130X_SET_ADDRESS_MODE_PAGE); +  	/* Switch to horizontal addressing mode */  	return ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_ADDRESS_MODE,  				 SSD130X_SET_ADDRESS_MODE_HORIZONTAL); @@ -396,13 +458,16 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf,  	 *  (5) A4 B4 C4 D4 E4 F4 G4 H4  	 */ -	ret = ssd130x_set_col_range(ssd130x, ssd130x->col_offset + x, width); -	if (ret < 0) -		goto out_free; +	if (!ssd130x->page_address_mode) { +		/* Set address range for horizontal addressing mode */ +		ret = ssd130x_set_col_range(ssd130x, ssd130x->col_offset + x, width); +		if (ret < 0) +			goto out_free; -	ret = ssd130x_set_page_range(ssd130x, ssd130x->page_offset + y / 8, pages); -	if (ret < 0) -		goto out_free; +		ret = ssd130x_set_page_range(ssd130x, ssd130x->page_offset + y / 8, pages); +		if (ret < 0) +			goto out_free; +	}  	for (i = 0; i < pages; i++) {  		int m = 8; @@ -421,9 +486,29 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf,  			}  			data_array[array_idx++] = data;  		} + +		/* +		 * In page addressing mode, the start address needs to be reset, +		 * and each page then needs to be written out separately. +		 */ +		if (ssd130x->page_address_mode) { +			ret = ssd130x_set_page_pos(ssd130x, +						   ssd130x->page_offset + i, +						   ssd130x->col_offset + x); +			if (ret < 0) +				goto out_free; + +			ret = ssd130x_write_data(ssd130x, data_array, width); +			if (ret < 0) +				goto out_free; + +			array_idx = 0; +		}  	} -	ret = ssd130x_write_data(ssd130x, data_array, width * pages); +	/* Write out update in one go if we aren't using page addressing mode */ +	if (!ssd130x->page_address_mode) +		ret = ssd130x_write_data(ssd130x, data_array, width * pages);  out_free:  	kfree(data_array); @@ -806,6 +891,9 @@ struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap)  	ssd130x->regmap = regmap;  	ssd130x->device_info = device_get_match_data(dev); +	if (ssd130x->device_info->page_mode_only) +		ssd130x->page_address_mode = 1; +  	ssd130x_parse_properties(ssd130x);  	ret = ssd130x_get_resources(ssd130x); diff --git a/drivers/gpu/drm/solomon/ssd130x.h b/drivers/gpu/drm/solomon/ssd130x.h index cd21cdccb566..d14f78c2eb07 100644 --- a/drivers/gpu/drm/solomon/ssd130x.h +++ b/drivers/gpu/drm/solomon/ssd130x.h @@ -18,12 +18,25 @@  #include <linux/regmap.h> +#define SSD130X_DATA				0x40 +#define SSD130X_COMMAND				0x80 + +enum ssd130x_variants { +	SH1106_ID, +	SSD1305_ID, +	SSD1306_ID, +	SSD1307_ID, +	SSD1309_ID, +	NR_SSD130X_VARIANTS +}; +  struct ssd130x_deviceinfo {  	u32 default_vcomh;  	u32 default_dclk_div;  	u32 default_dclk_frq;  	int need_pwm;  	int need_chargepump; +	bool page_mode_only;  };  struct ssd130x_device { @@ -38,6 +51,7 @@ struct ssd130x_device {  	const struct ssd130x_deviceinfo *device_info; +	unsigned page_address_mode : 1;  	unsigned area_color_enable : 1;  	unsigned com_invdir : 1;  	unsigned com_lrremap : 1; @@ -69,6 +83,8 @@ struct ssd130x_device {  	u8 page_end;  }; +extern const struct ssd130x_deviceinfo ssd130x_variants[]; +  struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap);  int ssd130x_remove(struct ssd130x_device *ssd130x);  void ssd130x_shutdown(struct ssd130x_device *ssd130x); diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index 3db3768a3241..b58415f2e4d8 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -406,7 +406,7 @@ static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_gdp *gdp)  		    (hw_nvn != gdp->node_list[i].top_field_paddr))  			return &gdp->node_list[i]; -	/* in hazardious cases restart with the first node */ +	/* in hazardous cases restart with the first node */  	DRM_ERROR("inconsistent NVN for %s: 0x%08X\n",  			sti_plane_to_str(&gdp->plane), hw_nvn); diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index f3ace11209dd..b3fbee7eac11 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -183,7 +183,7 @@ void hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset)  	writel(val, hdmi->regs + offset);  } -/** +/*   * HDMI interrupt handler threaded   *   * @irq: irq number @@ -215,7 +215,7 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)  	return IRQ_HANDLED;  } -/** +/*   * HDMI interrupt handler   *   * @irq: irq number @@ -237,7 +237,7 @@ static irqreturn_t hdmi_irq(int irq, void *arg)  	return IRQ_WAKE_THREAD;  } -/** +/*   * Set hdmi active area depending on the drm display mode selected   *   * @hdmi: pointer on the hdmi internal structure @@ -258,7 +258,7 @@ static void hdmi_active_area(struct sti_hdmi *hdmi)  	hdmi_write(hdmi, ymax, HDMI_ACTIVE_VID_YMAX);  } -/** +/*   * Overall hdmi configuration   *   * @hdmi: pointer on the hdmi internal structure @@ -336,7 +336,7 @@ static void hdmi_infoframe_reset(struct sti_hdmi *hdmi,  		hdmi_write(hdmi, 0x0, pack_offset + i);  } -/** +/*   * Helper to concatenate infoframe in 32 bits word   *   * @ptr: pointer on the hdmi internal structure @@ -353,7 +353,7 @@ static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size)  	return value;  } -/** +/*   * Helper to write info frame   *   * @hdmi: pointer on the hdmi internal structure @@ -423,7 +423,7 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi,  	hdmi_write(hdmi, val, HDMI_SW_DI_CFG);  } -/** +/*   * Prepare and configure the AVI infoframe   *   * AVI infoframe are transmitted at least once per two video field and @@ -466,7 +466,7 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)  	return 0;  } -/** +/*   * Prepare and configure the AUDIO infoframe   *   * AUDIO infoframe are transmitted once per frame and @@ -551,7 +551,7 @@ static int hdmi_vendor_infoframe_config(struct sti_hdmi *hdmi)  #define HDMI_TIMEOUT_SWRESET  100   /*milliseconds */ -/** +/*   * Software reset of the hdmi subsystem   *   * @hdmi: pointer on the hdmi internal structure @@ -785,7 +785,7 @@ static void sti_hdmi_disable(struct drm_bridge *bridge)  	cec_notifier_set_phys_addr(hdmi->notifier, CEC_PHYS_ADDR_INVALID);  } -/** +/*   * sti_hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent   * clocks. None-coherent clocks means that audio and TMDS clocks have not the   * same source (drifts between clocks). In this case assumption is that CTS is @@ -892,7 +892,7 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge)  	if (clk_prepare_enable(hdmi->clk_tmds))  		DRM_ERROR("Failed to prepare/enable hdmi_tmds clk\n");  	if (clk_prepare_enable(hdmi->clk_phy)) -		DRM_ERROR("Failed to prepare/enable hdmi_rejec_pll clk\n"); +		DRM_ERROR("Failed to prepare/enable hdmi_rejection_pll clk\n");  	hdmi->enabled = true; diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 17cc050207f4..6bd45df8f5a7 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -869,8 +869,8 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)  	struct drm_device *ddev = crtc->dev;  	struct drm_connector_list_iter iter;  	struct drm_connector *connector = NULL; -	struct drm_encoder *encoder = NULL; -	struct drm_bridge *bridge = NULL; +	struct drm_encoder *encoder = NULL, *en_iter; +	struct drm_bridge *bridge = NULL, *br_iter;  	struct drm_display_mode *mode = &crtc->state->adjusted_mode;  	u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;  	u32 total_width, total_height; @@ -880,15 +880,19 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)  	int ret;  	/* get encoder from crtc */ -	drm_for_each_encoder(encoder, ddev) -		if (encoder->crtc == crtc) +	drm_for_each_encoder(en_iter, ddev) +		if (en_iter->crtc == crtc) { +			encoder = en_iter;  			break; +		}  	if (encoder) {  		/* get bridge from encoder */ -		list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) -			if (bridge->encoder == encoder) +		list_for_each_entry(br_iter, &encoder->bridge_chain, chain_node) +			if (br_iter->encoder == encoder) { +				bridge = br_iter;  				break; +			}  		/* Get the connector from encoder */  		drm_connector_list_iter_begin(ddev, &iter); diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index 5e2b0175df36..2860e6bff8b7 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -135,7 +135,7 @@ static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi,  	dw_hdmi_phy_gen2_txpwron(hdmi, 0);  	dw_hdmi_phy_gen2_pddq(hdmi, 1); -	dw_hdmi_phy_reset(hdmi); +	dw_hdmi_phy_gen2_reset(hdmi);  	dw_hdmi_phy_gen2_pddq(hdmi, 0); diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index 60b92df615aa..dae47853b728 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -2650,6 +2650,20 @@ static void dispc_init_errata(struct dispc_device *dispc)  	}  } +static void dispc_softreset(struct dispc_device *dispc) +{ +	u32 val; +	int ret = 0; + +	/* Soft reset */ +	REG_FLD_MOD(dispc, DSS_SYSCONFIG, 1, 1, 1); +	/* Wait for reset to complete */ +	ret = readl_poll_timeout(dispc->base_common + DSS_SYSSTATUS, +				 val, val & 1, 100, 5000); +	if (ret) +		dev_warn(dispc->dev, "failed to reset dispc\n"); +} +  int dispc_init(struct tidss_device *tidss)  {  	struct device *dev = tidss->dev; @@ -2709,6 +2723,10 @@ int dispc_init(struct tidss_device *tidss)  			return r;  	} +	/* K2G display controller does not support soft reset */ +	if (feat->subrev != DISPC_K2G) +		dispc_softreset(dispc); +  	for (i = 0; i < dispc->feat->num_vps; i++) {  		u32 gamma_size = dispc->feat->vp_feat.color.gamma_size;  		u32 *gamma_table; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index c49996cf25d0..75d308ec173d 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -223,7 +223,7 @@ static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo)  	struct dma_resv_iter cursor;  	struct dma_fence *fence; -	dma_resv_iter_begin(&cursor, resv, true); +	dma_resv_iter_begin(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP);  	dma_resv_for_each_fence_unlocked(&cursor, fence) {  		if (!fence->ops->signaled)  			dma_fence_enable_sw_signaling(fence); @@ -252,7 +252,7 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo,  	struct dma_resv *resv = &bo->base._resv;  	int ret; -	if (dma_resv_test_signaled(resv, true)) +	if (dma_resv_test_signaled(resv, DMA_RESV_USAGE_BOOKKEEP))  		ret = 0;  	else  		ret = -EBUSY; @@ -264,7 +264,8 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo,  			dma_resv_unlock(bo->base.resv);  		spin_unlock(&bo->bdev->lru_lock); -		lret = dma_resv_wait_timeout(resv, true, interruptible, +		lret = dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP, +					     interruptible,  					     30 * HZ);  		if (lret < 0) @@ -367,7 +368,8 @@ static void ttm_bo_release(struct kref *kref)  			/* Last resort, if we fail to allocate memory for the  			 * fences block for the BO to become idle  			 */ -			dma_resv_wait_timeout(bo->base.resv, true, false, +			dma_resv_wait_timeout(bo->base.resv, +					      DMA_RESV_USAGE_BOOKKEEP, false,  					      30 * HZ);  		} @@ -378,7 +380,7 @@ static void ttm_bo_release(struct kref *kref)  		ttm_mem_io_free(bdev, bo->resource);  	} -	if (!dma_resv_test_signaled(bo->base.resv, true) || +	if (!dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP) ||  	    !dma_resv_trylock(bo->base.resv)) {  		/* The BO is not idle, resurrect it for delayed destroy */  		ttm_bo_flush_all_fences(bo); @@ -416,7 +418,6 @@ static void ttm_bo_release(struct kref *kref)  	dma_resv_unlock(bo->base.resv);  	atomic_dec(&ttm_glob.bo_count); -	dma_fence_put(bo->moving);  	bo->destroy(bo);  } @@ -712,9 +713,8 @@ void ttm_bo_unpin(struct ttm_buffer_object *bo)  EXPORT_SYMBOL(ttm_bo_unpin);  /* - * Add the last move fence to the BO and reserve a new shared slot. We only use - * a shared slot to avoid unecessary sync and rely on the subsequent bo move to - * either stall or use an exclusive fence respectively set bo->moving. + * Add the last move fence to the BO as kernel dependency and reserve a new + * fence slot.   */  static int ttm_bo_add_move_fence(struct ttm_buffer_object *bo,  				 struct ttm_resource_manager *man, @@ -737,17 +737,11 @@ static int ttm_bo_add_move_fence(struct ttm_buffer_object *bo,  		return ret;  	} -	dma_resv_add_shared_fence(bo->base.resv, fence); +	dma_resv_add_fence(bo->base.resv, fence, DMA_RESV_USAGE_KERNEL);  	ret = dma_resv_reserve_fences(bo->base.resv, 1); -	if (unlikely(ret)) { -		dma_fence_put(fence); -		return ret; -	} - -	dma_fence_put(bo->moving); -	bo->moving = fence; -	return 0; +	dma_fence_put(fence); +	return ret;  }  /* @@ -949,7 +943,6 @@ int ttm_bo_init_reserved(struct ttm_device *bdev,  	bo->bdev = bdev;  	bo->type = type;  	bo->page_alignment = page_alignment; -	bo->moving = NULL;  	bo->pin_count = 0;  	bo->sg = sg;  	bo->bulk_move = NULL; @@ -1044,14 +1037,14 @@ int ttm_bo_wait(struct ttm_buffer_object *bo,  	long timeout = 15 * HZ;  	if (no_wait) { -		if (dma_resv_test_signaled(bo->base.resv, true)) +		if (dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP))  			return 0;  		else  			return -EBUSY;  	} -	timeout = dma_resv_wait_timeout(bo->base.resv, true, interruptible, -					timeout); +	timeout = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP, +					interruptible, timeout);  	if (timeout < 0)  		return timeout; diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 1b96b91bf81b..1cbfb00c1d65 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -228,7 +228,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,  	atomic_inc(&ttm_glob.bo_count);  	INIT_LIST_HEAD(&fbo->base.ddestroy); -	fbo->base.moving = NULL;  	drm_vma_node_reset(&fbo->base.base.vma_node);  	kref_init(&fbo->base.kref); @@ -500,14 +499,12 @@ static int ttm_bo_move_to_ghost(struct ttm_buffer_object *bo,  	 * operation has completed.  	 */ -	dma_fence_put(bo->moving); -	bo->moving = dma_fence_get(fence); -  	ret = ttm_buffer_object_transfer(bo, &ghost_obj);  	if (ret)  		return ret; -	dma_resv_add_excl_fence(&ghost_obj->base._resv, fence); +	dma_resv_add_fence(&ghost_obj->base._resv, fence, +			   DMA_RESV_USAGE_KERNEL);  	/**  	 * If we're not moving to fixed memory, the TTM object @@ -545,9 +542,6 @@ static void ttm_bo_move_pipeline_evict(struct ttm_buffer_object *bo,  	spin_unlock(&from->move_lock);  	ttm_resource_free(bo, &bo->resource); - -	dma_fence_put(bo->moving); -	bo->moving = dma_fence_get(fence);  }  int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, @@ -561,7 +555,7 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,  	struct ttm_resource_manager *man = ttm_manager_type(bdev, new_mem->mem_type);  	int ret = 0; -	dma_resv_add_excl_fence(bo->base.resv, fence); +	dma_resv_add_fence(bo->base.resv, fence, DMA_RESV_USAGE_KERNEL);  	if (!evict)  		ret = ttm_bo_move_to_ghost(bo, fence, man->use_tt);  	else if (!from->use_tt && pipeline) @@ -578,6 +572,21 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,  }  EXPORT_SYMBOL(ttm_bo_move_accel_cleanup); +void ttm_bo_move_sync_cleanup(struct ttm_buffer_object *bo, +			      struct ttm_resource *new_mem) +{ +	struct ttm_device *bdev = bo->bdev; +	struct ttm_resource_manager *man = ttm_manager_type(bdev, new_mem->mem_type); +	int ret; + +	ret = ttm_bo_wait_free_node(bo, man->use_tt); +	if (WARN_ON(ret)) +		return; + +	ttm_bo_assign_mem(bo, new_mem); +} +EXPORT_SYMBOL(ttm_bo_move_sync_cleanup); +  /**   * ttm_bo_pipeline_gutting - purge the contents of a bo   * @bo: The buffer object diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 08ba083a80d2..5b324f245265 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -46,17 +46,13 @@  static vm_fault_t ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo,  				struct vm_fault *vmf)  { -	vm_fault_t ret = 0; -	int err = 0; - -	if (likely(!bo->moving)) -		goto out_unlock; +	long err = 0;  	/*  	 * Quick non-stalling check for idle.  	 */ -	if (dma_fence_is_signaled(bo->moving)) -		goto out_clear; +	if (dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_KERNEL)) +		return 0;  	/*  	 * If possible, avoid waiting for GPU with mmap_lock @@ -64,34 +60,30 @@ static vm_fault_t ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo,  	 * is the first attempt.  	 */  	if (fault_flag_allow_retry_first(vmf->flags)) { -		ret = VM_FAULT_RETRY;  		if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT) -			goto out_unlock; +			return VM_FAULT_RETRY;  		ttm_bo_get(bo);  		mmap_read_unlock(vmf->vma->vm_mm); -		(void) dma_fence_wait(bo->moving, true); +		(void)dma_resv_wait_timeout(bo->base.resv, +					    DMA_RESV_USAGE_KERNEL, true, +					    MAX_SCHEDULE_TIMEOUT);  		dma_resv_unlock(bo->base.resv);  		ttm_bo_put(bo); -		goto out_unlock; +		return VM_FAULT_RETRY;  	}  	/*  	 * Ordinary wait.  	 */ -	err = dma_fence_wait(bo->moving, true); -	if (unlikely(err != 0)) { -		ret = (err != -ERESTARTSYS) ? VM_FAULT_SIGBUS : +	err = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_KERNEL, true, +				    MAX_SCHEDULE_TIMEOUT); +	if (unlikely(err < 0)) { +		return (err != -ERESTARTSYS) ? VM_FAULT_SIGBUS :  			VM_FAULT_NOPAGE; -		goto out_unlock;  	} -out_clear: -	dma_fence_put(bo->moving); -	bo->moving = NULL; - -out_unlock: -	return ret; +	return 0;  }  static unsigned long ttm_bo_io_mem_pfn(struct ttm_buffer_object *bo, diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index 789c645f004e..dbee34a058df 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -101,7 +101,7 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,  			continue;  		} -		num_fences = min(entry->num_shared, 1u); +		num_fences = max(entry->num_shared, 1u);  		if (!ret) {  			ret = dma_resv_reserve_fences(bo->base.resv,  						      num_fences); @@ -154,10 +154,8 @@ void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,  	list_for_each_entry(entry, list, head) {  		struct ttm_buffer_object *bo = entry->bo; -		if (entry->num_shared) -			dma_resv_add_shared_fence(bo->base.resv, fence); -		else -			dma_resv_add_excl_fence(bo->base.resv, fence); +		dma_resv_add_fence(bo->base.resv, fence, entry->num_shared ? +				   DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE);  		ttm_bo_move_to_lru_tail_unlocked(bo);  		dma_resv_unlock(bo->base.resv);  	} diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 7d5a438180e9..65889b3caf50 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -644,3 +644,37 @@ ttm_kmap_iter_linear_io_fini(struct ttm_kmap_iter_linear_io *iter_io,  	ttm_mem_io_free(bdev, mem);  } + +#if defined(CONFIG_DEBUG_FS) + +static int ttm_resource_manager_show(struct seq_file *m, void *unused) +{ +	struct ttm_resource_manager *man = +		(struct ttm_resource_manager *)m->private; +	struct drm_printer p = drm_seq_file_printer(m); +	ttm_resource_manager_debug(man, &p); +	return 0; +} +DEFINE_SHOW_ATTRIBUTE(ttm_resource_manager); + +#endif + +/** + * ttm_resource_manager_create_debugfs - Create debugfs entry for specified + * resource manager. + * @man: The TTM resource manager for which the debugfs stats file be creates + * @parent: debugfs directory in which the file will reside + * @name: The filename to create. + * + * This function setups up a debugfs file that can be used to look + * at debug statistics of the specified ttm_resource_manager. + */ +void ttm_resource_manager_create_debugfs(struct ttm_resource_manager *man, +					 struct dentry * parent, +					 const char *name) +{ +#if defined(CONFIG_DEBUG_FS) +	debugfs_create_file(name, 0444, parent, man, &ttm_resource_manager_fops); +#endif +} +EXPORT_SYMBOL(ttm_resource_manager_create_debugfs); diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 961812d33827..2352e9640922 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -550,8 +550,8 @@ v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv,  	for (i = 0; i < job->bo_count; i++) {  		/* XXX: Use shared fences for read-only objects. */ -		dma_resv_add_excl_fence(job->bo[i]->resv, -					job->done_fence); +		dma_resv_add_fence(job->bo[i]->resv, job->done_fence, +				   DMA_RESV_USAGE_WRITE);  	}  	drm_gem_unlock_reservations(job->bo, job->bo_count, acquire_ctx); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 594bd6bb00d2..9eaf304fc20d 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -546,7 +546,8 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)  		bo = to_vc4_bo(&exec->bo[i]->base);  		bo->seqno = seqno; -		dma_resv_add_shared_fence(bo->base.base.resv, exec->fence); +		dma_resv_add_fence(bo->base.base.resv, exec->fence, +				   DMA_RESV_USAGE_READ);  	}  	list_for_each_entry(bo, &exec->unref_list, unref_head) { @@ -557,7 +558,8 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)  		bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);  		bo->write_seqno = seqno; -		dma_resv_add_excl_fence(bo->base.base.resv, exec->fence); +		dma_resv_add_fence(bo->base.base.resv, exec->fence, +				   DMA_RESV_USAGE_WRITE);  	}  } diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 9194cb52e706..2a58fc421cf6 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -611,6 +611,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)  	struct vc4_hvs *hvs = NULL;  	int ret;  	u32 dispctrl; +	u32 reg;  	hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);  	if (!hvs) @@ -682,6 +683,26 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)  	vc4->hvs = hvs; +	reg = HVS_READ(SCALER_DISPECTRL); +	reg &= ~SCALER_DISPECTRL_DSP2_MUX_MASK; +	HVS_WRITE(SCALER_DISPECTRL, +		  reg | VC4_SET_FIELD(0, SCALER_DISPECTRL_DSP2_MUX)); + +	reg = HVS_READ(SCALER_DISPCTRL); +	reg &= ~SCALER_DISPCTRL_DSP3_MUX_MASK; +	HVS_WRITE(SCALER_DISPCTRL, +		  reg | VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX)); + +	reg = HVS_READ(SCALER_DISPEOLN); +	reg &= ~SCALER_DISPEOLN_DSP4_MUX_MASK; +	HVS_WRITE(SCALER_DISPEOLN, +		  reg | VC4_SET_FIELD(3, SCALER_DISPEOLN_DSP4_MUX)); + +	reg = HVS_READ(SCALER_DISPDITHER); +	reg &= ~SCALER_DISPDITHER_DSP5_MUX_MASK; +	HVS_WRITE(SCALER_DISPDITHER, +		  reg | VC4_SET_FIELD(3, SCALER_DISPDITHER_DSP5_MUX)); +  	dispctrl = HVS_READ(SCALER_DISPCTRL);  	dispctrl |= SCALER_DISPCTRL_ENABLE; @@ -689,10 +710,6 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)  		    SCALER_DISPCTRL_DISPEIRQ(1) |  		    SCALER_DISPCTRL_DISPEIRQ(2); -	/* Set DSP3 (PV1) to use HVS channel 2, which would otherwise -	 * be unused. -	 */ -	dispctrl &= ~SCALER_DISPCTRL_DSP3_MUX_MASK;  	dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ |  		      SCALER_DISPCTRL_SLVWREIRQ |  		      SCALER_DISPCTRL_SLVRDEIRQ | @@ -706,7 +723,6 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)  		      SCALER_DISPCTRL_DSPEISLUR(1) |  		      SCALER_DISPCTRL_DSPEISLUR(2) |  		      SCALER_DISPCTRL_SCLEIRQ); -	dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);  	HVS_WRITE(SCALER_DISPCTRL, dispctrl); diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 747a2d199eca..c169bd72e53b 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -283,13 +283,18 @@ static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,  	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {  		struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);  		struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); +		unsigned int channel = vc4_state->assigned_channel;  		if (!vc4_state->update_muxing)  			continue;  		switch (vc4_crtc->data->hvs_output) {  		case 2: -			mux = (vc4_state->assigned_channel == 2) ? 0 : 1; +			drm_WARN_ON(&vc4->base, +				    VC4_GET_FIELD(HVS_READ(SCALER_DISPCTRL), +						  SCALER_DISPCTRL_DSP3_MUX) == channel); + +			mux = (channel == 2) ? 0 : 1;  			reg = HVS_READ(SCALER_DISPECTRL);  			HVS_WRITE(SCALER_DISPECTRL,  				  (reg & ~SCALER_DISPECTRL_DSP2_MUX_MASK) | @@ -297,10 +302,10 @@ static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,  			break;  		case 3: -			if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED) +			if (channel == VC4_HVS_CHANNEL_DISABLED)  				mux = 3;  			else -				mux = vc4_state->assigned_channel; +				mux = channel;  			reg = HVS_READ(SCALER_DISPCTRL);  			HVS_WRITE(SCALER_DISPCTRL, @@ -309,10 +314,10 @@ static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,  			break;  		case 4: -			if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED) +			if (channel == VC4_HVS_CHANNEL_DISABLED)  				mux = 3;  			else -				mux = vc4_state->assigned_channel; +				mux = channel;  			reg = HVS_READ(SCALER_DISPEOLN);  			HVS_WRITE(SCALER_DISPEOLN, @@ -322,10 +327,10 @@ static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,  			break;  		case 5: -			if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED) +			if (channel == VC4_HVS_CHANNEL_DISABLED)  				mux = 3;  			else -				mux = vc4_state->assigned_channel; +				mux = channel;  			reg = HVS_READ(SCALER_DISPDITHER);  			HVS_WRITE(SCALER_DISPDITHER, @@ -434,6 +439,9 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)  		 * requirements.  		 */  		clk_set_min_rate(hvs->core_clk, new_hvs_state->core_clock_rate); + +		drm_dbg(dev, "Core clock actual rate: %lu Hz\n", +			clk_get_rate(hvs->core_clk));  	}  } @@ -817,9 +825,18 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,  		unsigned int matching_channels;  		unsigned int channel; +		drm_dbg(dev, "%s: Trying to find a channel.\n", crtc->name); +  		/* Nothing to do here, let's skip it */ -		if (old_crtc_state->enable == new_crtc_state->enable) +		if (old_crtc_state->enable == new_crtc_state->enable) { +			if (new_crtc_state->enable) +				drm_dbg(dev, "%s: Already enabled, reusing channel %d.\n", +					crtc->name, new_vc4_crtc_state->assigned_channel); +			else +				drm_dbg(dev, "%s: Disabled, ignoring.\n", crtc->name); +  			continue; +		}  		/* Muxing will need to be modified, mark it as such */  		new_vc4_crtc_state->update_muxing = true; @@ -827,6 +844,10 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,  		/* If we're disabling our CRTC, we put back our channel */  		if (!new_crtc_state->enable) {  			channel = old_vc4_crtc_state->assigned_channel; + +			drm_dbg(dev, "%s: Disabling, Freeing channel %d\n", +				crtc->name, channel); +  			hvs_new_state->fifo_state[channel].in_use = false;  			new_vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED;  			continue; @@ -861,6 +882,8 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,  			return -EINVAL;  		channel = ffs(matching_channels) - 1; + +		drm_dbg(dev, "Assigned HVS channel %d to CRTC %s\n", channel, crtc->name);  		new_vc4_crtc_state->assigned_channel = channel;  		unassigned_channels &= ~BIT(channel);  		hvs_new_state->fifo_state[channel].in_use = true; diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 9809ca3e2945..82beb8c159f2 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -298,12 +298,18 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,  	if (WARN_ON(i == ARRAY_SIZE(drm_fmts)))  		return; -	ctrl = TXP_GO | TXP_VSTART_AT_EOF | TXP_EI | +	ctrl = TXP_GO | TXP_EI |  	       VC4_SET_FIELD(0xf, TXP_BYTE_ENABLE) |  	       VC4_SET_FIELD(txp_fmts[i], TXP_FORMAT);  	if (fb->format->has_alpha)  		ctrl |= TXP_ALPHA_ENABLE; +	else +		/* +		 * If TXP_ALPHA_ENABLE isn't set and TXP_ALPHA_INVERT is, the +		 * hardware will force the output padding to be 0xff. +		 */ +		ctrl |= TXP_ALPHA_INVERT;  	gem = drm_fb_cma_get_gem_obj(fb, 0);  	TXP_WRITE(TXP_DST_PTR, gem->paddr + fb->offsets[0]); diff --git a/drivers/gpu/drm/vgem/vgem_fence.c b/drivers/gpu/drm/vgem/vgem_fence.c index 2ddbebca87d9..c2a879734d40 100644 --- a/drivers/gpu/drm/vgem/vgem_fence.c +++ b/drivers/gpu/drm/vgem/vgem_fence.c @@ -130,6 +130,7 @@ int vgem_fence_attach_ioctl(struct drm_device *dev,  	struct vgem_file *vfile = file->driver_priv;  	struct dma_resv *resv;  	struct drm_gem_object *obj; +	enum dma_resv_usage usage;  	struct dma_fence *fence;  	int ret; @@ -151,7 +152,8 @@ int vgem_fence_attach_ioctl(struct drm_device *dev,  	/* Check for a conflicting fence */  	resv = obj->resv; -	if (!dma_resv_test_signaled(resv, arg->flags & VGEM_FENCE_WRITE)) { +	usage = dma_resv_usage_rw(arg->flags & VGEM_FENCE_WRITE); +	if (!dma_resv_test_signaled(resv, usage)) {  		ret = -EBUSY;  		goto err_fence;  	} @@ -159,12 +161,9 @@ int vgem_fence_attach_ioctl(struct drm_device *dev,  	/* Expose the fence via the dma-buf */  	dma_resv_lock(resv, NULL);  	ret = dma_resv_reserve_fences(resv, 1); -	if (!ret) { -		if (arg->flags & VGEM_FENCE_WRITE) -			dma_resv_add_excl_fence(resv, fence); -		else -			dma_resv_add_shared_fence(resv, fence); -	} +	if (!ret) +		dma_resv_add_fence(resv, fence, arg->flags & VGEM_FENCE_WRITE ? +				   DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);  	dma_resv_unlock(resv);  	/* Record the fence in our idr for later signaling */ diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c index 1820ca6cf673..580a78809836 100644 --- a/drivers/gpu/drm/virtio/virtgpu_gem.c +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c @@ -250,7 +250,8 @@ void virtio_gpu_array_add_fence(struct virtio_gpu_object_array *objs,  	int i;  	for (i = 0; i < objs->nents; i++) -		dma_resv_add_excl_fence(objs->objs[i]->resv, fence); +		dma_resv_add_fence(objs->objs[i]->resv, fence, +				   DMA_RESV_USAGE_WRITE);  }  void virtio_gpu_array_put_free(struct virtio_gpu_object_array *objs) diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index 77743fd2c61a..f8d83358d2a0 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -518,9 +518,10 @@ static int virtio_gpu_wait_ioctl(struct drm_device *dev, void *data,  		return -ENOENT;  	if (args->flags & VIRTGPU_WAIT_NOWAIT) { -		ret = dma_resv_test_signaled(obj->resv, true); +		ret = dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ);  	} else { -		ret = dma_resv_wait_timeout(obj->resv, true, true, timeout); +		ret = dma_resv_wait_timeout(obj->resv, DMA_RESV_USAGE_READ, +					    true, timeout);  	}  	if (ret == 0)  		ret = -EBUSY; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c index fe13aa8b4a64..408ede1f967f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c @@ -528,8 +528,8 @@ static int vmw_user_bo_synccpu_grab(struct vmw_buffer_object *vmw_bo,  	if (flags & drm_vmw_synccpu_allow_cs) {  		long lret; -		lret = dma_resv_wait_timeout(bo->base.resv, true, true, -					     nonblock ? 0 : +		lret = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_READ, +					     true, nonblock ? 0 :  					     MAX_SCHEDULE_TIMEOUT);  		if (!lret)  			return -EBUSY; @@ -758,7 +758,8 @@ void vmw_bo_fence_single(struct ttm_buffer_object *bo,  	ret = dma_resv_reserve_fences(bo->base.resv, 1);  	if (!ret) -		dma_resv_add_excl_fence(bo->base.resv, &fence->base); +		dma_resv_add_fence(bo->base.resv, &fence->base, +				   DMA_RESV_USAGE_KERNEL);  	else  		/* Last resort fallback when we are OOM */  		dma_fence_wait(&fence->base, false); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 791f9a5f3868..ffd975c14136 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -1385,6 +1385,23 @@ static void vmw_remove(struct pci_dev *pdev)  	vmw_driver_unload(dev);  } +static void vmw_debugfs_resource_managers_init(struct vmw_private *vmw) +{ +	struct drm_minor *minor = vmw->drm.primary; +	struct dentry *root = minor->debugfs_root; + +	ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, TTM_PL_SYSTEM), +					    root, "system_ttm"); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, TTM_PL_VRAM), +					    root, "vram_ttm"); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, VMW_PL_GMR), +					    root, "gmr_ttm"); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, VMW_PL_MOB), +					    root, "mob_ttm"); +	ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, VMW_PL_SYSTEM), +					    root, "system_mob_ttm"); +} +  static unsigned long  vmw_get_unmapped_area(struct file *file, unsigned long uaddr,  		      unsigned long len, unsigned long pgoff, @@ -1632,6 +1649,7 @@ static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  		goto out_unload;  	vmw_debugfs_gem_init(vmw); +	vmw_debugfs_resource_managers_init(vmw);  	return 0;  out_unload: diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 626067104751..a7d62a4eb47b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -1161,11 +1161,6 @@ int vmw_resources_clean(struct vmw_buffer_object *vbo, pgoff_t start,  		*num_prefault = __KERNEL_DIV_ROUND_UP(last_cleaned - res_start,  						      PAGE_SIZE);  		vmw_bo_fence_single(bo, NULL); -		if (bo->moving) -			dma_fence_put(bo->moving); - -		return dma_resv_get_singleton(bo->base.resv, false, -					      &bo->moving);  	}  	return 0; diff --git a/drivers/infiniband/core/umem_dmabuf.c b/drivers/infiniband/core/umem_dmabuf.c index d32cd7538835..fce80a4a5147 100644 --- a/drivers/infiniband/core/umem_dmabuf.c +++ b/drivers/infiniband/core/umem_dmabuf.c @@ -67,7 +67,8 @@ wait_fence:  	 * may be not up-to-date. Wait for the exporter to finish  	 * the migration.  	 */ -	return dma_resv_wait_timeout(umem_dmabuf->attach->dmabuf->resv, false, +	return dma_resv_wait_timeout(umem_dmabuf->attach->dmabuf->resv, +				     DMA_RESV_USAGE_KERNEL,  				     false, MAX_SCHEDULE_TIMEOUT);  }  EXPORT_SYMBOL(ib_umem_dmabuf_map_pages); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index a16b74f32aa9..176ed71d20c0 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -507,7 +507,6 @@ int of_platform_default_populate(struct device_node *root,  }  EXPORT_SYMBOL_GPL(of_platform_default_populate); -#ifndef CONFIG_PPC  static const struct of_device_id reserved_mem_matches[] = {  	{ .compatible = "qcom,rmtfs-mem" },  	{ .compatible = "qcom,cmd-db" }, @@ -527,26 +526,73 @@ static int __init of_platform_default_populate_init(void)  	if (!of_have_populated_dt())  		return -ENODEV; -	/* -	 * Handle certain compatibles explicitly, since we don't want to create -	 * platform_devices for every node in /reserved-memory with a -	 * "compatible", -	 */ -	for_each_matching_node(node, reserved_mem_matches) -		of_platform_device_create(node, NULL, NULL); +	if (IS_ENABLED(CONFIG_PPC)) { +		struct device_node *boot_display = NULL; +		struct platform_device *dev; +		int ret; + +		/* Check if we have a MacOS display without a node spec */ +		if (of_get_property(of_chosen, "linux,bootx-noscreen", NULL)) { +			/* +			 * The old code tried to work out which node was the MacOS +			 * display based on the address. I'm dropping that since the +			 * lack of a node spec only happens with old BootX versions +			 * (users can update) and with this code, they'll still get +			 * a display (just not the palette hacks). +			 */ +			dev = platform_device_alloc("bootx-noscreen", 0); +			if (WARN_ON(!dev)) +				return -ENOMEM; +			ret = platform_device_add(dev); +			if (WARN_ON(ret)) { +				platform_device_put(dev); +				return ret; +			} +		} -	node = of_find_node_by_path("/firmware"); -	if (node) { -		of_platform_populate(node, NULL, NULL, NULL); -		of_node_put(node); -	} +		/* +		 * For OF framebuffers, first create the device for the boot display, +		 * then for the other framebuffers. Only fail for the boot display; +		 * ignore errors for the rest. +		 */ +		for_each_node_by_type(node, "display") { +			if (!of_get_property(node, "linux,opened", NULL) || +			    !of_get_property(node, "linux,boot-display", NULL)) +				continue; +			dev = of_platform_device_create(node, "of-display", NULL); +			if (WARN_ON(!dev)) +				return -ENOMEM; +			boot_display = node; +			break; +		} +		for_each_node_by_type(node, "display") { +			if (!of_get_property(node, "linux,opened", NULL) || node == boot_display) +				continue; +			of_platform_device_create(node, "of-display", NULL); +		} -	node = of_get_compatible_child(of_chosen, "simple-framebuffer"); -	of_platform_device_create(node, NULL, NULL); -	of_node_put(node); +	} else { +		/* +		 * Handle certain compatibles explicitly, since we don't want to create +		 * platform_devices for every node in /reserved-memory with a +		 * "compatible", +		 */ +		for_each_matching_node(node, reserved_mem_matches) +			of_platform_device_create(node, NULL, NULL); + +		node = of_find_node_by_path("/firmware"); +		if (node) { +			of_platform_populate(node, NULL, NULL, NULL); +			of_node_put(node); +		} -	/* Populate everything else. */ -	of_platform_default_populate(NULL, NULL, NULL); +		node = of_get_compatible_child(of_chosen, "simple-framebuffer"); +		of_platform_device_create(node, NULL, NULL); +		of_node_put(node); + +		/* Populate everything else. */ +		of_platform_default_populate(NULL, NULL, NULL); +	}  	return 0;  } @@ -558,7 +604,6 @@ static int __init of_platform_sync_state_init(void)  	return 0;  }  late_initcall_sync(of_platform_sync_state_init); -#endif  int of_platform_device_destroy(struct device *dev, void *data)  { diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 2fc1b80a26ad..c4e91715ef00 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -86,10 +86,6 @@   * - fbcon state itself is protected by the console_lock, and the code does a   *   pretty good job at making sure that lock is held everywhere it's needed.   * - * - access to the registered_fb array is entirely unprotected. This should use - *   proper object lifetime handling, i.e. get/put_fb_info. This also means - *   switching from indices to proper pointers for fb_info everywhere. - *   * - fbcon doesn't bother with fb_lock/unlock at all. This is buggy, since it   *   means concurrent access to the same fbdev from both fbcon and userspace   *   will blow up. To fix this all fbcon calls from fbmem.c need to be moved out @@ -107,9 +103,23 @@ enum {  static struct fbcon_display fb_display[MAX_NR_CONSOLES]; +struct fb_info *fbcon_registered_fb[FB_MAX]; +int fbcon_num_registered_fb; + +#define fbcon_for_each_registered_fb(i)		\ +	for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++)		\ +		if (!fbcon_registered_fb[i]) {} else +  static signed char con2fb_map[MAX_NR_CONSOLES];  static signed char con2fb_map_boot[MAX_NR_CONSOLES]; +static struct fb_info *fbcon_info_from_console(int console) +{ +	WARN_CONSOLE_UNLOCKED(); + +	return fbcon_registered_fb[con2fb_map[console]]; +} +  static int logo_lines;  /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO     enums.  */ @@ -163,39 +173,19 @@ static int fbcon_cursor_noblink;   *  Interface used by the world   */ -static const char *fbcon_startup(void); -static void fbcon_init(struct vc_data *vc, int init); -static void fbcon_deinit(struct vc_data *vc); -static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, -			int width); -static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos); -static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, -			int count, int ypos, int xpos);  static void fbcon_clear_margins(struct vc_data *vc, int bottom_only); -static void fbcon_cursor(struct vc_data *vc, int mode); -static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, -			int height, int width); -static int fbcon_switch(struct vc_data *vc); -static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);  static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table);  /*   *  Internal routines   */ -static __inline__ void ywrap_up(struct vc_data *vc, int count); -static __inline__ void ywrap_down(struct vc_data *vc, int count); -static __inline__ void ypan_up(struct vc_data *vc, int count); -static __inline__ void ypan_down(struct vc_data *vc, int count); -static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx, -			    int dy, int dx, int height, int width, u_int y_break);  static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,  			   int unit);  static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,  			      int line, int count, int dy);  static void fbcon_modechanged(struct fb_info *info);  static void fbcon_set_all_vcs(struct fb_info *info); -static void fbcon_start(void); -static void fbcon_exit(void); +  static struct device *fbcon_device;  #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION @@ -218,7 +208,7 @@ static void fbcon_rotate(struct fb_info *info, u32 rotate)  	if (!ops || ops->currcon == -1)  		return; -	fb_info = registered_fb[con2fb_map[ops->currcon]]; +	fb_info = fbcon_info_from_console(ops->currcon);  	if (info == fb_info) {  		struct fbcon_display *p = &fb_display[ops->currcon]; @@ -245,7 +235,7 @@ static void fbcon_rotate_all(struct fb_info *info, u32 rotate)  	for (i = first_fb_vc; i <= last_fb_vc; i++) {  		vc = vc_cons[i].d;  		if (!vc || vc->vc_mode != KD_TEXT || -		    registered_fb[con2fb_map[i]] != info) +		    fbcon_info_from_console(i) != info)  			continue;  		p = &fb_display[vc->vc_num]; @@ -357,8 +347,8 @@ static int get_color(struct vc_data *vc, struct fb_info *info,  static void fb_flashcursor(struct work_struct *work)  { -	struct fb_info *info = container_of(work, struct fb_info, queue); -	struct fbcon_ops *ops = info->fbcon_par; +	struct fbcon_ops *ops = container_of(work, struct fbcon_ops, cursor_work.work); +	struct fb_info *info;  	struct vc_data *vc = NULL;  	int c;  	int mode; @@ -371,11 +361,14 @@ static void fb_flashcursor(struct work_struct *work)  	if (ret == 0)  		return; -	if (ops && ops->currcon != -1) +	/* protected by console_lock */ +	info = ops->info; + +	if (ops->currcon != -1)  		vc = vc_cons[ops->currcon].d;  	if (!vc || !con_is_visible(vc) || - 	    registered_fb[con2fb_map[vc->vc_num]] != info || +	    fbcon_info_from_console(vc->vc_num) != info ||  	    vc->vc_deccm != 1) {  		console_unlock();  		return; @@ -387,42 +380,25 @@ static void fb_flashcursor(struct work_struct *work)  	ops->cursor(vc, info, mode, get_color(vc, info, c, 1),  		    get_color(vc, info, c, 0));  	console_unlock(); -} -static void cursor_timer_handler(struct timer_list *t) -{ -	struct fbcon_ops *ops = from_timer(ops, t, cursor_timer); -	struct fb_info *info = ops->info; - -	queue_work(system_power_efficient_wq, &info->queue); -	mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies); +	queue_delayed_work(system_power_efficient_wq, &ops->cursor_work, +			   ops->cur_blink_jiffies);  } -static void fbcon_add_cursor_timer(struct fb_info *info) +static void fbcon_add_cursor_work(struct fb_info *info)  {  	struct fbcon_ops *ops = info->fbcon_par; -	if ((!info->queue.func || info->queue.func == fb_flashcursor) && -	    !(ops->flags & FBCON_FLAGS_CURSOR_TIMER) && -	    !fbcon_cursor_noblink) { -		if (!info->queue.func) -			INIT_WORK(&info->queue, fb_flashcursor); - -		timer_setup(&ops->cursor_timer, cursor_timer_handler, 0); -		mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies); -		ops->flags |= FBCON_FLAGS_CURSOR_TIMER; -	} +	if (!fbcon_cursor_noblink) +		queue_delayed_work(system_power_efficient_wq, &ops->cursor_work, +				   ops->cur_blink_jiffies);  } -static void fbcon_del_cursor_timer(struct fb_info *info) +static void fbcon_del_cursor_work(struct fb_info *info)  {  	struct fbcon_ops *ops = info->fbcon_par; -	if (info->queue.func == fb_flashcursor && -	    ops->flags & FBCON_FLAGS_CURSOR_TIMER) { -		del_timer_sync(&ops->cursor_timer); -		ops->flags &= ~FBCON_FLAGS_CURSOR_TIMER; -	} +	cancel_delayed_work_sync(&ops->cursor_work);  }  #ifndef MODULE @@ -540,7 +516,7 @@ static int do_fbcon_takeover(int show_logo)  {  	int err, i; -	if (!num_registered_fb) +	if (!fbcon_num_registered_fb)  		return -ENODEV;  	if (!show_logo) @@ -602,7 +578,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,  		save = kmalloc(array3_size(logo_lines, new_cols, 2),  			       GFP_KERNEL);  		if (save) { -			int i = cols < new_cols ? cols : new_cols; +			int i = min(cols, new_cols);  			scr_memsetw(save, erase, array3_size(logo_lines, new_cols, 2));  			r = q - step;  			for (cnt = 0; cnt < logo_lines; cnt++, r += i) @@ -703,87 +679,95 @@ static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)  #endif /* CONFIG_MISC_TILEBLITTING */ - -static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info, -				  int unit, int oldidx) +static void fbcon_release(struct fb_info *info)  { -	struct fbcon_ops *ops = NULL; -	int err = 0; +	lock_fb_info(info); +	if (info->fbops->fb_release) +		info->fbops->fb_release(info, 0); +	unlock_fb_info(info); -	if (!try_module_get(info->fbops->owner)) -		err = -ENODEV; +	module_put(info->fbops->owner); -	if (!err && info->fbops->fb_open && -	    info->fbops->fb_open(info, 0)) -		err = -ENODEV; +	if (info->fbcon_par) { +		struct fbcon_ops *ops = info->fbcon_par; -	if (!err) { -		ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); -		if (!ops) -			err = -ENOMEM; +		fbcon_del_cursor_work(info); +		kfree(ops->cursor_state.mask); +		kfree(ops->cursor_data); +		kfree(ops->cursor_src); +		kfree(ops->fontbuffer); +		kfree(info->fbcon_par); +		info->fbcon_par = NULL;  	} +} -	if (!err) { -		ops->cur_blink_jiffies = HZ / 5; -		ops->info = info; -		info->fbcon_par = ops; +static int fbcon_open(struct fb_info *info) +{ +	struct fbcon_ops *ops; -		if (vc) -			set_blitting_type(vc, info); -	} +	if (!try_module_get(info->fbops->owner)) +		return -ENODEV; -	if (err) { -		con2fb_map[unit] = oldidx; +	lock_fb_info(info); +	if (info->fbops->fb_open && +	    info->fbops->fb_open(info, 0)) { +		unlock_fb_info(info);  		module_put(info->fbops->owner); +		return -ENODEV; +	} +	unlock_fb_info(info); + +	ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); +	if (!ops) { +		fbcon_release(info); +		return -ENOMEM;  	} +	INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor); +	ops->info = info; +	info->fbcon_par = ops; +	ops->cur_blink_jiffies = HZ / 5; + +	return 0; +} + +static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info, +				  int unit) +{ +	int err; + +	err = fbcon_open(info); +	if (err) +		return err; + +	if (vc) +		set_blitting_type(vc, info); +  	return err;  } -static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, -				  struct fb_info *newinfo, int unit, -				  int oldidx, int found) +static void con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, +				   struct fb_info *newinfo)  { -	struct fbcon_ops *ops = oldinfo->fbcon_par; -	int err = 0, ret; +	int ret; -	if (oldinfo->fbops->fb_release && -	    oldinfo->fbops->fb_release(oldinfo, 0)) { -		con2fb_map[unit] = oldidx; -		if (!found && newinfo->fbops->fb_release) -			newinfo->fbops->fb_release(newinfo, 0); -		if (!found) -			module_put(newinfo->fbops->owner); -		err = -ENODEV; -	} +	fbcon_release(oldinfo); -	if (!err) { -		fbcon_del_cursor_timer(oldinfo); -		kfree(ops->cursor_state.mask); -		kfree(ops->cursor_data); -		kfree(ops->cursor_src); -		kfree(ops->fontbuffer); -		kfree(oldinfo->fbcon_par); -		oldinfo->fbcon_par = NULL; -		module_put(oldinfo->fbops->owner); -		/* -		  If oldinfo and newinfo are driving the same hardware, -		  the fb_release() method of oldinfo may attempt to -		  restore the hardware state.  This will leave the -		  newinfo in an undefined state. Thus, a call to -		  fb_set_par() may be needed for the newinfo. -		*/ -		if (newinfo && newinfo->fbops->fb_set_par) { -			ret = newinfo->fbops->fb_set_par(newinfo); +	/* +	  If oldinfo and newinfo are driving the same hardware, +	  the fb_release() method of oldinfo may attempt to +	  restore the hardware state.  This will leave the +	  newinfo in an undefined state. Thus, a call to +	  fb_set_par() may be needed for the newinfo. +	*/ +	if (newinfo && newinfo->fbops->fb_set_par) { +		ret = newinfo->fbops->fb_set_par(newinfo); -			if (ret) -				printk(KERN_ERR "con2fb_release_oldinfo: " -					"detected unhandled fb_set_par error, " -					"error code %d\n", ret); -		} +		if (ret) +			printk(KERN_ERR "con2fb_release_oldinfo: " +				"detected unhandled fb_set_par error, " +				"error code %d\n", ret);  	} - -	return err;  }  static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, @@ -794,7 +778,7 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,  	ops->currcon = fg_console; -	if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT)) { +	if (info->fbops->fb_set_par && !ops->initialized) {  		ret = info->fbops->fb_set_par(info);  		if (ret) @@ -803,14 +787,14 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,  				"error code %d\n", ret);  	} -	ops->flags |= FBCON_FLAGS_INIT; +	ops->initialized = true;  	ops->graphics = 0;  	fbcon_set_disp(info, &info->var, unit);  	if (show_logo) {  		struct vc_data *fg_vc = vc_cons[fg_console].d;  		struct fb_info *fg_info = -			registered_fb[con2fb_map[fg_console]]; +			fbcon_info_from_console(fg_console);  		fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols,  				   fg_vc->vc_rows, fg_vc->vc_cols, @@ -835,9 +819,9 @@ static int set_con2fb_map(int unit, int newidx, int user)  {  	struct vc_data *vc = vc_cons[unit].d;  	int oldidx = con2fb_map[unit]; -	struct fb_info *info = registered_fb[newidx]; +	struct fb_info *info = fbcon_registered_fb[newidx];  	struct fb_info *oldinfo = NULL; -	int found, err = 0; +	int found, err = 0, show_logo;  	WARN_CONSOLE_UNLOCKED(); @@ -853,31 +837,30 @@ static int set_con2fb_map(int unit, int newidx, int user)  	}  	if (oldidx != -1) -		oldinfo = registered_fb[oldidx]; +		oldinfo = fbcon_registered_fb[oldidx];  	found = search_fb_in_map(newidx); -	con2fb_map[unit] = newidx; -	if (!err && !found) -		err = con2fb_acquire_newinfo(vc, info, unit, oldidx); +	if (!err && !found) { +		err = con2fb_acquire_newinfo(vc, info, unit); +		if (!err) +			con2fb_map[unit] = newidx; +	}  	/*  	 * If old fb is not mapped to any of the consoles,  	 * fbcon should release it.  	 */  	if (!err && oldinfo && !search_fb_in_map(oldidx)) -		err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx, -					     found); +		con2fb_release_oldinfo(vc, oldinfo, info); -	if (!err) { -		int show_logo = (fg_console == 0 && !user && -				 logo_shown != FBCON_LOGO_DONTSHOW); +	show_logo = (fg_console == 0 && !user && +			 logo_shown != FBCON_LOGO_DONTSHOW); -		if (!found) -			fbcon_add_cursor_timer(info); -		con2fb_map_boot[unit] = newidx; -		con2fb_init_display(vc, info, unit, show_logo); -	} +	if (!found) +		fbcon_add_cursor_work(info); +	con2fb_map_boot[unit] = newidx; +	con2fb_init_display(vc, info, unit, show_logo);  	if (!search_fb_in_map(info_idx))  		info_idx = newidx; @@ -938,7 +921,6 @@ static const char *fbcon_startup(void)  	struct fbcon_display *p = &fb_display[fg_console];  	struct vc_data *vc = vc_cons[fg_console].d;  	const struct font_desc *font = NULL; -	struct module *owner;  	struct fb_info *info = NULL;  	struct fbcon_ops *ops;  	int rows, cols; @@ -947,36 +929,23 @@ static const char *fbcon_startup(void)  	 *  If num_registered_fb is zero, this is a call for the dummy part.  	 *  The frame buffer devices weren't initialized yet.  	 */ -	if (!num_registered_fb || info_idx == -1) +	if (!fbcon_num_registered_fb || info_idx == -1)  		return display_desc;  	/*  	 * Instead of blindly using registered_fb[0], we use info_idx, set by -	 * fb_console_init(); +	 * fbcon_fb_registered();  	 */ -	info = registered_fb[info_idx]; +	info = fbcon_registered_fb[info_idx];  	if (!info)  		return NULL; -	owner = info->fbops->owner; -	if (!try_module_get(owner)) -		return NULL; -	if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) { -		module_put(owner); +	if (fbcon_open(info))  		return NULL; -	} - -	ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); -	if (!ops) { -		module_put(owner); -		return NULL; -	} +	ops = info->fbcon_par;  	ops->currcon = -1;  	ops->graphics = 1;  	ops->cur_rotate = -1; -	ops->cur_blink_jiffies = HZ / 5; -	ops->info = info; -	info->fbcon_par = ops;  	p->con_rotate = initial_rotation;  	if (p->con_rotate == -1) @@ -1013,7 +982,7 @@ static const char *fbcon_startup(void)  		 info->var.yres,  		 info->var.bits_per_pixel); -	fbcon_add_cursor_timer(info); +	fbcon_add_cursor_work(info);  	return display_desc;  } @@ -1033,7 +1002,7 @@ static void fbcon_init(struct vc_data *vc, int init)  	if (con2fb_map[vc->vc_num] == -1)  		con2fb_map[vc->vc_num] = info_idx; -	info = registered_fb[con2fb_map[vc->vc_num]]; +	info = fbcon_info_from_console(vc->vc_num);  	if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)  		logo_shown = FBCON_LOGO_DONTSHOW; @@ -1046,7 +1015,7 @@ static void fbcon_init(struct vc_data *vc, int init)  		return;  	if (!info->fbcon_par) -		con2fb_acquire_newinfo(vc, info, vc->vc_num, -1); +		con2fb_acquire_newinfo(vc, info, vc->vc_num);  	/* If we are not the first console on this  	   fb, copy the font from that console */ @@ -1120,8 +1089,7 @@ static void fbcon_init(struct vc_data *vc, int init)  	 * We need to do it in fbcon_init() to prevent screen corruption.  	 */  	if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) { -		if (info->fbops->fb_set_par && -		    !(ops->flags & FBCON_FLAGS_INIT)) { +		if (info->fbops->fb_set_par && !ops->initialized) {  			ret = info->fbops->fb_set_par(info);  			if (ret) @@ -1130,7 +1098,7 @@ static void fbcon_init(struct vc_data *vc, int init)  					"error code %d\n", ret);  		} -		ops->flags |= FBCON_FLAGS_INIT; +		ops->initialized = true;  	}  	ops->graphics = 0; @@ -1175,6 +1143,27 @@ static void fbcon_free_font(struct fbcon_display *p, bool freefont)  static void set_vc_hi_font(struct vc_data *vc, bool set); +static void fbcon_release_all(void) +{ +	struct fb_info *info; +	int i, j, mapped; + +	fbcon_for_each_registered_fb(i) { +		mapped = 0; +		info = fbcon_registered_fb[i]; + +		for (j = first_fb_vc; j <= last_fb_vc; j++) { +			if (con2fb_map[j] == i) { +				mapped = 1; +				con2fb_map[j] = -1; +			} +		} + +		if (mapped) +			fbcon_release(info); +	} +} +  static void fbcon_deinit(struct vc_data *vc)  {  	struct fbcon_display *p = &fb_display[vc->vc_num]; @@ -1188,7 +1177,7 @@ static void fbcon_deinit(struct vc_data *vc)  	if (idx == -1)  		goto finished; -	info = registered_fb[idx]; +	info = fbcon_registered_fb[idx];  	if (!info)  		goto finished; @@ -1201,9 +1190,9 @@ static void fbcon_deinit(struct vc_data *vc)  		goto finished;  	if (con_is_visible(vc)) -		fbcon_del_cursor_timer(info); +		fbcon_del_cursor_work(info); -	ops->flags &= ~FBCON_FLAGS_INIT; +	ops->initialized = false;  finished:  	fbcon_free_font(p, free_font); @@ -1214,7 +1203,7 @@ finished:  		set_vc_hi_font(vc, false);  	if (!con_is_bound(&fb_con)) -		fbcon_exit(); +		fbcon_release_all();  	if (vc->vc_num == logo_shown)  		logo_shown = FBCON_LOGO_CANSHOW; @@ -1250,7 +1239,7 @@ finished:  static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,  			int width)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	struct fbcon_display *p = &fb_display[vc->vc_num]; @@ -1288,7 +1277,7 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,  static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,  			int count, int ypos, int xpos)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_display *p = &fb_display[vc->vc_num];  	struct fbcon_ops *ops = info->fbcon_par; @@ -1308,7 +1297,7 @@ static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)  static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	if (!fbcon_is_inactive(vc, info)) @@ -1317,7 +1306,7 @@ static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)  static void fbcon_cursor(struct vc_data *vc, int mode)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;   	int c = scr_readw((u16 *) vc->vc_pos); @@ -1327,9 +1316,9 @@ static void fbcon_cursor(struct vc_data *vc, int mode)  		return;  	if (vc->vc_cursor_type & CUR_SW) -		fbcon_del_cursor_timer(info); +		fbcon_del_cursor_work(info);  	else -		fbcon_add_cursor_timer(info); +		fbcon_add_cursor_work(info);  	ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; @@ -1411,7 +1400,7 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,  static __inline__ void ywrap_up(struct vc_data *vc, int count)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	struct fbcon_display *p = &fb_display[vc->vc_num]; @@ -1430,7 +1419,7 @@ static __inline__ void ywrap_up(struct vc_data *vc, int count)  static __inline__ void ywrap_down(struct vc_data *vc, int count)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	struct fbcon_display *p = &fb_display[vc->vc_num]; @@ -1449,7 +1438,7 @@ static __inline__ void ywrap_down(struct vc_data *vc, int count)  static __inline__ void ypan_up(struct vc_data *vc, int count)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_display *p = &fb_display[vc->vc_num];  	struct fbcon_ops *ops = info->fbcon_par; @@ -1473,7 +1462,7 @@ static __inline__ void ypan_up(struct vc_data *vc, int count)  static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	struct fbcon_display *p = &fb_display[vc->vc_num]; @@ -1497,7 +1486,7 @@ static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)  static __inline__ void ypan_down(struct vc_data *vc, int count)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_display *p = &fb_display[vc->vc_num];  	struct fbcon_ops *ops = info->fbcon_par; @@ -1521,7 +1510,7 @@ static __inline__ void ypan_down(struct vc_data *vc, int count)  static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	struct fbcon_display *p = &fb_display[vc->vc_num]; @@ -1682,10 +1671,75 @@ static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p,  	}  } +static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx, +			    int dy, int dx, int height, int width, u_int y_break) +{ +	struct fb_info *info = fbcon_info_from_console(vc->vc_num); +	struct fbcon_ops *ops = info->fbcon_par; +	u_int b; + +	if (sy < y_break && sy + height > y_break) { +		b = y_break - sy; +		if (dy < sy) {	/* Avoid trashing self */ +			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, +					y_break); +			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, +					height - b, width, y_break); +		} else { +			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, +					height - b, width, y_break); +			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, +					y_break); +		} +		return; +	} + +	if (dy < y_break && dy + height > y_break) { +		b = y_break - dy; +		if (dy < sy) {	/* Avoid trashing self */ +			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, +					y_break); +			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, +					height - b, width, y_break); +		} else { +			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, +					height - b, width, y_break); +			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, +					y_break); +		} +		return; +	} +	ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, +		   height, width); +} + +static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, +			int height, int width) +{ +	struct fb_info *info = fbcon_info_from_console(vc->vc_num); +	struct fbcon_display *p = &fb_display[vc->vc_num]; + +	if (fbcon_is_inactive(vc, info)) +		return; + +	if (!width || !height) +		return; + +	/*  Split blits that cross physical y_wrap case. +	 *  Pathological case involves 4 blits, better to use recursive +	 *  code rather than unrolled case +	 * +	 *  Recursive invocations don't need to erase the cursor over and +	 *  over again, so we use fbcon_bmove_rec() +	 */ +	fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width, +			p->vrows - p->yscroll); +} +  static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,  		enum con_scroll dir, unsigned int count)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_display *p = &fb_display[vc->vc_num];  	int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; @@ -1882,71 +1936,6 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,  } -static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, -			int height, int width) -{ -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; -	struct fbcon_display *p = &fb_display[vc->vc_num]; - -	if (fbcon_is_inactive(vc, info)) -		return; - -	if (!width || !height) -		return; - -	/*  Split blits that cross physical y_wrap case. -	 *  Pathological case involves 4 blits, better to use recursive -	 *  code rather than unrolled case -	 * -	 *  Recursive invocations don't need to erase the cursor over and -	 *  over again, so we use fbcon_bmove_rec() -	 */ -	fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width, -			p->vrows - p->yscroll); -} - -static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx, -			    int dy, int dx, int height, int width, u_int y_break) -{ -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; -	struct fbcon_ops *ops = info->fbcon_par; -	u_int b; - -	if (sy < y_break && sy + height > y_break) { -		b = y_break - sy; -		if (dy < sy) {	/* Avoid trashing self */ -			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, -					y_break); -			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, -					height - b, width, y_break); -		} else { -			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, -					height - b, width, y_break); -			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, -					y_break); -		} -		return; -	} - -	if (dy < y_break && dy + height > y_break) { -		b = y_break - dy; -		if (dy < sy) {	/* Avoid trashing self */ -			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, -					y_break); -			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, -					height - b, width, y_break); -		} else { -			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, -					height - b, width, y_break); -			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, -					y_break); -		} -		return; -	} -	ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, -		   height, width); -} -  static void updatescrollmode_accel(struct fbcon_display *p,  					struct fb_info *info,  					struct vc_data *vc) @@ -2015,7 +2004,7 @@ static void updatescrollmode(struct fbcon_display *p,  static int fbcon_resize(struct vc_data *vc, unsigned int width,   			unsigned int height, unsigned int user)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	struct fbcon_display *p = &fb_display[vc->vc_num];  	struct fb_var_screeninfo var = info->var; @@ -2084,7 +2073,7 @@ static int fbcon_switch(struct vc_data *vc)  	struct fb_var_screeninfo var;  	int i, ret, prev_console; -	info = registered_fb[con2fb_map[vc->vc_num]]; +	info = fbcon_info_from_console(vc->vc_num);  	ops = info->fbcon_par;  	if (logo_shown >= 0) { @@ -2098,7 +2087,7 @@ static int fbcon_switch(struct vc_data *vc)  	prev_console = ops->currcon;  	if (prev_console != -1) -		old_info = registered_fb[con2fb_map[prev_console]]; +		old_info = fbcon_info_from_console(prev_console);  	/*  	 * FIXME: If we have multiple fbdev's loaded, we need to  	 * update all info->currcon.  Perhaps, we can place this @@ -2107,9 +2096,9 @@ static int fbcon_switch(struct vc_data *vc)  	 *  	 * info->currcon = vc->vc_num;  	 */ -	for_each_registered_fb(i) { -		if (registered_fb[i]->fbcon_par) { -			struct fbcon_ops *o = registered_fb[i]->fbcon_par; +	fbcon_for_each_registered_fb(i) { +		if (fbcon_registered_fb[i]->fbcon_par) { +			struct fbcon_ops *o = fbcon_registered_fb[i]->fbcon_par;  			o->currcon = vc->vc_num;  		} @@ -2139,14 +2128,14 @@ static int fbcon_switch(struct vc_data *vc)  		}  		if (old_info != info) -			fbcon_del_cursor_timer(old_info); +			fbcon_del_cursor_work(old_info);  	}  	if (fbcon_is_inactive(vc, info) ||  	    ops->blank_state != FB_BLANK_UNBLANK) -		fbcon_del_cursor_timer(info); +		fbcon_del_cursor_work(info);  	else -		fbcon_add_cursor_timer(info); +		fbcon_add_cursor_work(info);  	set_blitting_type(vc, info);  	ops->cursor_reset = 1; @@ -2221,7 +2210,7 @@ static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,  static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	if (mode_switch) { @@ -2254,16 +2243,16 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)  	if (mode_switch || fbcon_is_inactive(vc, info) ||  	    ops->blank_state != FB_BLANK_UNBLANK) -		fbcon_del_cursor_timer(info); +		fbcon_del_cursor_work(info);  	else -		fbcon_add_cursor_timer(info); +		fbcon_add_cursor_work(info);  	return 0;  }  static int fbcon_debug_enter(struct vc_data *vc)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	ops->save_graphics = ops->graphics; @@ -2276,7 +2265,7 @@ static int fbcon_debug_enter(struct vc_data *vc)  static int fbcon_debug_leave(struct vc_data *vc)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	ops->graphics = ops->save_graphics; @@ -2412,7 +2401,7 @@ static void set_vc_hi_font(struct vc_data *vc, bool set)  static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,  			     const u8 * data, int userfont)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	struct fbcon_ops *ops = info->fbcon_par;  	struct fbcon_display *p = &fb_display[vc->vc_num];  	int resize; @@ -2466,7 +2455,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,  static int fbcon_set_font(struct vc_data *vc, struct console_font *font,  			  unsigned int flags)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	unsigned charcount = font->charcount;  	int w = font->width;  	int h = font->height; @@ -2530,7 +2519,7 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font,  static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	const struct font_desc *f;  	if (!name) @@ -2554,7 +2543,7 @@ static struct fb_cmap palette_cmap = {  static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)  { -	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; +	struct fb_info *info = fbcon_info_from_console(vc->vc_num);  	int i, j, k, depth;  	u8 val; @@ -2670,7 +2659,7 @@ static void fbcon_modechanged(struct fb_info *info)  		return;  	vc = vc_cons[ops->currcon].d;  	if (vc->vc_mode != KD_TEXT || -	    registered_fb[con2fb_map[ops->currcon]] != info) +	    fbcon_info_from_console(ops->currcon) != info)  		return;  	p = &fb_display[vc->vc_num]; @@ -2710,7 +2699,7 @@ static void fbcon_set_all_vcs(struct fb_info *info)  	for (i = first_fb_vc; i <= last_fb_vc; i++) {  		vc = vc_cons[i].d;  		if (!vc || vc->vc_mode != KD_TEXT || -		    registered_fb[con2fb_map[i]] != info) +		    fbcon_info_from_console(i) != info)  			continue;  		if (con_is_visible(vc)) { @@ -2754,7 +2743,7 @@ int fbcon_mode_deleted(struct fb_info *info,  		j = con2fb_map[i];  		if (j == -1)  			continue; -		fb_info = registered_fb[j]; +		fb_info = fbcon_registered_fb[j];  		if (fb_info != info)  			continue;  		p = &fb_display[i]; @@ -2783,16 +2772,17 @@ static void fbcon_unbind(void)  static inline void fbcon_unbind(void) {}  #endif /* CONFIG_VT_HW_CONSOLE_BINDING */ -/* called with console_lock held */  void fbcon_fb_unbind(struct fb_info *info)  { -	int i, new_idx = -1, ret = 0; +	int i, new_idx = -1;  	int idx = info->node; -	WARN_CONSOLE_UNLOCKED(); +	console_lock(); -	if (!fbcon_has_console_bind) +	if (!fbcon_has_console_bind) { +		console_unlock();  		return; +	}  	for (i = first_fb_vc; i <= last_fb_vc; i++) {  		if (con2fb_map[i] != idx && @@ -2808,7 +2798,7 @@ void fbcon_fb_unbind(struct fb_info *info)  				set_con2fb_map(i, new_idx, 0);  		}  	} else { -		struct fb_info *info = registered_fb[idx]; +		struct fb_info *info = fbcon_registered_fb[idx];  		/* This is sort of like set_con2fb_map, except it maps  		 * the consoles to no device and then releases the @@ -2820,29 +2810,30 @@ void fbcon_fb_unbind(struct fb_info *info)  			if (con2fb_map[i] == idx) {  				con2fb_map[i] = -1;  				if (!search_fb_in_map(idx)) { -					ret = con2fb_release_oldinfo(vc_cons[i].d, -								     info, NULL, i, -								     idx, 0); -					if (ret) { -						con2fb_map[i] = idx; -						return; -					} +					con2fb_release_oldinfo(vc_cons[i].d, +							       info, NULL);  				}  			}  		}  		fbcon_unbind();  	} + +	console_unlock();  } -/* called with console_lock held */  void fbcon_fb_unregistered(struct fb_info *info)  {  	int i, idx; -	WARN_CONSOLE_UNLOCKED(); +	console_lock(); -	if (deferred_takeover) +	fbcon_registered_fb[info->node] = NULL; +	fbcon_num_registered_fb--; + +	if (deferred_takeover) { +		console_unlock();  		return; +	}  	idx = info->node;  	for (i = first_fb_vc; i <= last_fb_vc; i++) { @@ -2853,7 +2844,7 @@ void fbcon_fb_unregistered(struct fb_info *info)  	if (idx == info_idx) {  		info_idx = -1; -		for_each_registered_fb(i) { +		fbcon_for_each_registered_fb(i) {  			info_idx = i;  			break;  		} @@ -2869,8 +2860,9 @@ void fbcon_fb_unregistered(struct fb_info *info)  	if (primary_device == idx)  		primary_device = -1; -	if (!num_registered_fb) +	if (!fbcon_num_registered_fb)  		do_unregister_con_driver(&fb_con); +	console_unlock();  }  void fbcon_remap_all(struct fb_info *info) @@ -2928,13 +2920,21 @@ static inline void fbcon_select_primary(struct fb_info *info)  }  #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ +static bool lockless_register_fb; +module_param_named_unsafe(lockless_register_fb, lockless_register_fb, bool, 0400); +MODULE_PARM_DESC(lockless_register_fb, +	"Lockless framebuffer registration for debugging [default=off]"); +  /* called with console_lock held */ -int fbcon_fb_registered(struct fb_info *info) +static int do_fb_registered(struct fb_info *info)  {  	int ret = 0, i, idx;  	WARN_CONSOLE_UNLOCKED(); +	fbcon_registered_fb[info->node] = info; +	fbcon_num_registered_fb++; +  	idx = info->node;  	fbcon_select_primary(info); @@ -2963,6 +2963,25 @@ int fbcon_fb_registered(struct fb_info *info)  	return ret;  } +int fbcon_fb_registered(struct fb_info *info) +{ +	int ret; + +	if (!lockless_register_fb) +		console_lock(); +	else +		atomic_inc(&ignore_console_lock_warning); + +	ret = do_fb_registered(info); + +	if (!lockless_register_fb) +		console_unlock(); +	else +		atomic_dec(&ignore_console_lock_warning); + +	return ret; +} +  void fbcon_fb_blanked(struct fb_info *info, int blank)  {  	struct fbcon_ops *ops = info->fbcon_par; @@ -2973,7 +2992,7 @@ void fbcon_fb_blanked(struct fb_info *info, int blank)  	vc = vc_cons[ops->currcon].d;  	if (vc->vc_mode != KD_TEXT || -			registered_fb[con2fb_map[ops->currcon]] != info) +			fbcon_info_from_console(ops->currcon) != info)  		return;  	if (con_is_visible(vc)) { @@ -2993,7 +3012,7 @@ void fbcon_new_modelist(struct fb_info *info)  	const struct fb_videomode *mode;  	for (i = first_fb_vc; i <= last_fb_vc; i++) { -		if (registered_fb[con2fb_map[i]] != info) +		if (fbcon_info_from_console(i) != info)  			continue;  		if (!fb_display[i].mode)  			continue; @@ -3048,9 +3067,9 @@ int fbcon_set_con2fb_map_ioctl(void __user *argp)  		return -EINVAL;  	if (con2fb.framebuffer >= FB_MAX)  		return -EINVAL; -	if (!registered_fb[con2fb.framebuffer]) +	if (!fbcon_registered_fb[con2fb.framebuffer])  		request_module("fb%d", con2fb.framebuffer); -	if (!registered_fb[con2fb.framebuffer]) { +	if (!fbcon_registered_fb[con2fb.framebuffer]) {  		return -EINVAL;  	} @@ -3117,10 +3136,10 @@ static ssize_t store_rotate(struct device *device,  	console_lock();  	idx = con2fb_map[fg_console]; -	if (idx == -1 || registered_fb[idx] == NULL) +	if (idx == -1 || fbcon_registered_fb[idx] == NULL)  		goto err; -	info = registered_fb[idx]; +	info = fbcon_registered_fb[idx];  	rotate = simple_strtoul(buf, last, 0);  	fbcon_rotate(info, rotate);  err: @@ -3139,10 +3158,10 @@ static ssize_t store_rotate_all(struct device *device,  	console_lock();  	idx = con2fb_map[fg_console]; -	if (idx == -1 || registered_fb[idx] == NULL) +	if (idx == -1 || fbcon_registered_fb[idx] == NULL)  		goto err; -	info = registered_fb[idx]; +	info = fbcon_registered_fb[idx];  	rotate = simple_strtoul(buf, last, 0);  	fbcon_rotate_all(info, rotate);  err: @@ -3159,14 +3178,14 @@ static ssize_t show_rotate(struct device *device,  	console_lock();  	idx = con2fb_map[fg_console]; -	if (idx == -1 || registered_fb[idx] == NULL) +	if (idx == -1 || fbcon_registered_fb[idx] == NULL)  		goto err; -	info = registered_fb[idx]; +	info = fbcon_registered_fb[idx];  	rotate = fbcon_get_rotate(info);  err:  	console_unlock(); -	return snprintf(buf, PAGE_SIZE, "%d\n", rotate); +	return sysfs_emit(buf, "%d\n", rotate);  }  static ssize_t show_cursor_blink(struct device *device, @@ -3179,19 +3198,19 @@ static ssize_t show_cursor_blink(struct device *device,  	console_lock();  	idx = con2fb_map[fg_console]; -	if (idx == -1 || registered_fb[idx] == NULL) +	if (idx == -1 || fbcon_registered_fb[idx] == NULL)  		goto err; -	info = registered_fb[idx]; +	info = fbcon_registered_fb[idx];  	ops = info->fbcon_par;  	if (!ops)  		goto err; -	blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0; +	blink = delayed_work_pending(&ops->cursor_work);  err:  	console_unlock(); -	return snprintf(buf, PAGE_SIZE, "%d\n", blink); +	return sysfs_emit(buf, "%d\n", blink);  }  static ssize_t store_cursor_blink(struct device *device, @@ -3205,10 +3224,10 @@ static ssize_t store_cursor_blink(struct device *device,  	console_lock();  	idx = con2fb_map[fg_console]; -	if (idx == -1 || registered_fb[idx] == NULL) +	if (idx == -1 || fbcon_registered_fb[idx] == NULL)  		goto err; -	info = registered_fb[idx]; +	info = fbcon_registered_fb[idx];  	if (!info->fbcon_par)  		goto err; @@ -3217,10 +3236,10 @@ static ssize_t store_cursor_blink(struct device *device,  	if (blink) {  		fbcon_cursor_noblink = 0; -		fbcon_add_cursor_timer(info); +		fbcon_add_cursor_work(info);  	} else {  		fbcon_cursor_noblink = 1; -		fbcon_del_cursor_timer(info); +		fbcon_del_cursor_work(info);  	}  err: @@ -3265,8 +3284,11 @@ static void fbcon_register_existing_fbs(struct work_struct *work)  	console_lock(); -	for_each_registered_fb(i) -		fbcon_fb_registered(registered_fb[i]); +	deferred_takeover = false; +	logo_shown = FBCON_LOGO_DONTSHOW; + +	fbcon_for_each_registered_fb(i) +		do_fb_registered(fbcon_registered_fb[i]);  	console_unlock();  } @@ -3282,8 +3304,6 @@ static int fbcon_output_notifier(struct notifier_block *nb,  	pr_info("fbcon: Taking over console\n");  	dummycon_unregister_output_notifier(&fbcon_output_nb); -	deferred_takeover = false; -	logo_shown = FBCON_LOGO_DONTSHOW;  	/* We may get called in atomic context */  	schedule_work(&fbcon_deferred_takeover_work); @@ -3306,67 +3326,6 @@ static void fbcon_start(void)  		return;  	}  #endif - -	if (num_registered_fb) { -		int i; - -		for_each_registered_fb(i) { -			info_idx = i; -			break; -		} - -		do_fbcon_takeover(0); -	} -} - -static void fbcon_exit(void) -{ -	struct fb_info *info; -	int i, j, mapped; - -#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER -	if (deferred_takeover) { -		dummycon_unregister_output_notifier(&fbcon_output_nb); -		deferred_takeover = false; -	} -#endif - -	for_each_registered_fb(i) { -		int pending = 0; - -		mapped = 0; -		info = registered_fb[i]; - -		if (info->queue.func) -			pending = cancel_work_sync(&info->queue); -		pr_debug("fbcon: %s pending work\n", (pending ? "canceled" : "no")); - -		for (j = first_fb_vc; j <= last_fb_vc; j++) { -			if (con2fb_map[j] == i) { -				mapped = 1; -				con2fb_map[j] = -1; -			} -		} - -		if (mapped) { -			if (info->fbops->fb_release) -				info->fbops->fb_release(info, 0); -			module_put(info->fbops->owner); - -			if (info->fbcon_par) { -				struct fbcon_ops *ops = info->fbcon_par; - -				fbcon_del_cursor_timer(info); -				kfree(ops->cursor_src); -				kfree(ops->cursor_state.mask); -				kfree(info->fbcon_par); -				info->fbcon_par = NULL; -			} - -			if (info->queue.func == fb_flashcursor) -				info->queue.func = NULL; -		} -	}  }  void __init fb_console_init(void) @@ -3408,10 +3367,19 @@ static void __exit fbcon_deinit_device(void)  void __exit fb_console_exit(void)  { +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER +	console_lock(); +	if (deferred_takeover) +		dummycon_unregister_output_notifier(&fbcon_output_nb); +	console_unlock(); + +	cancel_work_sync(&fbcon_deferred_takeover_work); +#endif +  	console_lock();  	fbcon_deinit_device();  	device_destroy(fb_class, MKDEV(0, 0)); -	fbcon_exit(); +  	do_unregister_con_driver(&fb_con);  	console_unlock();  }	 diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h index 969d41ecede5..0eaf54a21151 100644 --- a/drivers/video/fbdev/core/fbcon.h +++ b/drivers/video/fbdev/core/fbcon.h @@ -14,12 +14,10 @@  #include <linux/types.h>  #include <linux/vt_buffer.h>  #include <linux/vt_kern.h> +#include <linux/workqueue.h>  #include <asm/io.h> -#define FBCON_FLAGS_INIT         1 -#define FBCON_FLAGS_CURSOR_TIMER 2 -     /*      *    This is the interface between the low-level console driver and the      *    low-level frame buffer device @@ -68,7 +66,7 @@ struct fbcon_ops {  	int  (*update_start)(struct fb_info *info);  	int  (*rotate_font)(struct fb_info *info, struct vc_data *vc);  	struct fb_var_screeninfo var;  /* copy of the current fb_var_screeninfo */ -	struct timer_list cursor_timer; /* Cursor timer */ +	struct delayed_work cursor_work; /* Cursor timer */  	struct fb_cursor cursor_state;  	struct fbcon_display *p;  	struct fb_info *info; @@ -79,7 +77,7 @@ struct fbcon_ops {  	int    blank_state;  	int    graphics;  	int    save_graphics; /* for debug enter/leave */ -	int    flags; +	bool   initialized;  	int    rotate;  	int    cur_rotate;  	char  *cursor_data; diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index a6bb0e438216..84427470367b 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -1577,14 +1577,12 @@ static void do_remove_conflicting_framebuffers(struct apertures_struct *a,  			 * allocate the memory range.  			 *  			 * If it's not a platform device, at least print a warning. A -			 * fix would add code to remove the device from the system. +			 * fix would add code to remove the device from the system. For +			 * framebuffers without any Linux device, print a warning as +			 * well.  			 */  			if (!device) { -				/* TODO: Represent each OF framebuffer as its own -				 * device in the device hierarchy. For now, offb -				 * doesn't have such a device, so unregister the -				 * framebuffer as before without warning. -				 */ +				pr_warn("fb%d: no device set\n", i);  				do_unregister_framebuffer(registered_fb[i]);  			} else if (dev_is_platform(device)) {  				registered_fb[i]->forced_out = true; @@ -1597,14 +1595,9 @@ static void do_remove_conflicting_framebuffers(struct apertures_struct *a,  	}  } -static bool lockless_register_fb; -module_param_named_unsafe(lockless_register_fb, lockless_register_fb, bool, 0400); -MODULE_PARM_DESC(lockless_register_fb, -	"Lockless framebuffer registration for debugging [default=off]"); -  static int do_register_framebuffer(struct fb_info *fb_info)  { -	int i, ret; +	int i;  	struct fb_videomode mode;  	if (fb_check_foreignness(fb_info)) @@ -1673,19 +1666,7 @@ static int do_register_framebuffer(struct fb_info *fb_info)  	}  #endif -	if (!lockless_register_fb) -		console_lock(); -	else -		atomic_inc(&ignore_console_lock_warning); -	lock_fb_info(fb_info); -	ret = fbcon_fb_registered(fb_info); -	unlock_fb_info(fb_info); - -	if (!lockless_register_fb) -		console_unlock(); -	else -		atomic_dec(&ignore_console_lock_warning); -	return ret; +	return fbcon_fb_registered(fb_info);  }  static void unbind_console(struct fb_info *fb_info) @@ -1695,11 +1676,7 @@ static void unbind_console(struct fb_info *fb_info)  	if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))  		return; -	console_lock(); -	lock_fb_info(fb_info);  	fbcon_fb_unbind(fb_info); -	unlock_fb_info(fb_info); -	console_unlock();  }  static void unlink_framebuffer(struct fb_info *fb_info) @@ -1742,9 +1719,7 @@ static void do_unregister_framebuffer(struct fb_info *fb_info)  		fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);  	}  #endif -	console_lock();  	fbcon_fb_unregistered(fb_info); -	console_unlock();  	/* this may free fb info */  	put_fb_info(fb_info); diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index 26892940c213..8c1ee9ecec3d 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -91,9 +91,11 @@ static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)  	var->activate |= FB_ACTIVATE_FORCE;  	console_lock(); +	lock_fb_info(fb_info);  	err = fb_set_var(fb_info, var);  	if (!err)  		fbcon_update_vcs(fb_info, var->activate & FB_ACTIVATE_ALL); +	unlock_fb_info(fb_info);  	console_unlock();  	if (err)  		return err; diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c index afdb6aa48add..b1acb1ebebe9 100644 --- a/drivers/video/fbdev/offb.c +++ b/drivers/video/fbdev/offb.c @@ -386,10 +386,10 @@ static void offb_init_palette_hacks(struct fb_info *info, struct device_node *dp  		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR;  } -static void __init offb_init_fb(const char *name, -				int width, int height, int depth, -				int pitch, unsigned long address, -				int foreign_endian, struct device_node *dp) +static void offb_init_fb(struct platform_device *parent, const char *name, +			 int width, int height, int depth, +			 int pitch, unsigned long address, +			 int foreign_endian, struct device_node *dp)  {  	unsigned long res_size = pitch * height;  	struct offb_par *par = &default_par; @@ -410,12 +410,13 @@ static void __init offb_init_fb(const char *name,  		return;  	} -	info = framebuffer_alloc(sizeof(u32) * 16, NULL); +	info = framebuffer_alloc(sizeof(u32) * 16, &parent->dev);  	if (!info) {  		release_mem_region(res_start, res_size);  		return;  	} +	platform_set_drvdata(parent, info);  	fix = &info->fix;  	var = &info->var; @@ -535,7 +536,8 @@ out_aper:  } -static void __init offb_init_nodriver(struct device_node *dp, int no_real_node) +static void offb_init_nodriver(struct platform_device *parent, struct device_node *dp, +			       int no_real_node)  {  	unsigned int len;  	int i, width = 640, height = 480, depth = 8, pitch = 640; @@ -650,46 +652,76 @@ static void __init offb_init_nodriver(struct device_node *dp, int no_real_node)  		/* kludge for valkyrie */  		if (of_node_name_eq(dp, "valkyrie"))  			address += 0x1000; -		offb_init_fb(no_real_node ? "bootx" : NULL, +		offb_init_fb(parent, no_real_node ? "bootx" : NULL,  			     width, height, depth, pitch, address,  			     foreign_endian, no_real_node ? NULL : dp);  	}  } -static int __init offb_init(void) +static int offb_remove(struct platform_device *pdev)  { -	struct device_node *dp = NULL, *boot_disp = NULL; +	struct fb_info *info = platform_get_drvdata(pdev); -	if (fb_get_options("offb", NULL)) -		return -ENODEV; +	if (info) +		unregister_framebuffer(info); -	/* Check if we have a MacOS display without a node spec */ -	if (of_get_property(of_chosen, "linux,bootx-noscreen", NULL) != NULL) { -		/* The old code tried to work out which node was the MacOS -		 * display based on the address. I'm dropping that since the -		 * lack of a node spec only happens with old BootX versions -		 * (users can update) and with this code, they'll still get -		 * a display (just not the palette hacks). -		 */ -		offb_init_nodriver(of_chosen, 1); -	} +	return 0; +} -	for_each_node_by_type(dp, "display") { -		if (of_get_property(dp, "linux,opened", NULL) && -		    of_get_property(dp, "linux,boot-display", NULL)) { -			boot_disp = dp; -			offb_init_nodriver(dp, 0); -		} -	} -	for_each_node_by_type(dp, "display") { -		if (of_get_property(dp, "linux,opened", NULL) && -		    dp != boot_disp) -			offb_init_nodriver(dp, 0); -	} +static int offb_probe_bootx_noscreen(struct platform_device *pdev) +{ +	offb_init_nodriver(pdev, of_chosen, 1);  	return 0;  } +static struct platform_driver offb_driver_bootx_noscreen = { +	.driver = { +		.name = "bootx-noscreen", +	}, +	.probe = offb_probe_bootx_noscreen, +	.remove = offb_remove, +}; + +static int offb_probe_display(struct platform_device *pdev) +{ +	offb_init_nodriver(pdev, pdev->dev.of_node, 0); + +	return 0; +} +static const struct of_device_id offb_of_match_display[] = { +	{ .compatible = "display", }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, offb_of_match_display); + +static struct platform_driver offb_driver_display = { +	.driver = { +		.name = "of-display", +		.of_match_table = offb_of_match_display, +	}, +	.probe = offb_probe_display, +	.remove = offb_remove, +}; + +static int __init offb_init(void) +{ +	if (fb_get_options("offb", NULL)) +		return -ENODEV; + +	platform_driver_register(&offb_driver_bootx_noscreen); +	platform_driver_register(&offb_driver_display); + +	return 0; +}  module_init(offb_init); + +static void __exit offb_exit(void) +{ +	platform_driver_unregister(&offb_driver_display); +	platform_driver_unregister(&offb_driver_bootx_noscreen); +} +module_exit(offb_exit); +  MODULE_LICENSE("GPL"); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 2a1f85f9a8a3..f668e75fbabe 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -143,6 +143,11 @@ struct dw_hdmi_plat_data {  					   const struct drm_display_info *info,  					   const struct drm_display_mode *mode); +	/* Platform-specific audio enable/disable (optional) */ +	void (*enable_audio)(struct dw_hdmi *hdmi, int channel, +			     int width, int rate, int non_pcm); +	void (*disable_audio)(struct dw_hdmi *hdmi); +  	/* Vendor PHY support */  	const struct dw_hdmi_phy_ops *phy_ops;  	const char *phy_name; @@ -173,6 +178,8 @@ void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense);  int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,  			   struct device *codec_dev); +void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm); +void dw_hdmi_set_sample_width(struct dw_hdmi *hdmi, unsigned int width);  void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate);  void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt);  void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi, u8 *channel_status); @@ -187,9 +194,11 @@ void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address);  void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,  			   unsigned char addr); +void dw_hdmi_phy_gen1_reset(struct dw_hdmi *hdmi); +  void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable);  void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable); -void dw_hdmi_phy_reset(struct dw_hdmi *hdmi); +void dw_hdmi_phy_gen2_reset(struct dw_hdmi *hdmi);  enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,  					       void *data); diff --git a/include/drm/dp/drm_dp_helper.h b/include/drm/dp/drm_dp_helper.h index 1eccd9741943..91af98e6617c 100644 --- a/include/drm/dp/drm_dp_helper.h +++ b/include/drm/dp/drm_dp_helper.h @@ -2053,6 +2053,7 @@ struct drm_dp_aux {  	bool is_remote;  }; +int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset);  ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,  			 void *buffer, size_t size);  ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index c76932b68a33..2d524f8b0802 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -94,7 +94,6 @@ struct ttm_tt;   * @deleted: True if the object is only a zombie and already deleted.   * @ddestroy: List head for the delayed destroy list.   * @swap: List head for swap LRU list. - * @moving: Fence set when BO is moving   * @offset: The current GPU offset, which can have different meanings   * depending on the memory type. For SYSTEM type memory, it should be 0.   * @cur_placement: Hint of current placement. @@ -147,7 +146,6 @@ struct ttm_buffer_object {  	 * Members protected by a bo reservation.  	 */ -	struct dma_fence *moving;  	unsigned priority;  	unsigned pin_count; diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 059a595e14e5..897b88f0bd59 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -245,7 +245,7 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,  			      struct ttm_resource *new_mem);  /** - * ttm_bo_move_accel_cleanup. + * ttm_bo_move_sync_cleanup.   *   * @bo: A pointer to a struct ttm_buffer_object.   * @new_mem: struct ttm_resource indicating where to move. @@ -253,13 +253,8 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,   * Special case of ttm_bo_move_accel_cleanup where the bo is guaranteed   * by the caller to be idle. Typically used after memcpy buffer moves.   */ -static inline void ttm_bo_move_sync_cleanup(struct ttm_buffer_object *bo, -					    struct ttm_resource *new_mem) -{ -	int ret = ttm_bo_move_accel_cleanup(bo, NULL, true, false, new_mem); - -	WARN_ON(ret); -} +void ttm_bo_move_sync_cleanup(struct ttm_buffer_object *bo, +			      struct ttm_resource *new_mem);  /**   * ttm_bo_pipeline_gutting. diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h index 314f73de4280..441653693970 100644 --- a/include/drm/ttm/ttm_resource.h +++ b/include/drm/ttm/ttm_resource.h @@ -215,8 +215,7 @@ struct ttm_lru_bulk_move_pos {  /**   * struct ttm_lru_bulk_move   * - * @tt: first/last lru entry for resources in the TT domain - * @vram: first/last lru entry for resources in the VRAM domain + * @pos: first/last lru entry for resources in the each domain/priority   *   * Container for the current bulk move state. Should be used with   * ttm_lru_bulk_move_init() and ttm_bo_set_bulk_move(). @@ -382,4 +381,8 @@ ttm_kmap_iter_linear_io_init(struct ttm_kmap_iter_linear_io *iter_io,  void ttm_kmap_iter_linear_io_fini(struct ttm_kmap_iter_linear_io *iter_io,  				  struct ttm_device *bdev,  				  struct ttm_resource *mem); + +void ttm_resource_manager_create_debugfs(struct ttm_resource_manager *man, +					 struct dentry * parent, +					 const char *name);  #endif diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 6fb91956ab8d..71731796c8c3 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -393,27 +393,30 @@ struct dma_buf {  	 * e.g. exposed in `Implicit Fence Poll Support`_ must follow the  	 * below rules.  	 * -	 * - Drivers must add a shared fence through dma_resv_add_shared_fence() -	 *   for anything the userspace API considers a read access. This highly -	 *   depends upon the API and window system. +	 * - Drivers must add a read fence through dma_resv_add_fence() with the +	 *   DMA_RESV_USAGE_READ flag for anything the userspace API considers a +	 *   read access. This highly depends upon the API and window system.  	 * -	 * - Similarly drivers must set the exclusive fence through -	 *   dma_resv_add_excl_fence() for anything the userspace API considers -	 *   write access. +	 * - Similarly drivers must add a write fence through +	 *   dma_resv_add_fence() with the DMA_RESV_USAGE_WRITE flag for +	 *   anything the userspace API considers write access.  	 * -	 * - Drivers may just always set the exclusive fence, since that only +	 * - Drivers may just always add a write fence, since that only  	 *   causes unecessarily synchronization, but no correctness issues.  	 *  	 * - Some drivers only expose a synchronous userspace API with no  	 *   pipelining across drivers. These do not set any fences for their  	 *   access. An example here is v4l.  	 * +	 * - Driver should use dma_resv_usage_rw() when retrieving fences as +	 *   dependency for implicit synchronization. +	 *  	 * DYNAMIC IMPORTER RULES:  	 *  	 * Dynamic importers, see dma_buf_attachment_is_dynamic(), have  	 * additional constraints on how they set up fences:  	 * -	 * - Dynamic importers must obey the exclusive fence and wait for it to +	 * - Dynamic importers must obey the write fences and wait for them to  	 *   signal before allowing access to the buffer's underlying storage  	 *   through the device.  	 * @@ -423,8 +426,9 @@ struct dma_buf {  	 *  	 * IMPORTANT:  	 * -	 * All drivers must obey the struct dma_resv rules, specifically the -	 * rules for updating and obeying fences. +	 * All drivers and memory management related functions must obey the +	 * struct dma_resv rules, specifically the rules for updating and +	 * obeying fences. See enum dma_resv_usage for further descriptions.  	 */  	struct dma_resv *resv; diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h index 5fa04d0fccad..c8ccbc94d5d2 100644 --- a/include/linux/dma-resv.h +++ b/include/linux/dma-resv.h @@ -50,10 +50,86 @@ extern struct ww_class reservation_ww_class;  struct dma_resv_list;  /** + * enum dma_resv_usage - how the fences from a dma_resv obj are used + * + * This enum describes the different use cases for a dma_resv object and + * controls which fences are returned when queried. + * + * An important fact is that there is the order KERNEL<WRITE<READ<BOOKKEEP and + * when the dma_resv object is asked for fences for one use case the fences + * for the lower use case are returned as well. + * + * For example when asking for WRITE fences then the KERNEL fences are returned + * as well. Similar when asked for READ fences then both WRITE and KERNEL + * fences are returned as well. + */ +enum dma_resv_usage { +	/** +	 * @DMA_RESV_USAGE_KERNEL: For in kernel memory management only. +	 * +	 * This should only be used for things like copying or clearing memory +	 * with a DMA hardware engine for the purpose of kernel memory +	 * management. +	 * +	 * Drivers *always* must wait for those fences before accessing the +	 * resource protected by the dma_resv object. The only exception for +	 * that is when the resource is known to be locked down in place by +	 * pinning it previously. +	 */ +	DMA_RESV_USAGE_KERNEL, + +	/** +	 * @DMA_RESV_USAGE_WRITE: Implicit write synchronization. +	 * +	 * This should only be used for userspace command submissions which add +	 * an implicit write dependency. +	 */ +	DMA_RESV_USAGE_WRITE, + +	/** +	 * @DMA_RESV_USAGE_READ: Implicit read synchronization. +	 * +	 * This should only be used for userspace command submissions which add +	 * an implicit read dependency. +	 */ +	DMA_RESV_USAGE_READ, + +	/** +	 * @DMA_RESV_USAGE_BOOKKEEP: No implicit sync. +	 * +	 * This should be used by submissions which don't want to participate in +	 * implicit synchronization. +	 * +	 * The most common case are preemption fences as well as page table +	 * updates and their TLB flushes. +	 */ +	DMA_RESV_USAGE_BOOKKEEP +}; + +/** + * dma_resv_usage_rw - helper for implicit sync + * @write: true if we create a new implicit sync write + * + * This returns the implicit synchronization usage for write or read accesses, + * see enum dma_resv_usage and &dma_buf.resv. + */ +static inline enum dma_resv_usage dma_resv_usage_rw(bool write) +{ +	/* This looks confusing at first sight, but is indeed correct. +	 * +	 * The rational is that new write operations needs to wait for the +	 * existing read and write operations to finish. +	 * But a new read operation only needs to wait for the existing write +	 * operations to finish. +	 */ +	return write ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE; +} + +/**   * struct dma_resv - a reservation object manages fences for a buffer   * - * There are multiple uses for this, with sometimes slightly different rules in - * how the fence slots are used. + * This is a container for dma_fence objects which needs to handle multiple use + * cases.   *   * One use is to synchronize cross-driver access to a struct dma_buf, either for   * dynamic buffer management or just to handle implicit synchronization between @@ -80,50 +156,16 @@ struct dma_resv {  	struct ww_mutex lock;  	/** -	 * @seq: -	 * -	 * Sequence count for managing RCU read-side synchronization, allows -	 * read-only access to @fence_excl and @fence while ensuring we take a -	 * consistent snapshot. -	 */ -	seqcount_ww_mutex_t seq; - -	/** -	 * @fence_excl: +	 * @fences:  	 * -	 * The exclusive fence, if there is one currently. +	 * Array of fences which where added to the dma_resv object  	 * -	 * To guarantee that no fences are lost, this new fence must signal -	 * only after the previous exclusive fence has signalled. If -	 * semantically only a new access is added without actually treating the -	 * previous one as a dependency the exclusive fences can be strung -	 * together using struct dma_fence_chain. -	 * -	 * Note that actual semantics of what an exclusive or shared fence mean -	 * is defined by the user, for reservation objects shared across drivers -	 * see &dma_buf.resv. -	 */ -	struct dma_fence __rcu *fence_excl; - -	/** -	 * @fence: -	 * -	 * List of current shared fences. -	 * -	 * There are no ordering constraints of shared fences against the -	 * exclusive fence slot. If a waiter needs to wait for all access, it -	 * has to wait for both sets of fences to signal. -	 * -	 * A new fence is added by calling dma_resv_add_shared_fence(). Since -	 * this often needs to be done past the point of no return in command +	 * A new fence is added by calling dma_resv_add_fence(). Since this +	 * often needs to be done past the point of no return in command  	 * submission it cannot fail, and therefore sufficient slots need to be  	 * reserved by calling dma_resv_reserve_fences(). -	 * -	 * Note that actual semantics of what an exclusive or shared fence mean -	 * is defined by the user, for reservation objects shared across drivers -	 * see &dma_buf.resv.  	 */ -	struct dma_resv_list __rcu *fence; +	struct dma_resv_list __rcu *fences;  };  /** @@ -142,14 +184,14 @@ struct dma_resv_iter {  	/** @obj: The dma_resv object we iterate over */  	struct dma_resv *obj; -	/** @all_fences: If all fences should be returned */ -	bool all_fences; +	/** @usage: Return fences with this usage or lower. */ +	enum dma_resv_usage usage;  	/** @fence: the currently handled fence */  	struct dma_fence *fence; -	/** @seq: sequence number to check for modifications */ -	unsigned int seq; +	/** @fence_usage: the usage of the current fence */ +	enum dma_resv_usage fence_usage;  	/** @index: index into the shared fences */  	unsigned int index; @@ -157,8 +199,8 @@ struct dma_resv_iter {  	/** @fences: the shared fences; private, *MUST* not dereference  */  	struct dma_resv_list *fences; -	/** @shared_count: number of shared fences */ -	unsigned int shared_count; +	/** @num_fences: number of fences */ +	unsigned int num_fences;  	/** @is_restarted: true if this is the first returned fence */  	bool is_restarted; @@ -173,14 +215,14 @@ struct dma_fence *dma_resv_iter_next(struct dma_resv_iter *cursor);   * dma_resv_iter_begin - initialize a dma_resv_iter object   * @cursor: The dma_resv_iter object to initialize   * @obj: The dma_resv object which we want to iterate over - * @all_fences: If all fences should be returned or just the exclusive one + * @usage: controls which fences to include, see enum dma_resv_usage.   */  static inline void dma_resv_iter_begin(struct dma_resv_iter *cursor,  				       struct dma_resv *obj, -				       bool all_fences) +				       enum dma_resv_usage usage)  {  	cursor->obj = obj; -	cursor->all_fences = all_fences; +	cursor->usage = usage;  	cursor->fence = NULL;  } @@ -197,14 +239,15 @@ static inline void dma_resv_iter_end(struct dma_resv_iter *cursor)  }  /** - * dma_resv_iter_is_exclusive - test if the current fence is the exclusive one + * dma_resv_iter_usage - Return the usage of the current fence   * @cursor: the cursor of the current position   * - * Returns true if the currently returned fence is the exclusive one. + * Returns the usage of the currently processed fence.   */ -static inline bool dma_resv_iter_is_exclusive(struct dma_resv_iter *cursor) +static inline enum dma_resv_usage +dma_resv_iter_usage(struct dma_resv_iter *cursor)  { -	return cursor->index == 0; +	return cursor->fence_usage;  }  /** @@ -241,7 +284,7 @@ static inline bool dma_resv_iter_is_restarted(struct dma_resv_iter *cursor)   * dma_resv_for_each_fence - fence iterator   * @cursor: a struct dma_resv_iter pointer   * @obj: a dma_resv object pointer - * @all_fences: true if all fences should be returned + * @usage: controls which fences to return   * @fence: the current fence   *   * Iterate over the fences in a struct dma_resv object while holding the @@ -250,8 +293,8 @@ static inline bool dma_resv_iter_is_restarted(struct dma_resv_iter *cursor)   * valid as long as the lock is held and so no extra reference to the fence is   * taken.   */ -#define dma_resv_for_each_fence(cursor, obj, all_fences, fence)	\ -	for (dma_resv_iter_begin(cursor, obj, all_fences),	\ +#define dma_resv_for_each_fence(cursor, obj, usage, fence)	\ +	for (dma_resv_iter_begin(cursor, obj, usage),	\  	     fence = dma_resv_iter_first(cursor); fence;	\  	     fence = dma_resv_iter_next(cursor)) @@ -259,9 +302,9 @@ static inline bool dma_resv_iter_is_restarted(struct dma_resv_iter *cursor)  #define dma_resv_assert_held(obj) lockdep_assert_held(&(obj)->lock.base)  #ifdef CONFIG_DEBUG_MUTEXES -void dma_resv_reset_shared_max(struct dma_resv *obj); +void dma_resv_reset_max_fences(struct dma_resv *obj);  #else -static inline void dma_resv_reset_shared_max(struct dma_resv *obj) {} +static inline void dma_resv_reset_max_fences(struct dma_resv *obj) {}  #endif  /** @@ -407,25 +450,26 @@ static inline struct ww_acquire_ctx *dma_resv_locking_ctx(struct dma_resv *obj)   */  static inline void dma_resv_unlock(struct dma_resv *obj)  { -	dma_resv_reset_shared_max(obj); +	dma_resv_reset_max_fences(obj);  	ww_mutex_unlock(&obj->lock);  }  void dma_resv_init(struct dma_resv *obj);  void dma_resv_fini(struct dma_resv *obj);  int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences); -void dma_resv_add_shared_fence(struct dma_resv *obj, struct dma_fence *fence); +void dma_resv_add_fence(struct dma_resv *obj, struct dma_fence *fence, +			enum dma_resv_usage usage);  void dma_resv_replace_fences(struct dma_resv *obj, uint64_t context, -			     struct dma_fence *fence); -void dma_resv_add_excl_fence(struct dma_resv *obj, struct dma_fence *fence); -int dma_resv_get_fences(struct dma_resv *obj, bool write, +			     struct dma_fence *fence, +			     enum dma_resv_usage usage); +int dma_resv_get_fences(struct dma_resv *obj, enum dma_resv_usage usage,  			unsigned int *num_fences, struct dma_fence ***fences); -int dma_resv_get_singleton(struct dma_resv *obj, bool write, +int dma_resv_get_singleton(struct dma_resv *obj, enum dma_resv_usage usage,  			   struct dma_fence **fence);  int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src); -long dma_resv_wait_timeout(struct dma_resv *obj, bool wait_all, bool intr, -			   unsigned long timeout); -bool dma_resv_test_signaled(struct dma_resv *obj, bool test_all); +long dma_resv_wait_timeout(struct dma_resv *obj, enum dma_resv_usage usage, +			   bool intr, unsigned long timeout); +bool dma_resv_test_signaled(struct dma_resv *obj, enum dma_resv_usage usage);  void dma_resv_describe(struct dma_resv *obj, struct seq_file *seq);  #endif /* _LINUX_RESERVATION_H */ diff --git a/include/linux/fb.h b/include/linux/fb.h index 9a77ab615c36..f95da1af9ff6 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -450,7 +450,6 @@ struct fb_info {  	struct fb_var_screeninfo var;	/* Current var */  	struct fb_fix_screeninfo fix;	/* Current fix */  	struct fb_monspecs monspecs;	/* Current Monitor specs */ -	struct work_struct queue;	/* Framebuffer event queue */  	struct fb_pixmap pixmap;	/* Image hardware mapper */  	struct fb_pixmap sprite;	/* Cursor hardware mapper */  	struct fb_cmap cmap;		/* Current cmap */ diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index 37ded6b8fee6..3926e9027947 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -17,7 +17,6 @@  #include <linux/kcsan-checks.h>  #include <linux/lockdep.h>  #include <linux/mutex.h> -#include <linux/ww_mutex.h>  #include <linux/preempt.h>  #include <linux/spinlock.h> @@ -164,7 +163,7 @@ static inline void seqcount_lockdep_reader_access(const seqcount_t *s)   * static initializer or init function. This enables lockdep to validate   * that the write side critical section is properly serialized.   * - * LOCKNAME:	raw_spinlock, spinlock, rwlock, mutex, or ww_mutex. + * LOCKNAME:	raw_spinlock, spinlock, rwlock or mutex   */  /* @@ -184,7 +183,6 @@ static inline void seqcount_lockdep_reader_access(const seqcount_t *s)  #define seqcount_spinlock_init(s, lock)		seqcount_LOCKNAME_init(s, lock, spinlock)  #define seqcount_rwlock_init(s, lock)		seqcount_LOCKNAME_init(s, lock, rwlock)  #define seqcount_mutex_init(s, lock)		seqcount_LOCKNAME_init(s, lock, mutex) -#define seqcount_ww_mutex_init(s, lock)		seqcount_LOCKNAME_init(s, lock, ww_mutex)  /*   * SEQCOUNT_LOCKNAME()	- Instantiate seqcount_LOCKNAME_t and helpers @@ -277,7 +275,6 @@ SEQCOUNT_LOCKNAME(raw_spinlock, raw_spinlock_t,  false,    s->lock,        raw_s  SEQCOUNT_LOCKNAME(spinlock,     spinlock_t,      __SEQ_RT, s->lock,        spin,     spin_lock(s->lock))  SEQCOUNT_LOCKNAME(rwlock,       rwlock_t,        __SEQ_RT, s->lock,        read,     read_lock(s->lock))  SEQCOUNT_LOCKNAME(mutex,        struct mutex,    true,     s->lock,        mutex,    mutex_lock(s->lock)) -SEQCOUNT_LOCKNAME(ww_mutex,     struct ww_mutex, true,     &s->lock->base, ww_mutex, ww_mutex_lock(s->lock, NULL))  /*   * SEQCNT_LOCKNAME_ZERO - static initializer for seqcount_LOCKNAME_t @@ -304,8 +301,7 @@ SEQCOUNT_LOCKNAME(ww_mutex,     struct ww_mutex, true,     &s->lock->base, ww_mu  	__seqprop_case((s),	raw_spinlock,	prop),			\  	__seqprop_case((s),	spinlock,	prop),			\  	__seqprop_case((s),	rwlock,		prop),			\ -	__seqprop_case((s),	mutex,		prop),			\ -	__seqprop_case((s),	ww_mutex,	prop)) +	__seqprop_case((s),	mutex,		prop))  #define seqprop_ptr(s)			__seqprop(s, ptr)  #define seqprop_sequence(s)		__seqprop(s, sequence) diff --git a/kernel/futex/futex.h b/kernel/futex/futex.h index c264cbeab71c..b5379c0e6d6d 100644 --- a/kernel/futex/futex.h +++ b/kernel/futex/futex.h @@ -3,6 +3,7 @@  #define _FUTEX_H  #include <linux/futex.h> +#include <linux/rtmutex.h>  #include <linux/sched/wake_q.h>  #ifdef CONFIG_PREEMPT_RT  | 
