summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2018-05-08 20:39:46 +1000
committerBen Skeggs <bskeggs@redhat.com>2018-05-18 15:01:21 +1000
commiteb47db4f3bb58b0143a911b29417e89f28e1b0c8 (patch)
treece2ffc0d618234a346c3fb687a3191fedcdc6357
parent6eb01aa8988873167adc5285f4afef310d01b8fb (diff)
downloadlinux-eb47db4f3bb58b0143a911b29417e89f28e1b0c8.tar.gz
linux-eb47db4f3bb58b0143a911b29417e89f28e1b0c8.tar.bz2
linux-eb47db4f3bb58b0143a911b29417e89f28e1b0c8.zip
drm/nouveau/fifo: support channel count query
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl0080.h4
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/device.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_chan.c25
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_chan.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.c11
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.h2
-rw-r--r--drivers/gpu/drm/nouveau/nv04_fence.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv10_fence.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv17_fence.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fence.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv84_fence.c11
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/user.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c14
15 files changed, 64 insertions, 24 deletions
diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl0080.h b/drivers/gpu/drm/nouveau/include/nvif/cl0080.h
index 6a54cda9613e..5af610ea260e 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/cl0080.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/cl0080.h
@@ -57,6 +57,7 @@ struct nv_device_time_v0 {
#define NV_DEVICE_INFO_UNIT (0xffffffffULL << 32)
#define NV_DEVICE_INFO(n) ((n) | (0x00000000ULL << 32))
+#define NV_DEVICE_FIFO(n) ((n) | (0x00000001ULL << 32))
/* This will be returned for unsupported queries. */
#define NV_DEVICE_INFO_INVALID ~0ULL
@@ -79,4 +80,7 @@ struct nv_device_time_v0 {
#define NV_DEVICE_INFO_ENGINE_SEC2 NV_DEVICE_INFO(0x0000000e)
#define NV_DEVICE_INFO_ENGINE_NVDEC NV_DEVICE_INFO(0x0000000f)
#define NV_DEVICE_INFO_ENGINE_NVENC NV_DEVICE_INFO(0x00000010)
+
+/* Returns the number of available channels. */
+#define NV_DEVICE_FIFO_CHANNELS NV_DEVICE_FIFO(0x00000000)
#endif
diff --git a/drivers/gpu/drm/nouveau/include/nvif/device.h b/drivers/gpu/drm/nouveau/include/nvif/device.h
index 6edb6266857e..216dbd9fa616 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/device.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/device.h
@@ -67,6 +67,5 @@ u64 nvif_device_time(struct nvif_device *);
#include <engine/fifo.h>
#include <engine/gr.h>
-#define nvxx_fifo(a) nvxx_device(a)->fifo
#define nvxx_gr(a) nvxx_device(a)->gr
#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c
index af1116655910..db69d13f32a7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_chan.c
+++ b/drivers/gpu/drm/nouveau/nouveau_chan.c
@@ -474,3 +474,28 @@ done:
cli->base.super = super;
return ret;
}
+
+int
+nouveau_channels_init(struct nouveau_drm *drm)
+{
+ struct {
+ struct nv_device_info_v1 m;
+ struct {
+ struct nv_device_info_v1_data channels;
+ } v;
+ } args = {
+ .m.version = 1,
+ .m.count = sizeof(args.v) / sizeof(args.v.channels),
+ .v.channels.mthd = NV_DEVICE_FIFO_CHANNELS,
+ };
+ struct nvif_object *device = &drm->client.device.object;
+ int ret;
+
+ ret = nvif_object_mthd(device, NV_DEVICE_V0_INFO, &args, sizeof(args));
+ if (ret || args.v.channels.mthd == NV_DEVICE_INFO_INVALID)
+ return -ENODEV;
+
+ drm->chan.nr = args.v.channels.data;
+ drm->chan.context_base = dma_fence_context_alloc(drm->chan.nr);
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.h b/drivers/gpu/drm/nouveau/nouveau_chan.h
index 14607c16a2bd..64454c2ebd90 100644
--- a/drivers/gpu/drm/nouveau/nouveau_chan.h
+++ b/drivers/gpu/drm/nouveau/nouveau_chan.h
@@ -45,6 +45,7 @@ struct nouveau_channel {
atomic_t killed;
};
+int nouveau_channels_init(struct nouveau_drm *);
int nouveau_channel_new(struct nouveau_drm *, struct nvif_device *,
u32 arg0, u32 arg1, struct nouveau_channel **);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index bbbf353682e1..dddd42592472 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -307,6 +307,10 @@ nouveau_accel_init(struct nouveau_drm *drm)
if (nouveau_noaccel)
return;
+ ret = nouveau_channels_init(drm);
+ if (ret)
+ return;
+
/* initialise synchronisation routines */
/*XXX: this is crap, but the fence/channel stuff is a little
* backwards in some places. this will be fixed.
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 881b44b89a01..6e1acaec3400 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -170,6 +170,12 @@ struct nouveau_drm {
/* synchronisation */
void *fence;
+ /* Global channel management. */
+ struct {
+ int nr;
+ u64 context_base;
+ } chan;
+
/* context for accelerated drm-internal operations */
struct nouveau_channel *cechan;
struct nouveau_channel *channel;
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 503fa94dc06d..412d49bc6e56 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -74,15 +74,14 @@ nouveau_fence_signal(struct nouveau_fence *fence)
}
static struct nouveau_fence *
-nouveau_local_fence(struct dma_fence *fence, struct nouveau_drm *drm) {
- struct nouveau_fence_priv *priv = (void*)drm->fence;
-
+nouveau_local_fence(struct dma_fence *fence, struct nouveau_drm *drm)
+{
if (fence->ops != &nouveau_fence_ops_legacy &&
fence->ops != &nouveau_fence_ops_uevent)
return NULL;
- if (fence->context < priv->context_base ||
- fence->context >= priv->context_base + priv->contexts)
+ if (fence->context < drm->chan.context_base ||
+ fence->context >= drm->chan.context_base + drm->chan.nr)
return NULL;
return from_fence(fence);
@@ -176,7 +175,7 @@ nouveau_fence_context_new(struct nouveau_channel *chan, struct nouveau_fence_cha
INIT_LIST_HEAD(&fctx->flip);
INIT_LIST_HEAD(&fctx->pending);
spin_lock_init(&fctx->lock);
- fctx->context = priv->context_base + chan->chid;
+ fctx->context = chan->drm->chan.context_base + chan->chid;
if (chan == chan->drm->cechan)
strcpy(fctx->name, "copy engine channel");
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h
index 5bd8d30d1657..b999e6058046 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.h
@@ -55,8 +55,6 @@ struct nouveau_fence_priv {
int (*context_new)(struct nouveau_channel *);
void (*context_del)(struct nouveau_channel *);
- u32 contexts;
- u64 context_base;
bool uevent;
};
diff --git a/drivers/gpu/drm/nouveau/nv04_fence.c b/drivers/gpu/drm/nouveau/nv04_fence.c
index fa8f2375c398..c41e82be4893 100644
--- a/drivers/gpu/drm/nouveau/nv04_fence.c
+++ b/drivers/gpu/drm/nouveau/nv04_fence.c
@@ -109,7 +109,5 @@ nv04_fence_create(struct nouveau_drm *drm)
priv->base.dtor = nv04_fence_destroy;
priv->base.context_new = nv04_fence_context_new;
priv->base.context_del = nv04_fence_context_del;
- priv->base.contexts = 15;
- priv->base.context_base = dma_fence_context_alloc(priv->base.contexts);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv10_fence.c b/drivers/gpu/drm/nouveau/nv10_fence.c
index 2998bde29211..4476b712dc84 100644
--- a/drivers/gpu/drm/nouveau/nv10_fence.c
+++ b/drivers/gpu/drm/nouveau/nv10_fence.c
@@ -103,8 +103,6 @@ nv10_fence_create(struct nouveau_drm *drm)
priv->base.dtor = nv10_fence_destroy;
priv->base.context_new = nv10_fence_context_new;
priv->base.context_del = nv10_fence_context_del;
- priv->base.contexts = 31;
- priv->base.context_base = dma_fence_context_alloc(priv->base.contexts);
spin_lock_init(&priv->lock);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv17_fence.c b/drivers/gpu/drm/nouveau/nv17_fence.c
index 6477b7069e14..5d613d43b84d 100644
--- a/drivers/gpu/drm/nouveau/nv17_fence.c
+++ b/drivers/gpu/drm/nouveau/nv17_fence.c
@@ -125,8 +125,6 @@ nv17_fence_create(struct nouveau_drm *drm)
priv->base.resume = nv17_fence_resume;
priv->base.context_new = nv17_fence_context_new;
priv->base.context_del = nv10_fence_context_del;
- priv->base.contexts = 31;
- priv->base.context_base = dma_fence_context_alloc(priv->base.contexts);
spin_lock_init(&priv->lock);
ret = nouveau_bo_new(&drm->client, 4096, 0x1000, TTM_PL_FLAG_VRAM,
diff --git a/drivers/gpu/drm/nouveau/nv50_fence.c b/drivers/gpu/drm/nouveau/nv50_fence.c
index a369d978e267..a00ecc3de053 100644
--- a/drivers/gpu/drm/nouveau/nv50_fence.c
+++ b/drivers/gpu/drm/nouveau/nv50_fence.c
@@ -78,8 +78,6 @@ nv50_fence_create(struct nouveau_drm *drm)
priv->base.resume = nv17_fence_resume;
priv->base.context_new = nv50_fence_context_new;
priv->base.context_del = nv10_fence_context_del;
- priv->base.contexts = 127;
- priv->base.context_base = dma_fence_context_alloc(priv->base.contexts);
spin_lock_init(&priv->lock);
ret = nouveau_bo_new(&drm->client, 4096, 0x1000, TTM_PL_FLAG_VRAM,
diff --git a/drivers/gpu/drm/nouveau/nv84_fence.c b/drivers/gpu/drm/nouveau/nv84_fence.c
index 5f0c0c27d5dc..090664899247 100644
--- a/drivers/gpu/drm/nouveau/nv84_fence.c
+++ b/drivers/gpu/drm/nouveau/nv84_fence.c
@@ -141,9 +141,9 @@ nv84_fence_suspend(struct nouveau_drm *drm)
struct nv84_fence_priv *priv = drm->fence;
int i;
- priv->suspend = vmalloc(priv->base.contexts * sizeof(u32));
+ priv->suspend = vmalloc(drm->chan.nr * sizeof(u32));
if (priv->suspend) {
- for (i = 0; i < priv->base.contexts; i++)
+ for (i = 0; i < drm->chan.nr; i++)
priv->suspend[i] = nouveau_bo_rd32(priv->bo, i*4);
}
@@ -157,7 +157,7 @@ nv84_fence_resume(struct nouveau_drm *drm)
int i;
if (priv->suspend) {
- for (i = 0; i < priv->base.contexts; i++)
+ for (i = 0; i < drm->chan.nr; i++)
nouveau_bo_wr32(priv->bo, i*4, priv->suspend[i]);
vfree(priv->suspend);
priv->suspend = NULL;
@@ -179,7 +179,6 @@ nv84_fence_destroy(struct nouveau_drm *drm)
int
nv84_fence_create(struct nouveau_drm *drm)
{
- struct nvkm_fifo *fifo = nvxx_fifo(&drm->client.device);
struct nv84_fence_priv *priv;
u32 domain;
int ret;
@@ -194,8 +193,6 @@ nv84_fence_create(struct nouveau_drm *drm)
priv->base.context_new = nv84_fence_context_new;
priv->base.context_del = nv84_fence_context_del;
- priv->base.contexts = fifo->nr;
- priv->base.context_base = dma_fence_context_alloc(priv->base.contexts);
priv->base.uevent = true;
mutex_init(&priv->mutex);
@@ -207,7 +204,7 @@ nv84_fence_create(struct nouveau_drm *drm)
* will lose CPU/GPU coherency!
*/
TTM_PL_FLAG_TT | TTM_PL_FLAG_UNCACHED;
- ret = nouveau_bo_new(&drm->client, 16 * priv->base.contexts, 0,
+ ret = nouveau_bo_new(&drm->client, 16 * drm->chan.nr, 0,
domain, 0, 0, NULL, NULL, &priv->bo);
if (ret == 0) {
ret = nouveau_bo_pin(priv->bo, domain, false);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/user.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/user.c
index 42a552d314ef..600bdb870462 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/user.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/user.c
@@ -46,6 +46,7 @@ nvkm_udevice_info_subdev(struct nvkm_device *device, u64 mthd, u64 *data)
enum nvkm_devidx subidx;
switch (mthd & NV_DEVICE_INFO_UNIT) {
+ case NV_DEVICE_FIFO(0): subidx = NVKM_ENGINE_FIFO; break;
default:
return -EINVAL;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c
index 64f6b7654a08..49b37a8a94b7 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c
@@ -30,6 +30,7 @@
#include <subdev/mc.h>
#include <nvif/event.h>
+#include <nvif/cl0080.h>
#include <nvif/unpack.h>
void
@@ -271,6 +272,18 @@ nvkm_fifo_fini(struct nvkm_engine *engine, bool suspend)
}
static int
+nvkm_fifo_info(struct nvkm_engine *engine, u64 mthd, u64 *data)
+{
+ struct nvkm_fifo *fifo = nvkm_fifo(engine);
+ switch (mthd) {
+ case NV_DEVICE_FIFO_CHANNELS: *data = fifo->nr; return 0;
+ default:
+ break;
+ }
+ return -ENOSYS;
+}
+
+static int
nvkm_fifo_oneinit(struct nvkm_engine *engine)
{
struct nvkm_fifo *fifo = nvkm_fifo(engine);
@@ -311,6 +324,7 @@ nvkm_fifo = {
.dtor = nvkm_fifo_dtor,
.preinit = nvkm_fifo_preinit,
.oneinit = nvkm_fifo_oneinit,
+ .info = nvkm_fifo_info,
.init = nvkm_fifo_init,
.fini = nvkm_fifo_fini,
.intr = nvkm_fifo_intr,
2"=u:yPf1cTemalMm|\CIiO 8#|Cߩ׌r!ΘB\|Xک90Hn;xvsbD}0n?GFv?P4LTv&ZQZJE}Ӂ'MT[ %IHx{e(׺B0n;mOm"y0Z.() S ~ݻݼ50U ڨ(7' AQ|͸q~ÙOn2י!aři~NN ~k֔= E߯O׳sT <"T,bY+xsmӇb<,Gڳ=}}gZ]k7-1ӹ^}zu2@I/qo)ML h&a/BN!/|G{>[T&ϩ?"H&rZ?'Va۫l5G9;k|W aBD3gT_83y -qF| hJdODp-ZuEHk K7 7GV HiBL0U2Ckf;a/:H֕tyɥ೴}z?CMA<_TY<#*kT9v߂(wMD_nY0t`B3 o~2&taAOv?MHHۣzy*AAh);(`Pl%f}t>kHF#=csJ틼VXԝ.= cja# gY.XS)-iWKFCe6)cCXfaIHK"i;!+)Q KZ{3[Xv8(G"Á ύ+ OǛi_,v3?R ,`9N] oe+tMDneTK&&"%X{6OVspBdۧg(E({O}lJ]>6=me,Wal&6WHsgu]!I-}HtDS)K6"Jt ~xYJ#ql'paFlBn7Af.|rvp8*uXUcqHj5h,tha3.ra>湗F0,GٜA,."utIhK("T~^<+<'ؠ:4SV6qf@m6 R0@nɌ%M q*߃X&D1>F;^Z9]wO;~gB[2G׌ONѢ)ʼn0 =mAlSwOrwPHxN] wþX>9ܲ-؂``UK(Ȇ ,_" M&6o^VD}-1|\5M=̿G#ؙ|t<ö!6e4iƒD&GnZ˼ n(?׍x*7(Tc.CZv,%sy3koH;*7(EN 7;!GPJ<V5hة.|q0(0./! %ԣ4HJMQœ^Y4j*{,xhwrt:ǜXBP0쑲iU,Z9aς)(b2F]H)X Ec?)0uˁFn.āCĈӐ߄?3 [SasH$׭CktZ_|T˺Kf^e\J y88,v˞0? ?-92-BBy] T؁֠(~N||/;܁!|Ҷ͍)~]j5o8wQrg_ֿlk_M-Wy0a;|;?h 9sOTJ9B9˒nG=,X|P<-oa8;^R0í(#r:~}5>eE1%$p;}ѢHBtyctNGZf~R|7du - Nf3e* HHnM#vIY*z踻$!Q'?hg%Bvxh̬P~ndFj)K Ut3[uyL1PVaQX⣯ BA}O'6/bo<-5>SH'M~5߰?CB#.@Y۞"ԤIEKYY2tYz_RTt>$>XQABXi q36o$9}Z[fH0t/P?/~R|3.q.H]q+ V֢eLq[< =omoX]l [$ 3QbC8qΏ1|4ҵ2>$n^Ł?{=I?/=~߷? Yxxl a啥w>!K2q}[ŸPT?񫡪 y狹5 L|ufz\x3]0f4C8X#f0] *ơlh'U"i pϿP z'ݖs{Q;sCB}_uæW_O<#f@. F߸1m"?gr?k}vj7_ɏeф2M_񨏣̊raՀ1Vq7;[Ğ"G3vƽrm%@@u389GۅW0BdFy<3w2[-#?k-**w!Ij 1VXyhK+vF^F)2"Y\JDlG'=,*tU=5K}z?o*W`tўy~'zSaeϒH)4{OFWfU jZ߹G }5Y[UQ]|^MQ7 ֆJS1!˄ӿmRkp쉃8<ާjV.9#k{x\+Q #`\{ƙ8 *${OWm S- '0("D.Irq5V#(omSl\ 1^1\k襑 Lm:]o_c-7ZBrV>8 r fkUM`B] {< &iRյ6ACg;@^ &|TWEx.R!5"-ޤcjzי%^!-<39X>*cP/G$htzf*anxءZJސ+ɶI^L†P}xF6i"5LpcP,ʓgk3[&Y>F{S+rF3 vNY jO}Cd `,υH]uӿTQ?}x}.spE5qSzhNLP -M-rf^t p^2pd Ek> -׆૎ o"8aV5LyC~\վᡊMA|لwL0ࠦj<Ǘa@+ ktVs$ͬ=m"@A`ƻrzp'.b>mI ~Cǻfg %d7W9_>?vL9 7K3t 0tnknq|t_Fm*LTFЉ9?o#Y7"iO]KN3s0jmIӫP (T}{!u}e0<2ՄD33B!?T2QKdą6^)^NM 2iCMsa[e1?5_v'k?9YF &Hn3SV^>W-0"ީ9O,.Lۛ_A,)xF}|4^Bi;y[tAFݞ%x$?|qX &oǓ(>Oѽ y]/]^)󋄚3J΃o|!TduιTg$(A)|(:XII16؋70n3)K|,jLd^`/4u=vkSꉜGQBm =c+O9AěQZN&XܦLpdsƱ {|tqSwcs];ڜD])lsѶڥx|A07.iPᩝ-A529N';9+RTq2xZVb(̅1" s- z +lwyRNELeg8IOgԹJ8Λ j`(>"ܴլ]JU >~'d3{@җg:Mu w, c]])--3WEVCU#\kC!U!FɭZ3ci>亪׵Pn܉)K!.gOF*sd;C@noΜӇ icdIxX{ui"UT swiM=+1;OMAqMzaҜ$Lf|Şcs|m%·q:柭pWKfV?Mw$DΝ\oy6~XΧ2.b%r#ˆK$#cteÉɩx|]s \amB82Ʋ$|U.G9K:xdWͲn _5UϿSu:519(SYwtq;ҙƫ pjl1rruۜ>㛦]t[3<\8}8k\9(S]R 7YGnk=޷Z͉<-}_8l2M]?/9QŻc6\E)Z?F5U|ή3ǥ{XlDCIgg)wLp E4J(֯ڗvw0Ou)/BLﳵQKhɔy.|g$ هcɽ:1ϑOi.Vgh/ n3@LN9BY43T!ƜZ(`WyFeky'/tMg,XxZ8D:?f9Fu05c]4q&k1`N"\Ѝr4Ñ1ZJø~Htr[[[]&N=`_m"s|w\ϭ w {ߎmr L8a:&+hr<KAޡX` {{] ^3ӼKt%pI]@ҁ(pN <: w8L6q53Fb2M&Vf ݴdۄa#1d6D2-+n"MU'sd1s[`VHʤ?FˣoZ^"pYqFޏ~N"ByPzR~*):Fiucd!#V_kDQ++V;pV.i/KJjNsXfF#BgE)#ޏ&î(>1i"!ɼ[/թyɚV< F"j:ry)~ ;qG^υ!8nE1)L~+ (|9UFq!QGPVj̯Ƃ!M4~=4lY(&TGm5DoQe"tEv`g 9)NG(ẇ*܄F|\^Pi7 FUN/-C =O "4ܪs(MWS'3Wo +v63fn9a6ܔ7zMhwȹq̑Sab a|gD4Njއ.P[|b<m>vtʢ衿+Q6|yWQPVҜ <#/3xB$~c6M*P6P* 8vqЧVq^:}yOKkAIBXuFozGiv{gwv 1/~o{TQ2$a?kQ9ɀNP\~Ph:]U5/pfF1 HBGhdhh5*asZ9utfGjTDAqJ<}掯W=㙊m4AgLw;j$XN4V(nF3L:%`t5ͬ3.GI3Y{ۜU ?6P0Qe/6잍Fw2ÛOw0Қ}b>r^pfȃrs_Q@{ڟ~AE[FFʫl;/sGsF{_auD]J)oF0ni}*`ng}Qq[>++-281R A0"'H}Pv d#K,{!` .h ^s"\0v@WZFAA gu"-L*}2׀`zy}GlaBdpe mj=h(.4Gܠ(f&#ѹP5-5j9^FQhm?Ҷ8};#{Yu"ۺPEh==N)=\5>G];2"/jsd`\r..OW}t woP:c~cjOm[Nsz:]d'oUO樦,H#Ϩ]b$G,ibxs i|KNt.P 1JIs~r &oA`QvܵBo|'ϴ GY ]9H__PpϳdI~yy8Y"y~X?s> 51mH,^_J&1>zRm_8N1-?ox>"$"X=ӵ ƫlBryc0h# oR(TsgD^;֔nCTw,g-c$py/ENC |MO|݇!EŤFK 2Իcn^ KH\2I]*odh0> ej;G;PB1Cúo'o~ (>v\Dآ=4hq2BpΛă? ?]CȁhWqA:׹v^ǢM śVGXGC_'E *䷾?G}oBMW2FJo199u쀭1.}絚e"=6ڮJc~"^ "zVMµHjeg/8wBx2akݹGk8W͇.i}IKlU۳=Krэ6<3V[JGoė .jL*\'~Źަ0>\k)!5iI߽BUKQ /|E`aJu]yüoTbԡ7Ϭ%\ !cq w&b߯.EU>bkVpBcG:Om|eeרc y}H%!kK$.S3rCV,Ϗvjϕ!܉;'ޏ~M;P$ nP%"_X *<%${XEk4 od8 ^f(Wӵz}BL&`wM HF7'1НsO7l|vqDNmN4It" 2e=d6+Г4OPF>A񔲚dyLC)Cidw& T@H` D$B$"S?9({ީ)BУQ K2R"=/@,- R` Z6 uN% u3 s7 A`A9- (Ed_8b[{<@$O{~c;S\_ꁝ-Ni^lf{3P==`+w>7>F\F@_kU_k~1?N^% ?b+;|>,7xyzxm[&өΨϽ,_ȯGnenR6BD')$NM" hfh$l34k4v;gѮg3 X! p-6#ˀ6`Ba~J~y5FC#ƌ!Ips~>O'`!_=^B=?%P;7kspᖼ;Σ1_@_:vF{,J۸Avݮ1NZ/B:M3!!}Fo/>Ř cӲx<}|;1l8#^z(?O>y"9bp { ]."W޶o=#cfՀ[(U =;[t@J)ہw+o.][ز w8\z~SSi2ĽL>,fy _ƉRY![Lj؝->m5kͷiVZǧJ0㕻7M?bȅ!htZ`(h«~$@l'rIh!sN@?:''nM;7eFx=Џ0XđAE;O @g{Oo#M ~TMf ZoVL~"(Ka_'}Gd>'WҊD~ 6^h&"_/+ױrLFZ vth! /N,,ƿSZ4"&U"OU|C apkg>uQW~߷v isc61Phc_G?N+Y l`< =7rytj0{2diy?}~Xo8 4F\8-κ֗a 9hfH{V$JN|ֳ ngk[G]~{B|쬼]WwAd}kZGwg]Ͼ3mRkFIRέAoKzl7}vGʣe-y;Ƹ_>sX̫@moQYX4IX]c8~ .O;cB|7IП%}H JƒV}Q)TE]}|iu8{c E|wD՝=>n/PLV!ܽ%\dW3L*š5˽W֥3#Zv}#C٪qW[ôh&ܘ)A&InBƊc.-PT̢+ȇ1<忥J79UQnV5wLL| =:S2>Ki(69 k;||F-h9-qXI*@uIUUr5r`nsh_T1aY})NT{}o+!#9H zܬ-uU?_\hQ@F.{2*L(ůHk7 _zh^j֌f]k Arixw^=/hv*Ch{s Yu-i 1'O.^4Nf=C~_F6z:KQ'lKqyG]R>ma<~w =>/o#{\ܪyGL6r^+\Y[1.uZrœiA dbx,Pi9.].opBHU%z7;%qkդl+5R~H ڗ?z$G;i]x?Fwyr[Lnȣ̆hMoG?͆9`$" 7_9P`N7߰c5n#*::)QyJ =W ϝ9(|ޟ4yr.J|;|9) 4L^,gY0Ȇp!ҿW#tk&*ZX&3i Z"` IO?ܮ%CjC^Dw<3@³pS8ں"թ\.5E{u{8DU p1~ 6Żx,/n}&*+5L63C@~1(–?"ǡ?$RŢZ4I Ski!@K|!]U1F 0`X }>()c t^T8"uh ͉ w|ydhW&VhvyAxAD Iʹmn"qczgK c!b6zJkiN8 o^Uvf.U$HmCu'CQ~/dk/* r_,aJ9ObBQbX~/QOqY8m 6͡yL`<0;z=UI: - SzEu&,N{QXJ{#^XFD9Ӣa|I>Wn4=2Q9 sNv=44[J)u_r9\hmRIG*f{7T~ C(\V5 'aFLb#֭>k{cˀuV$4 cL萕VsUJOBya0X$ zT!3|]h}] }k M{,TaōoȏeubU[a5luUlsɧfROQY4QFz4PfzZ(HWs@;[:C`鍴 b9\Dhqm@ n;ԅ0yohDubF$#"!\ŕu߃GcN_Ndycl͸f݆T˿>ʢZ׭T5%#$!EUCADYJ&*/{gYm\7hŋrlj}il}1PeG I:ɌQ>^PbU[E>:е/mș=6ua:YSt$bS}\(JR5 (jQ50CyX6[ 3UM\L "K>LPS QoήVPר~e| %PmoJU[\Il?W\[ém\ ŎG\Uu[pGwcB87 .ARF?-HTH+[V#yٲ_/b쀟9p:ʅc{CqoG3k(YS5몗^ƒع\T VB=XVq$97d~}³,af>'57ܐٮ+h` QHNO A(FJ_+)uljM0p9qx%9%L pt{S&oeMS\56!ʘTyDHz4d\)HGU]sY5}twjzV˾'т=Yħ ݜ7NʈۋZ>4zKTTEG/{zs~*Gg) ~?WBhCp9k-p®÷꽶s\((>.cFqoyq$|K;]}V]Nܜɘ |z9YKgP t~F",0jS+o^N,*<ϣ.@>QjO]>$M5 |s8BZ]ξ"kE`z,~]񔏚ŞaUIڸ8Km9E$ Ώ[9 Qa~N 8 gj%wL6$d|n[4ds `ɈW9M^_:Gj'IZ=asϒێӺl7080*809;)mz A~,_}:տ75 ƿ!X `xDMT1D$6U2FehHܗ$H.*'ݯMmHhTP@ 6r: ^vHpCJtدgSoC/`pH|ظi'˲*;c:Csf2i2!Mj)64"FKBÌZ1h lxbF\VȠZY4Ankk{ G ҍCvi?,e)mF1Mᦆ̀O\6[(9NA@7tyR,3,740i7^ւz&6NɀdTqGR EC oAD,Wa; M(`:7=V~g}8?[s[5Y '(z[-^kGw?)|OgWB? W#fn脍p0O|]n᧺(CIc\}TwWY7hŖCup-b7#`-e;@fhD~)yԶ>t 891m nvo0}ڕA$RW uz9$ȽFPKwNᮣK-~׵]'5aaO3 rX}cїLC}6{ٓʕ`كd)>4pڑ9ՁaMOAdž쩛24dМMֻ-jS Kw|Fp Djs4eZbkw~Taʲ.9Yn6ZTnvdF[ ,t-kPE)Rvd=@K:N _75%GP؉8[9YA|(|N4NAblOЌ"ÿ\,{u8 n;QD/HVcGYs||$G;X\$Wn1:չn%n u*>\vw|[L ÷1Y}Racv$|{8_ҍmVo϶{f66e4OǹR%zik.r9W-TLJ|{Ny/5b.7…p꣑<F҆F^-e4cq^_6lƋhjP[Ayf׏b&3:SB<ߌc?XF6.2F*ΰNXrmKٿx|HL F8jE'9DH..0`ƛ`(#z\4F&]~ u݆\N{Sem~ '~⍵\}vq3/?b}X"b$.e(BJfss# OIP:Q66pÛ]i:ou64AvsRv{76FIHPV%mnHZc'xIpC>`.kWC?t`۰f0; %$kJSDCL̷TЉJ :QvJA\ct6QhYUaJV]8k$m!L FmA~|-rx+kmXhl8~VtѲ׭xN1t@-f|E]GP>J=)il5)\-PEq+|xiim;'ziNJJP+,mnF S Rheu$af (=@Hi "qUؑO 釶7\]wU(p5t '#upF8 ycǫ~t㔃ynjhG8ĶpgO}UJ@ G 6 hHM6X/`!+)Z![9nfjI.N32'h1*x~Sut#NFsNيqd~5ucI'u/dȈ,i3 1hq#ذwfH3VX=:5sxC1߮zάD ,v_ҨGU2f;%=Lsch3Y;Sϼ,Gz遝0g9Ђ\ֻbB3ބ;p'Y,$Md6aCk# Ȇ ~q~؂%J^kYF3piŔrdza3))2XQlpHf9, &"LlW6&LƸ/gC ߸F[ Z:$.לga]2Ytמ_m{O*ctqu@Vƺ9法YOH-o[>Tط#'J7>,!WKGj́fAJݓUhmQ46Jy9-8Q󐠹evM6پ9!Zd:q`2z̃d@*R&C /B w#p? +6h"R)裃8}Q(}ij4_cC_:䋿?8dV'IIĊt/GA&$~aSOpA$h$I$HD8QO?H $AESh{H߅I75j2 (Lm!,C8kHpq%{hm[:׍d=V|.xU YM?Q`F^:c-XE 6FXl["yU;qH4?xGw`#_dcD-af~ ;T_kg0lwB]*Eyv(H'GE3Ҫ٤h1"%V,]h/V׸~. C6$#~ !jg"5|Gm;5ĒЦ?-ם@h@Ke4ΧDMʻ4A~ba}}y~ @`Y+F1DJj t9.Op 'lyE QfiwXF~L1Z Y8h $$M$X%̭W7G}ǁϿ߆U5T][W|SXM1|6[];lDy{DOT'VцTC՘MxEҡ@ ^ _O=gDPbs2EEyb*Go,J\+[jVR׉{ykףbd@EJ`*P&IJ**Jj `i(*P"aA9&0fPcA :3ՆedfVJea+60dI 'UVfV)ȍUX@RX@RX@R֫3 + B* \$)r+35BVeh+a$n74 F$$n}>˦Ea]Z{B%O {D<߮z{ѼK!ipި *)4zL!0`b٤QHj ovaOaA@ػF$v{mFR[9r@H̱'!%W iW4]^L6_&D^^e°H7:#i qڞXPQڀ0uٝ[/>"*.BG`B!CI+'|8RU5,c^%iu XͶwje.aajX*N؀v}VA+Vbd׌UǶ[5̬U5Qd Zrrպ*; ,A@?90bnx߁[P6a<:ATŰDD?)) PSSE T*'55HQDQPFɡ "!C@WQ\ hU(]JA(i,b%S/Ɇj]&>8.s8Қ"J(~)M-^6J6Ƙg#|awFeQO$hB *m.E$uPJ,U)W؈ X3ʢ^ͽ %访͘襇r\Xi ZdđTjX!}d@pE~x*mj@xv_Ocǿծf}GskY`p-r.Bau:Xk΁*XEʼ$iHFQ@i5ڑWYK ^zԇ ^ IΌzK^Iz׈_oM!~u_L]CS>&V&Z/"&e-;&a?q%\h*sˎ#6'4 :&DH!M ꎰ(F^xl_: PvuD5{V+'DGCF5..%jO08wƻ %=DHh+S 0*5]|pta( a% XVߎi5d`.IF1Pޫ_<$B\[;fݾ6=Cxtx>9]rkn7(m柙ZbcsMʙS#)ƿ O2H/h:2)AQZRp&P(9tstgIjKvY!IڊhK$Yag=W[3/P2' RRW"2d\Wr[JNMkBeIF^8^!z[> 8_bP:2R [HUi uA~/iqR+bPI̼'[ m6 BHms sM\usq4-G L鱴QvKpٺFq7ewO PB.#uY]:Wya.nsp:0ë9/o7_ǣo==_ B=Q@tJ Jӿ_6n~\q3Uv'eC(8<9?lLxk1N8!\FshVۈތ-"I X@5(u?/F;\')I[q0Xj?ꐐe/wK&@'8HIk2l:w 1jTNg6ik BOP$3{>k&ٞ;R7ABXB #S` &f)c +䎸Ơ+J?{]T82c8b}:ڹ9Y??okZ%-ʪ^%vuU1?ߧfo}z3&J:vW0M}x3&p;cK_;gxdDŷUOBUВ}ѢĒya;o$]>\.{:, y飯Ha YJ! OaC>0scc 辆Sz4LǍCN!1Ɖu2z'QJRjD:qGo Ej4a%/ ?|P6cU=lBT.1?#{wxw@BMSO°?Ǧut{†:z;atH$-Qjsyr*LۗHUF>OH@mh,l`HZ' jsv=J^n& O _nHŷIZ44(B(j1@hc+RS1GȠj4 .F T43\в/rj(!#)vW*h[sˆPG(:YtmVR+"<}Gh/'ߦuB%wWʒnДQ*tܯ <@J/6m? d0+ mvQNȓq-߮v>-) XʺjrY9TYh"ϙ\SC.!y aTLY/8Nox~js6tRzĚ`c J1@ֿ*EèTWe%]慜I2+GsVQ<4t{z~^9 l}s(e볂  ^nul]BmM!D3 &wѣz2Q)Vn5:&,*eWKx9HI1O@A#yi̩t,5Dj7jޯz` aBJq(׋(A B&qQu} (6U sŇlۮSRo 4ۆq1m2_#IW`쏯 74698~$C1/xjCnij! 0-h1iI.-"P+BKw(aY.hM!IWۦ6ju '+׾,7ޛO0,cs$ 5|m:$-WO{Uʀ!^8Zef/T5P|夤-`%;s"/I?b?>~]'rTI\^eO[M [@& X'd܅Zbp<ϧH6pw_{|F8d^[:]xQ]f<+MA+7idmn}\|}#en rX1/@vӊ;ڿ?Ing\ $ R/CKCTk\d/`:b)H!l"P0$_6,Me5h#$,*`1Xpu[c?r-uUM!} dYvg&Q۬@ GqaT)R}:HFȇ- ]WVuf&]:뫍j=vc?0g_=-_|ҢKYtmS :N8zv`x~t] i۳ufQ%*O4SjT[*ޮ#cҫ\. {+rKAAä-C@>|b$rk Au7܍J1k\5-:iĥii+Gwuna3Lau')zd#{S] T!X3t\B#аhW._QgJ#^@_r