mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] drm/imagination: Firmware interface cleanup and improvements
@ 2026-09-10  7:56 Alexandru Dadu
  2026-09-10  7:56 ` [PATCH 1/2] drm/imagination: Collect KCCB initialisation Alexandru Dadu
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Alexandru Dadu @ 2026-09-10  7:56 UTC (permalink / raw)
  To: Alessio Belle, Luigi Santivetti, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter
  Cc: imagination, dri-devel, linux-kernel, Alexandru Dadu, Matt Coster

Series contains cleanup commits targeted to FW interface.
    - Avoid initialization of never used data.
    - KCCB member initialized in the correct place.

Signed-off-by: Alexandru Dadu <alexandru.dadu@imgtec.com>
---
Alessio Belle (1):
      drm/imagination: Avoid initialisation of unused FW trace buffer pointer

Matt Coster (1):
      drm/imagination: Collect KCCB initialisation

 drivers/gpu/drm/imagination/pvr_ccb.c      | 38 ++++++++++++++++++++++++++----
 drivers/gpu/drm/imagination/pvr_fw.c       | 17 +------------
 drivers/gpu/drm/imagination/pvr_fw_trace.c |  1 -
 3 files changed, 35 insertions(+), 21 deletions(-)
---
base-commit: bd4f284df04d76fd65e57141cb1e6e7a49e4c3cb
change-id: 20260909-b4-avoid-init-of-unused-fw-trace-buffer-pointer-ad76e5b57945

Best regards,
--  
Alexandru Dadu <alexandru.dadu@imgtec.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/2] drm/imagination: Collect KCCB initialisation
  2026-09-10  7:56 [PATCH 0/2] drm/imagination: Firmware interface cleanup and improvements Alexandru Dadu
@ 2026-09-10  7:56 ` Alexandru Dadu
  2026-09-14 14:18   ` Alessio Belle
  2026-09-10  7:56 ` [PATCH 2/2] drm/imagination: Avoid initialisation of unused FW trace buffer pointer Alexandru Dadu
  2026-09-15 15:13 ` [PATCH 0/2] drm/imagination: Firmware interface cleanup and improvements Alessio Belle
  2 siblings, 1 reply; 6+ messages in thread
From: Alexandru Dadu @ 2026-09-10  7:56 UTC (permalink / raw)
  To: Alessio Belle, Luigi Santivetti, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter
  Cc: imagination, dri-devel, linux-kernel, Alexandru Dadu, Matt Coster

From: Matt Coster <matt.coster@imgtec.com>

There's one FW object attached to KCCB usage (the return buffer) that is
separately initialised in pvr_fw_init(). Move this initialisation to
pvr_kccb_init() alongside the initialisation of rest of the members of
struct pvr_device->kccb.

Signed-off-by: Matt Coster <matt.coster@imgtec.com>
Signed-off-by: Alexandru Dadu <alexandru.dadu@imgtec.com>
---
 drivers/gpu/drm/imagination/pvr_ccb.c | 38 +++++++++++++++++++++++++++++++----
 drivers/gpu/drm/imagination/pvr_fw.c  | 17 +---------------
 2 files changed, 35 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/imagination/pvr_ccb.c b/drivers/gpu/drm/imagination/pvr_ccb.c
index b702d122d791..3b5942b674be 100644
--- a/drivers/gpu/drm/imagination/pvr_ccb.c
+++ b/drivers/gpu/drm/imagination/pvr_ccb.c
@@ -17,6 +17,7 @@
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/mutex.h>
+#include <linux/overflow.h>
 #include <linux/types.h>
 #include <linux/workqueue.h>
 
@@ -527,6 +528,7 @@ void pvr_kccb_wake_up_waiters(struct pvr_device *pvr_dev)
  */
 void pvr_kccb_fini(struct pvr_device *pvr_dev)
 {
+	pvr_fw_object_unmap_and_destroy(pvr_dev->kccb.rtn_obj);
 	pvr_ccb_fini(&pvr_dev->kccb.ccb);
 	WARN_ON(!list_empty(&pvr_dev->kccb.waiters));
 	WARN_ON(pvr_dev->kccb.reserved_count);
@@ -543,14 +545,42 @@ void pvr_kccb_fini(struct pvr_device *pvr_dev)
 int
 pvr_kccb_init(struct pvr_device *pvr_dev)
 {
-	pvr_dev->kccb.slot_count = 1 << ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT;
+	const u32 num_slots_log2 = ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT;
+	const u32 num_slots = 1 << num_slots_log2;
+	u32 rtn_size;
+	int err;
+
+	/*
+	 * The inputs here are compile-time constants; there's no reason to try
+	 * to gracefully handle overflow at runtime.
+	 */
+	BUILD_BUG_ON(check_mul_overflow(num_slots, sizeof(*pvr_dev->kccb.rtn), &rtn_size));
+
+	pvr_dev->kccb.slot_count = num_slots;
 	INIT_LIST_HEAD(&pvr_dev->kccb.waiters);
 	pvr_dev->kccb.fence_ctx.id = dma_fence_context_alloc(1);
 	spin_lock_init(&pvr_dev->kccb.fence_ctx.lock);
 
-	return pvr_ccb_init(pvr_dev, &pvr_dev->kccb.ccb,
-			    ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT,
-			    sizeof(struct rogue_fwif_kccb_cmd));
+	err = pvr_ccb_init(pvr_dev, &pvr_dev->kccb.ccb, num_slots_log2,
+			   sizeof(struct rogue_fwif_kccb_cmd));
+	if (err)
+		return err;
+
+	/* Allocate memory for KCCB return slots. */
+	pvr_dev->kccb.rtn = pvr_fw_object_create_and_map(pvr_dev, rtn_size,
+							 PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
+							 NULL, NULL, &pvr_dev->kccb.rtn_obj);
+	if (IS_ERR(pvr_dev->kccb.rtn)) {
+		err = PTR_ERR(pvr_dev->kccb.rtn);
+		goto err_ccb_fini;
+	}
+
+	return 0;
+
+err_ccb_fini:
+	pvr_ccb_fini(&pvr_dev->kccb.ccb);
+
+	return err;
 }
 
 /**
diff --git a/drivers/gpu/drm/imagination/pvr_fw.c b/drivers/gpu/drm/imagination/pvr_fw.c
index 850a3ec8e775..cec17352cf90 100644
--- a/drivers/gpu/drm/imagination/pvr_fw.c
+++ b/drivers/gpu/drm/imagination/pvr_fw.c
@@ -945,8 +945,6 @@ pvr_fw_init(struct pvr_device *pvr_dev)
 		[PVR_FW_PROCESSOR_TYPE_RISCV] = &pvr_fw_defs_riscv,
 	};
 
-	u32 kccb_size_log2 = ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT;
-	u32 kccb_rtn_size = (1 << kccb_size_log2) * sizeof(*pvr_dev->kccb.rtn);
 	struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
 	int err;
 
@@ -981,18 +979,9 @@ pvr_fw_init(struct pvr_device *pvr_dev)
 	if (err)
 		goto err_kccb_fini;
 
-	/* Allocate memory for KCCB return slots. */
-	pvr_dev->kccb.rtn = pvr_fw_object_create_and_map(pvr_dev, kccb_rtn_size,
-							 PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
-							 NULL, NULL, &pvr_dev->kccb.rtn_obj);
-	if (IS_ERR(pvr_dev->kccb.rtn)) {
-		err = PTR_ERR(pvr_dev->kccb.rtn);
-		goto err_fwccb_fini;
-	}
-
 	err = pvr_fw_create_structures(pvr_dev);
 	if (err)
-		goto err_kccb_rtn_release;
+		goto err_fwccb_fini;
 
 	err = pvr_fw_start(pvr_dev);
 	if (err)
@@ -1014,9 +1003,6 @@ pvr_fw_init(struct pvr_device *pvr_dev)
 err_destroy_structures:
 	pvr_fw_destroy_structures(pvr_dev);
 
-err_kccb_rtn_release:
-	pvr_fw_object_unmap_and_destroy(pvr_dev->kccb.rtn_obj);
-
 err_fwccb_fini:
 	pvr_ccb_fini(&pvr_dev->fwccb);
 
@@ -1047,7 +1033,6 @@ pvr_fw_fini(struct pvr_device *pvr_dev)
 	WRITE_ONCE(fw_dev->initialised, false);
 
 	pvr_fw_destroy_structures(pvr_dev);
-	pvr_fw_object_unmap_and_destroy(pvr_dev->kccb.rtn_obj);
 
 	/*
 	 * Ensure FWCCB worker has finished executing before destroying FWCCB. The IRQ handler has

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/2] drm/imagination: Avoid initialisation of unused FW trace buffer pointer
  2026-09-10  7:56 [PATCH 0/2] drm/imagination: Firmware interface cleanup and improvements Alexandru Dadu
  2026-09-10  7:56 ` [PATCH 1/2] drm/imagination: Collect KCCB initialisation Alexandru Dadu
@ 2026-09-10  7:56 ` Alexandru Dadu
  2026-09-14 15:48   ` Luigi Santivetti
  2026-09-15 15:13 ` [PATCH 0/2] drm/imagination: Firmware interface cleanup and improvements Alessio Belle
  2 siblings, 1 reply; 6+ messages in thread
From: Alexandru Dadu @ 2026-09-10  7:56 UTC (permalink / raw)
  To: Alessio Belle, Luigi Santivetti, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter
  Cc: imagination, dri-devel, linux-kernel, Alexandru Dadu

From: Alessio Belle <alessio.belle@imgtec.com>

This pointer, which is part of struct rogue_fwif_tracebuf_space (a FW
interface structure), was initialised but never used to access the FW
trace buffer - only the source pointer in the initialisation being
removed is used.

Signed-off-by: Alessio Belle <alessio.belle@imgtec.com>
Signed-off-by: Alexandru Dadu <alexandru.dadu@imgtec.com>
---
 drivers/gpu/drm/imagination/pvr_fw_trace.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c
index 6bb5baa6c41b..813b7b0c8d4e 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_trace.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c
@@ -101,7 +101,6 @@ tracebuf_ctrl_init(void *cpu_ptr, void *priv)
 		pvr_fw_object_get_fw_addr(trace_buffer->buf_obj,
 					  &tracebuf_space->trace_buffer_fw_addr);
 
-		tracebuf_space->trace_buffer = trace_buffer->buf;
 		tracebuf_space->trace_pointer = 0;
 	}
 }

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] drm/imagination: Collect KCCB initialisation
  2026-09-10  7:56 ` [PATCH 1/2] drm/imagination: Collect KCCB initialisation Alexandru Dadu
@ 2026-09-14 14:18   ` Alessio Belle
  0 siblings, 0 replies; 6+ messages in thread
From: Alessio Belle @ 2026-09-14 14:18 UTC (permalink / raw)
  To: Alexandru Dadu
  Cc: Luigi Santivetti, opensource, imagination, tzimmermann, simona,
	dri-devel, linux-kernel, airlied, maarten.lankhorst, mripard

On Thu, 2026-09-10 at 10:56 +0300, Alexandru Dadu wrote:
> From: Matt Coster <matt.coster@imgtec.com>
> 
> There's one FW object attached to KCCB usage (the return buffer) that is
> separately initialised in pvr_fw_init(). Move this initialisation to
> pvr_kccb_init() alongside the initialisation of rest of the members of
> struct pvr_device->kccb.
> 
> Signed-off-by: Matt Coster <matt.coster@imgtec.com>
> Signed-off-by: Alexandru Dadu <alexandru.dadu@imgtec.com>

Reviewed-by: Alessio Belle <alessio.belle@imgtec.com>

Thanks,
Alessio

> ---
>  drivers/gpu/drm/imagination/pvr_ccb.c | 38 +++++++++++++++++++++++++++++++----
>  drivers/gpu/drm/imagination/pvr_fw.c  | 17 +---------------
>  2 files changed, 35 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/gpu/drm/imagination/pvr_ccb.c b/drivers/gpu/drm/imagination/pvr_ccb.c
> index b702d122d791..3b5942b674be 100644
> --- a/drivers/gpu/drm/imagination/pvr_ccb.c
> +++ b/drivers/gpu/drm/imagination/pvr_ccb.c
> @@ -17,6 +17,7 @@
>  #include <linux/jiffies.h>
>  #include <linux/kernel.h>
>  #include <linux/mutex.h>
> +#include <linux/overflow.h>
>  #include <linux/types.h>
>  #include <linux/workqueue.h>
>  
> @@ -527,6 +528,7 @@ void pvr_kccb_wake_up_waiters(struct pvr_device *pvr_dev)
>   */
>  void pvr_kccb_fini(struct pvr_device *pvr_dev)
>  {
> +	pvr_fw_object_unmap_and_destroy(pvr_dev->kccb.rtn_obj);
>  	pvr_ccb_fini(&pvr_dev->kccb.ccb);
>  	WARN_ON(!list_empty(&pvr_dev->kccb.waiters));
>  	WARN_ON(pvr_dev->kccb.reserved_count);
> @@ -543,14 +545,42 @@ void pvr_kccb_fini(struct pvr_device *pvr_dev)
>  int
>  pvr_kccb_init(struct pvr_device *pvr_dev)
>  {
> -	pvr_dev->kccb.slot_count = 1 << ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT;
> +	const u32 num_slots_log2 = ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT;
> +	const u32 num_slots = 1 << num_slots_log2;
> +	u32 rtn_size;
> +	int err;
> +
> +	/*
> +	 * The inputs here are compile-time constants; there's no reason to try
> +	 * to gracefully handle overflow at runtime.
> +	 */
> +	BUILD_BUG_ON(check_mul_overflow(num_slots, sizeof(*pvr_dev->kccb.rtn), &rtn_size));
> +
> +	pvr_dev->kccb.slot_count = num_slots;
>  	INIT_LIST_HEAD(&pvr_dev->kccb.waiters);
>  	pvr_dev->kccb.fence_ctx.id = dma_fence_context_alloc(1);
>  	spin_lock_init(&pvr_dev->kccb.fence_ctx.lock);
>  
> -	return pvr_ccb_init(pvr_dev, &pvr_dev->kccb.ccb,
> -			    ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT,
> -			    sizeof(struct rogue_fwif_kccb_cmd));
> +	err = pvr_ccb_init(pvr_dev, &pvr_dev->kccb.ccb, num_slots_log2,
> +			   sizeof(struct rogue_fwif_kccb_cmd));
> +	if (err)
> +		return err;
> +
> +	/* Allocate memory for KCCB return slots. */
> +	pvr_dev->kccb.rtn = pvr_fw_object_create_and_map(pvr_dev, rtn_size,
> +							 PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
> +							 NULL, NULL, &pvr_dev->kccb.rtn_obj);
> +	if (IS_ERR(pvr_dev->kccb.rtn)) {
> +		err = PTR_ERR(pvr_dev->kccb.rtn);
> +		goto err_ccb_fini;
> +	}
> +
> +	return 0;
> +
> +err_ccb_fini:
> +	pvr_ccb_fini(&pvr_dev->kccb.ccb);
> +
> +	return err;
>  }
>  
>  /**
> diff --git a/drivers/gpu/drm/imagination/pvr_fw.c b/drivers/gpu/drm/imagination/pvr_fw.c
> index 850a3ec8e775..cec17352cf90 100644
> --- a/drivers/gpu/drm/imagination/pvr_fw.c
> +++ b/drivers/gpu/drm/imagination/pvr_fw.c
> @@ -945,8 +945,6 @@ pvr_fw_init(struct pvr_device *pvr_dev)
>  		[PVR_FW_PROCESSOR_TYPE_RISCV] = &pvr_fw_defs_riscv,
>  	};
>  
> -	u32 kccb_size_log2 = ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT;
> -	u32 kccb_rtn_size = (1 << kccb_size_log2) * sizeof(*pvr_dev->kccb.rtn);
>  	struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
>  	int err;
>  
> @@ -981,18 +979,9 @@ pvr_fw_init(struct pvr_device *pvr_dev)
>  	if (err)
>  		goto err_kccb_fini;
>  
> -	/* Allocate memory for KCCB return slots. */
> -	pvr_dev->kccb.rtn = pvr_fw_object_create_and_map(pvr_dev, kccb_rtn_size,
> -							 PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
> -							 NULL, NULL, &pvr_dev->kccb.rtn_obj);
> -	if (IS_ERR(pvr_dev->kccb.rtn)) {
> -		err = PTR_ERR(pvr_dev->kccb.rtn);
> -		goto err_fwccb_fini;
> -	}
> -
>  	err = pvr_fw_create_structures(pvr_dev);
>  	if (err)
> -		goto err_kccb_rtn_release;
> +		goto err_fwccb_fini;
>  
>  	err = pvr_fw_start(pvr_dev);
>  	if (err)
> @@ -1014,9 +1003,6 @@ pvr_fw_init(struct pvr_device *pvr_dev)
>  err_destroy_structures:
>  	pvr_fw_destroy_structures(pvr_dev);
>  
> -err_kccb_rtn_release:
> -	pvr_fw_object_unmap_and_destroy(pvr_dev->kccb.rtn_obj);
> -
>  err_fwccb_fini:
>  	pvr_ccb_fini(&pvr_dev->fwccb);
>  
> @@ -1047,7 +1033,6 @@ pvr_fw_fini(struct pvr_device *pvr_dev)
>  	WRITE_ONCE(fw_dev->initialised, false);
>  
>  	pvr_fw_destroy_structures(pvr_dev);
> -	pvr_fw_object_unmap_and_destroy(pvr_dev->kccb.rtn_obj);
>  
>  	/*
>  	 * Ensure FWCCB worker has finished executing before destroying FWCCB. The IRQ handler has
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] drm/imagination: Avoid initialisation of unused FW trace buffer pointer
  2026-09-10  7:56 ` [PATCH 2/2] drm/imagination: Avoid initialisation of unused FW trace buffer pointer Alexandru Dadu
@ 2026-09-14 15:48   ` Luigi Santivetti
  0 siblings, 0 replies; 6+ messages in thread
From: Luigi Santivetti @ 2026-09-14 15:48 UTC (permalink / raw)
  To: Alexandru Dadu, Alessio Belle, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter
  Cc: imagination, dri-devel, linux-kernel

On Thu, 2026-09-10 at 10:56 +0300, Alexandru Dadu wrote:
> From: Alessio Belle <alessio.belle@imgtec.com>
> 
> This pointer, which is part of struct rogue_fwif_tracebuf_space (a FW
> interface structure), was initialised but never used to access the FW
> trace buffer - only the source pointer in the initialisation being
> removed is used.
> 
> Signed-off-by: Alessio Belle <alessio.belle@imgtec.com>
> Signed-off-by: Alexandru Dadu <alexandru.dadu@imgtec.com>
> ---

Reviewed-by: Luigi Santivetti <luigi.santivetti@imgtec.com>

>  drivers/gpu/drm/imagination/pvr_fw_trace.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c
> b/drivers/gpu/drm/imagination/pvr_fw_trace.c
> index 6bb5baa6c41b..813b7b0c8d4e 100644
> --- a/drivers/gpu/drm/imagination/pvr_fw_trace.c
> +++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c
> @@ -101,7 +101,6 @@ tracebuf_ctrl_init(void *cpu_ptr, void *priv)
>  		pvr_fw_object_get_fw_addr(trace_buffer->buf_obj,
>  					  &tracebuf_space-
> >trace_buffer_fw_addr);
>  
> -		tracebuf_space->trace_buffer = trace_buffer->buf;
>  		tracebuf_space->trace_pointer = 0;
>  	}
>  }
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/2] drm/imagination: Firmware interface cleanup and improvements
  2026-09-10  7:56 [PATCH 0/2] drm/imagination: Firmware interface cleanup and improvements Alexandru Dadu
  2026-09-10  7:56 ` [PATCH 1/2] drm/imagination: Collect KCCB initialisation Alexandru Dadu
  2026-09-10  7:56 ` [PATCH 2/2] drm/imagination: Avoid initialisation of unused FW trace buffer pointer Alexandru Dadu
@ 2026-09-15 15:13 ` Alessio Belle
  2 siblings, 0 replies; 6+ messages in thread
From: Alessio Belle @ 2026-09-15 15:13 UTC (permalink / raw)
  To: Luigi Santivetti, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Alexandru Dadu
  Cc: imagination, dri-devel, linux-kernel, Matt Coster

On Thu, 10 Sep 2026 10:56:38 +0300, Alexandru Dadu wrote:
> Series contains cleanup commits targeted to FW interface.
>     - Avoid initialization of never used data.
>     - KCCB member initialized in the correct place.

Applied to drm-misc-next, thanks!

[1/2] drm/imagination: Collect KCCB initialisation
      commit: 05cce7f9b638536c544328ea135edb864f503aa5
[2/2] drm/imagination: Avoid initialisation of unused FW trace buffer pointer
      commit: 109f4e9010c37966d59780f7c76064581064b7f2

Best regards,
-- 
Alessio Belle <alessio.belle@imgtec.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-15 15:14 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-10  7:56 [PATCH 0/2] drm/imagination: Firmware interface cleanup and improvements Alexandru Dadu
2026-09-10  7:56 ` [PATCH 1/2] drm/imagination: Collect KCCB initialisation Alexandru Dadu
2026-09-14 14:18   ` Alessio Belle
2026-09-10  7:56 ` [PATCH 2/2] drm/imagination: Avoid initialisation of unused FW trace buffer pointer Alexandru Dadu
2026-09-14 15:48   ` Luigi Santivetti
2026-09-15 15:13 ` [PATCH 0/2] drm/imagination: Firmware interface cleanup and improvements Alessio Belle

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®